This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / pcfs / pcfs_conv.c
CommitLineData
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 */
37u_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 */
45u_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 */
54u_long lasttime;
55u_long lastday;
56union dosdate lastddate;
57union 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 */
64void
65unix2dostime(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
129union dosdate lastdosdate;
130u_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 */
138void
139dos2unixtime(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 */
207int
208dos2unixfn(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 */
280void
281unix2dosfn(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