Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Written by Paul Popelka (paulp@uts.amdahl.com) | |
3 | * | |
4 | * You can do anything you want with this software, | |
5 | * just don't say you wrote it, | |
6 | * and don't remove this notice. | |
7 | * | |
8 | * This software is provided "as is". | |
9 | * | |
10 | * The author supplies this software to be publicly | |
11 | * redistributed on the understanding that the author | |
12 | * is not responsible for the correct functioning of | |
13 | * this software in any circumstances and is not liable | |
14 | * for any damages caused by this software. | |
15 | * | |
16 | * October 1992 | |
17 | * | |
7729ff78 | 18 | * $Id: pcfs_conv.c,v 1.4 1993/12/19 00:54:27 wollman Exp $ |
15637ed4 RG |
19 | */ |
20 | ||
21 | /* | |
22 | * System include files. | |
23 | */ | |
24 | #include "param.h" | |
fde1aeb2 | 25 | #include "systm.h" |
15637ed4 RG |
26 | #include "time.h" |
27 | #include "kernel.h" /* defines tz */ | |
28 | ||
29 | /* | |
30 | * PCFS include files. | |
31 | */ | |
32 | #include "direntry.h" | |
33 | ||
34 | /* | |
35 | * Days in each month in a regular year. | |
36 | */ | |
37 | u_short regyear[] = { | |
38 | 31, 28, 31, 30, 31, 30, | |
39 | 31, 31, 30, 31, 30, 31 | |
40 | }; | |
41 | ||
42 | /* | |
43 | * Days in each month in a leap year. | |
44 | */ | |
45 | u_short leapyear[] = { | |
46 | 31, 29, 31, 30, 31, 30, | |
47 | 31, 31, 30, 31, 30, 31 | |
48 | }; | |
49 | ||
50 | /* | |
51 | * Variables used to remember parts of the last time | |
52 | * conversion. Maybe we can avoid a full conversion. | |
53 | */ | |
54 | u_long lasttime; | |
55 | u_long lastday; | |
56 | union dosdate lastddate; | |
57 | union dostime lastdtime; | |
58 | ||
59 | /* | |
60 | * Convert the unix version of time to dos's idea of time | |
61 | * to be used in file timestamps. | |
62 | * The passed in unix time is assumed to be in GMT. | |
63 | */ | |
64 | void | |
65 | unix2dostime(tvp, ddp, dtp) | |
66 | struct timeval *tvp; | |
67 | union dosdate *ddp; | |
68 | union dostime *dtp; | |
69 | { | |
70 | u_long days; | |
71 | u_long inc; | |
72 | u_long year; | |
73 | u_long month; | |
74 | u_short *months; | |
75 | ||
76 | /* | |
77 | * If the time from the last conversion is the same | |
78 | * as now, then skip the computations and use the | |
79 | * saved result. | |
80 | */ | |
81 | if (lasttime != tvp->tv_sec) { | |
82 | lasttime = tvp->tv_sec - (tz.tz_minuteswest * 60) | |
83 | /* +- daylight savings time correction */; | |
84 | lastdtime.dts.dt_2seconds = (lasttime % 60) >> 1; | |
85 | lastdtime.dts.dt_minutes = (lasttime / 60) % 60; | |
86 | lastdtime.dts.dt_hours = (lasttime / (60 * 60)) % 24; | |
87 | ||
88 | /* | |
89 | * If the number of days since 1970 is the same as the | |
90 | * last time we did the computation then skip all this | |
91 | * leap year and month stuff. | |
92 | */ | |
93 | days = lasttime / (24 * 60 * 60); | |
94 | if (days != lastday) { | |
95 | lastday = days; | |
96 | for (year = 1970; ; year++) { | |
97 | inc = year & 0x03 ? 365 : 366; | |
98 | if (days < inc) break; | |
99 | days -= inc; | |
100 | } | |
101 | months = year & 0x03 ? regyear : leapyear; | |
102 | for (month = 0; month < 12; month++) { | |
103 | if (days < months[month]) break; | |
104 | days -= months[month]; | |
105 | } | |
106 | lastddate.dds.dd_day = days + 1; | |
107 | lastddate.dds.dd_month = month+1; | |
108 | /* | |
109 | * Remember dos's idea of time is relative to 1980. | |
110 | * unix's is relative to 1970. If somehow we get a | |
111 | * time before 1980 then don't give totally crazy | |
112 | * results. | |
113 | */ | |
114 | lastddate.dds.dd_year = year < 1980 ? 0 : year - 1980; | |
115 | } | |
116 | } | |
117 | dtp->dti = lastdtime.dti; | |
118 | ddp->ddi = lastddate.ddi; | |
119 | } | |
120 | ||
121 | /* | |
122 | * The number of seconds between Jan 1, 1970 and | |
123 | * Jan 1, 1980. | |
124 | * In that interval there were 8 regular years and | |
125 | * 2 leap years. | |
126 | */ | |
127 | #define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) | |
128 | ||
129 | union dosdate lastdosdate; | |
130 | u_long lastseconds; | |
131 | ||
132 | /* | |
133 | * Convert from dos' idea of time to unix'. | |
134 | * This will probably only be called from the | |
135 | * stat(), and fstat() system calls | |
136 | * and so probably need not be too efficient. | |
137 | */ | |
138 | void | |
139 | dos2unixtime(ddp, dtp, tvp) | |
140 | union dosdate *ddp; | |
141 | union dostime *dtp; | |
142 | struct timeval *tvp; | |
143 | { | |
144 | u_long seconds; | |
145 | u_long month; | |
146 | u_long yr; | |
147 | u_long days; | |
148 | u_short *months; | |
149 | ||
150 | seconds = (dtp->dts.dt_2seconds << 1) + | |
151 | (dtp->dts.dt_minutes * 60) + | |
152 | (dtp->dts.dt_hours * 60 * 60); | |
153 | /* | |
154 | * If the year, month, and day from the last conversion | |
155 | * are the same then use the saved value. | |
156 | */ | |
157 | if (lastdosdate.ddi != ddp->ddi) { | |
158 | lastdosdate.ddi = ddp->ddi; | |
159 | days = 0; | |
160 | for (yr = 0; yr < ddp->dds.dd_year; yr++) { | |
161 | days += yr & 0x03 ? 365 : 366; | |
162 | } | |
163 | months = yr & 0x03 ? regyear : leapyear; | |
164 | /* | |
165 | * Prevent going from 0 to 0xffffffff in the following | |
166 | * loop. | |
167 | */ | |
168 | if (ddp->dds.dd_month == 0) { | |
169 | printf("dos2unixtime(): month value out of range (%d)\n", | |
170 | ddp->dds.dd_month); | |
171 | ddp->dds.dd_month = 1; | |
172 | } | |
173 | for (month = 0; month < ddp->dds.dd_month-1; month++) { | |
174 | days += months[month]; | |
175 | } | |
176 | days += ddp->dds.dd_day - 1; | |
177 | lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; | |
178 | } | |
179 | tvp->tv_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) | |
180 | /* -+ daylight savings time correction */; | |
181 | tvp->tv_usec = 0; | |
182 | } | |
183 | ||
184 | /* | |
185 | * Cheezy macros to do case detection and conversion | |
186 | * for the ascii character set. DOESN'T work for ebcdic. | |
187 | */ | |
188 | #define isupper(c) (c >= 'A' && c <= 'Z') | |
189 | #define islower(c) (c >= 'a' && c <= 'z') | |
190 | #define toupper(c) (c & ~' ') | |
191 | #define tolower(c) (c | ' ') | |
192 | ||
193 | /* | |
194 | * DOS filenames are made of 2 parts, the name part and | |
195 | * the extension part. The name part is 8 characters | |
196 | * long and the extension part is 3 characters long. They | |
197 | * may contain trailing blanks if the name or extension | |
198 | * are not long enough to fill their respective fields. | |
199 | */ | |
200 | ||
201 | /* | |
202 | * Convert a DOS filename to a unix filename. | |
203 | * And, return the number of characters in the | |
204 | * resulting unix filename excluding the terminating | |
205 | * null. | |
206 | */ | |
207 | int | |
208 | dos2unixfn(dn, un) | |
209 | u_char dn[11]; | |
210 | u_char *un; | |
211 | { | |
212 | int i; | |
213 | int ni; | |
214 | int ei; | |
215 | int thislong = 0; | |
216 | u_char c; | |
217 | u_char *origun = un; | |
218 | ||
219 | /* | |
220 | * Find the last character in the name portion | |
221 | * of the dos filename. | |
222 | */ | |
223 | for (ni = 7; ni >= 0; ni--) | |
224 | if (dn[ni] != ' ') break; | |
225 | ||
226 | /* | |
227 | * Find the last character in the extension | |
228 | * portion of the filename. | |
229 | */ | |
230 | for (ei = 10; ei >= 8; ei--) | |
231 | if (dn[ei] != ' ') break; | |
232 | ||
233 | /* | |
234 | * Copy the name portion into the unix filename | |
235 | * string. | |
236 | * NOTE: DOS filenames are usually kept in upper | |
237 | * case. To make it more unixy we convert all | |
238 | * DOS filenames to lower case. Some may like | |
239 | * this, some may not. | |
240 | */ | |
241 | for (i = 0; i <= ni; i++) { | |
242 | c = dn[i]; | |
243 | *un++ = isupper(c) ? tolower(c) : c; | |
244 | thislong++; | |
245 | } | |
246 | ||
247 | /* | |
248 | * Now, if there is an extension then put in a period | |
249 | * and copy in the extension. | |
250 | */ | |
251 | if (ei >= 8) { | |
252 | *un++ = '.'; | |
253 | thislong++; | |
254 | for (i = 8; i <= ei; i++) { | |
255 | c = dn[i]; | |
256 | *un++ = isupper(c) ? tolower(c) : c; | |
257 | thislong++; | |
258 | } | |
259 | } | |
260 | *un++ = 0; | |
261 | ||
262 | /* | |
263 | * If first char of the filename is SLOT_E5 (0x05), then | |
264 | * the real first char of the filename should be 0xe5. | |
265 | * But, they couldn't just have a 0xe5 mean 0xe5 because | |
266 | * that is used to mean a freed directory slot. | |
267 | * Another dos quirk. | |
268 | */ | |
269 | if (*origun == SLOT_E5) | |
270 | *origun = 0xe5; | |
271 | ||
272 | return thislong; | |
273 | } | |
274 | ||
275 | /* | |
276 | * Convert a unix filename to a DOS filename. | |
277 | * This function does not ensure that valid | |
278 | * characters for a dos filename are supplied. | |
279 | */ | |
280 | void | |
281 | unix2dosfn(un, dn, unlen) | |
282 | u_char *un; | |
283 | u_char dn[11]; | |
284 | int unlen; | |
285 | { | |
286 | int i; | |
4c45483e | 287 | u_char c = 0; |
15637ed4 RG |
288 | |
289 | /* | |
290 | * Fill the dos filename string with blanks. | |
291 | * These are DOS's pad characters. | |
292 | */ | |
293 | for (i = 0; i <= 10; i++) | |
294 | dn[i] = ' '; | |
295 | ||
296 | /* | |
297 | * The filenames "." and ".." are handled specially, | |
298 | * since they don't follow dos filename rules. | |
299 | */ | |
7729ff78 | 300 | if (un[0] == '.' && unlen == 1) { |
15637ed4 RG |
301 | dn[0] = '.'; |
302 | return; | |
303 | } | |
7729ff78 | 304 | if (un[0] == '.' && un[1] == '.' && unlen == 2) { |
15637ed4 RG |
305 | dn[0] = '.'; |
306 | dn[1] = '.'; | |
307 | return; | |
308 | } | |
309 | ||
310 | /* | |
311 | * Copy the unix filename into the dos filename string | |
312 | * upto the end of string, a '.', or 8 characters. | |
313 | * Whichever happens first stops us. | |
314 | * This forms the name portion of the dos filename. | |
315 | * Fold to upper case. | |
316 | */ | |
317 | for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { | |
318 | dn[i] = islower(c) ? toupper(c) : c; | |
319 | un++; | |
320 | unlen--; | |
321 | } | |
322 | ||
323 | /* | |
324 | * If the first char of the filename is 0xe5, then translate | |
325 | * it to 0x05. This is because 0xe5 is the marker for a | |
326 | * deleted directory slot. I guess this means you can't | |
327 | * have filenames that start with 0x05. I suppose we should | |
328 | * check for this and doing something about it. | |
329 | */ | |
330 | if (dn[0] == SLOT_DELETED) | |
331 | dn[0] = SLOT_E5; | |
332 | ||
333 | /* | |
334 | * Strip any further characters up to a '.' or the | |
335 | * end of the string. | |
336 | */ | |
337 | while (unlen && (c = *un) && c != '.') { | |
338 | un++; | |
339 | unlen--; | |
340 | } | |
341 | ||
342 | /* | |
343 | * If we stopped on a '.', then get past it. | |
344 | */ | |
345 | if (c == '.') un++; | |
346 | ||
347 | /* | |
348 | * Copy in the extension part of the name, if any. | |
349 | * Force to upper case. | |
350 | * Note that the extension is allowed to contain '.'s. | |
351 | * Filenames in this form are probably inaccessable | |
352 | * under dos. | |
353 | */ | |
354 | for (i = 8; i <= 10 && unlen && (c = *un); i++) { | |
355 | dn[i] = islower(c) ? toupper(c) : c; | |
356 | un++; | |
357 | unlen--; | |
358 | } | |
359 | } | |
360 | ||
361 | /* | |
362 | * Get rid of these macros before someone discovers | |
363 | * we are using such hideous things. | |
364 | */ | |
365 | #undef isupper | |
366 | #undef islower | |
367 | #undef toupper | |
368 | #undef tolower |