/* * Copyright (c) 1989, 1998 by Sun Microsystems, Inc. * All rights reserved. */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #pragma ident "%Z%%M% %I% %E% SMI" #ifndef KERNEL #define KERNEL #endif #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Structure returned by gmtime and localtime calls (see ctime(3)). */ struct tm { short tm_sec; short tm_min; short tm_hour; short tm_mday; short tm_mon; short tm_year; short tm_wday; short tm_yday; short tm_isdst; }; void pc_tvtopct(timestruc_t *, struct pctime *); void pc_pcttotv(struct pctime *, timestruc_t *); int pc_validchar(char); static struct tm *localtime(time_t *tim); static int sunday(struct tm *, int); static int dysize(int); static struct tm *gmtime(int); static time_t ctime(struct tm *); /* The cm struct defines tm_year relative to 1900 */ #define YEAR_ZERO 1900 /* * convert timestruct to pctime */ void pc_tvtopct( timestruc_t *tvp, /* time input */ struct pctime *pctp) /* pctime output */ { struct tm *ctp; ctp = localtime(&tvp->tv_sec); #define setfield(S, FIELD, SFT, MSK) \ S = (ltohs(S) & ~(MSK << SFT)) | (((FIELD) & MSK) << SFT); S = htols(S); setfield(pctp->pct_time, ctp->tm_sec / 2, SECSHIFT, SECMASK); setfield(pctp->pct_time, ctp->tm_min, MINSHIFT, MINMASK); setfield(pctp->pct_time, ctp->tm_hour, HOURSHIFT, HOURMASK); setfield(pctp->pct_date, ctp->tm_mday, DAYSHIFT, DAYMASK); setfield(pctp->pct_date, ctp->tm_mon + 1, MONSHIFT, MONMASK); setfield(pctp->pct_date, ctp->tm_year - 80, YEARSHIFT, YEARMASK); #undef setfield } /* * convert pctime to timeval */ void pc_pcttotv( struct pctime *pctp, /* ptime input */ timestruc_t *tvp) /* tinmeval output */ { struct tm tm; #define getfield(S, SFT, M) (((int)(ltohs(S)) >> SFT) & M) tm.tm_sec = getfield(pctp->pct_time, SECSHIFT, SECMASK) * 2; tm.tm_min = getfield(pctp->pct_time, MINSHIFT, MINMASK); tm.tm_hour = getfield(pctp->pct_time, HOURSHIFT, HOURMASK); tm.tm_mday = getfield(pctp->pct_date, DAYSHIFT, DAYMASK); tm.tm_mon = getfield(pctp->pct_date, MONSHIFT, MONMASK) - 1; tm.tm_year = 80 + getfield(pctp->pct_date, YEARSHIFT, YEARMASK); #undef getfield tvp->tv_nsec = 0; tvp->tv_sec = ctime(&tm); } /* * This routine converts time as follows. * The epoch is 0000 Jan 1 1970 GMT. * The argument time is in seconds since then. * The localtime(t) entry returns a pointer to an array * containing * seconds (0-59) * minutes (0-59) * hours (0-23) * day of month (1-31) * month (0-11) * year-1900 * weekday (0-6, Sun is 0) * day of the year * daylight savings flag * * The routine calls the system to determine the local * timezone and whether Daylight Saving Time is permitted locally. * (DST is then determined by the current local rules) * * The routine does not work * in Saudi Arabia which runs on Solar time. * */ static int dmsize[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* * The following table is used for 1974 and 1975 and * gives the day number of the first day after the Sunday of the * change. */ struct dstab { int dayyr; int daylb; int dayle; }; static struct dstab usdaytab[] = { 1974, 5, 333, /* 1974: Jan 6 - last Sun. in Nov */ 1975, 58, 303, /* 1975: Last Sun. in Feb - last Sun in Oct */ 0, 119, 303, /* all other years: end Apr - end Oct */ }; static struct dstab ausdaytab[] = { 1970, 400, 0, /* 1970: no daylight saving at all */ 1971, 303, 0, /* 1971: daylight saving from Oct 31 */ 1972, 303, 58, /* 1972: Jan 1 -> Feb 27 & Oct 31 -> dec 31 */ 0, 303, 65, /* others: -> Mar 7, Oct 31 -> */ }; /* * The European tables ... based on hearsay * Believed correct for: * WE: Great Britain, Ireland, Portugal * ME: Belgium, Luxembourg, Netherlands, Denmark, Norway, * Austria, Poland, Czechoslovakia, Sweden, Switzerland, * DDR, DBR, France, Spain, Hungary, Italy, Jugoslavia * Eastern European dst is unknown, we'll make it ME until someone speaks up. * EE: Bulgaria, Finland, Greece, Rumania, Turkey, Western Russia */ static struct dstab wedaytab[] = { 1983, 86, 303, /* 1983: end March - end Oct */ 1984, 86, 303, /* 1984: end March - end Oct */ 1985, 86, 303, /* 1985: end March - end Oct */ 0, 400, 0, /* others: no daylight saving at all ??? */ }; static struct dstab medaytab[] = { 1983, 86, 272, /* 1983: end March - end Sep */ 1984, 86, 272, /* 1984: end March - end Sep */ 1985, 86, 272, /* 1985: end March - end Sep */ 0, 400, 0, /* others: no daylight saving at all ??? */ }; static struct dayrules { int dst_type; /* number obtained from system */ int dst_hrs; /* hours to add when dst on */ struct dstab *dst_rules; /* one of the above */ enum {STH, NTH} dst_hemi; /* southern, northern hemisphere */ } dayrules [] = { DST_USA, 1, usdaytab, NTH, DST_AUST, 1, ausdaytab, STH, DST_WET, 1, wedaytab, NTH, DST_MET, 1, medaytab, NTH, DST_EET, 1, medaytab, NTH, /* XXX */ -1, }; struct pcfs_args pc_tz; /* this is set by pcfs_mount */ static struct tm * localtime(time_t *tim) { int dayno; struct tm *ct; int dalybeg, daylend; struct dayrules *dr; struct dstab *ds; int year; int copyt; copyt = *tim - (int)pc_tz.secondswest; ct = gmtime(copyt); dayno = ct->tm_yday; for (dr = dayrules; dr->dst_type >= 0; dr++) if (dr->dst_type == pc_tz.dsttime) break; if (dr->dst_type >= 0) { year = ct->tm_year + 1900; for (ds = dr->dst_rules; ds->dayyr; ds++) { if (ds->dayyr == year) { break; } } dalybeg = ds->daylb; /* first Sun after dst starts */ daylend = ds->dayle; /* first Sun after dst ends */ dalybeg = sunday(ct, dalybeg); daylend = sunday(ct, daylend); switch (dr->dst_hemi) { case NTH: if (!( (dayno > dalybeg || (dayno == dalybeg && ct->tm_hour >= 2)) && (dayno < daylend || (dayno == daylend && ct->tm_hour < 1)))) { return (ct); } break; case STH: if (!( (dayno > dalybeg || (dayno == dalybeg && ct->tm_hour >= 2)) || (dayno < daylend || (dayno == daylend && ct->tm_hour < 2)))) { return (ct); } break; default: return (ct); } copyt += dr->dst_hrs*60*60; ct = gmtime(copyt); ct->tm_isdst++; } return (ct); } /* * The argument is a 0-origin day number. * The value is the day number of the first * Sunday on or after the day. */ static int sunday(struct tm *t, int d) { if (d >= 58) d += dysize(YEAR_ZERO + t->tm_year) - 365; return (d - (d - t->tm_yday + t->tm_wday + 700) % 7); } static int dysize(int y) { if (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) return (366); return (365); } static struct tm * gmtime(int tim) { int d0, d1; int hms, day; short *tp; static struct tm xtime; /* * break initial number into days */ hms = tim % 86400; day = tim / 86400; if (hms < 0) { hms += 86400; day -= 1; } tp = (short *)&xtime; /* * generate hours:minutes:seconds */ *tp++ = hms%60; d1 = hms/60; *tp++ = d1%60; d1 /= 60; *tp++ = (short)d1; /* * day is the day number. * generate day of the week. * The addend is 4 mod 7 (1/1/1970 was Thursday) */ xtime.tm_wday = (day+7340036)%7; /* * year number */ if (day >= 0) for (d1 = 70; day >= dysize(YEAR_ZERO + d1); d1++) day -= dysize(YEAR_ZERO + d1); else for (d1 = 70; day < 0; d1--) day += dysize(YEAR_ZERO + d1 - 1); xtime.tm_year = (short)d1; xtime.tm_yday = d0 = day; /* * generate month */ if (dysize(YEAR_ZERO + d1) == 366) dmsize[1] = 29; for (d1 = 0; d0 >= dmsize[d1]; d1++) d0 -= dmsize[d1]; dmsize[1] = 28; *tp++ = d0+1; *tp++ = (short)d1; xtime.tm_isdst = 0; return (&xtime); } /* * convert year, month, day, hour, minute, sec to (int)time. */ static time_t ctime(struct tm *tp) { int i; time_t ct; if (tp->tm_mon < 0 || tp->tm_mon > 11 || tp->tm_mday < 1 || tp->tm_mday > 31 || tp->tm_hour < 0 || tp->tm_hour > 23 || tp->tm_min < 0 || tp->tm_min > 59 || tp->tm_sec < 0 || tp->tm_sec > 59) { return (0); } ct = 0; for (i = /* 19 */ 70; i < tp->tm_year; i++) ct += dysize(YEAR_ZERO + i); /* Leap year */ if (dysize(YEAR_ZERO + tp->tm_year) == 366 && tp->tm_mon >= 2) ct++; i = tp->tm_mon + 1; while (--i) ct += dmsize[i-1]; ct += tp->tm_mday-1; ct = 24*ct + tp->tm_hour; ct = 60*ct + tp->tm_min; ct = 60*ct + tp->tm_sec; /* convert to GMT assuming local time */ ct += (int)pc_tz.secondswest; /* now fix up local daylight time */ if (localtime(&ct)->tm_isdst) ct -= 60*60; return (ct); } /* * Determine whether a character is valid for a pc 8.3 file system file name. * The Windows 95 Resource Kit claims that these are valid: * uppercase letters and numbers * blank * ASCII characters greater than 127 * $%'-_@~`!()^#& * Long file names can also have * lowercase letters * +,;=[] */ int pc_validchar(char c) { char *cp; int n; static char valtab[] = { "$#&@!%()-{}<>`_^~|' " }; /* * Should be "$#&@!%()-{}`_^~' " ?? * From experiment in DOSWindows, *+=|\[];:",<>.?/ are illegal. * See IBM DOS4.0 Tech Ref. B-57. */ if (c >= 'A' && c <= 'Z') return (1); if (c >= '0' && c <= '9') return (1); cp = valtab; n = sizeof (valtab); while (n--) { if (c == *cp++) return (1); } return (0); } /* * Determine whether a character is valid for a pc 8.3 file system file name. * The Windows 95 Resource Kit claims that these are valid: * uppercase letters and numbers * blank * ASCII characters greater than 127 * $%'-_@~`!()^#& * Long file names can also have * lowercase letters * +,;=[]. */ int pc_valid_lfn_char(char c) { char *cp; int n; static char valtab[] = { "+,;=[].$#&@!%()-{}<>`_^~|' " }; if (c >= 'a' && c <= 'z') return (1); if (c >= 'A' && c <= 'Z') return (1); if (c >= '0' && c <= '9') return (1); cp = valtab; n = sizeof (valtab); while (n--) { if (c == *cp++) return (1); } return (0); } int pc_valid_long_fn(char *namep) { char *tmp; for (tmp = namep; *tmp != '\0'; tmp++) if (!pc_valid_lfn_char(*tmp)) return (0); if ((tmp - namep) >= PCMAXNAMLEN) return (0); return (1); } int pc_fname_ext_to_name(char *namep, char *fname, char *ext, int foldcase) { int i; char *tp = namep; char c; i = PCFNAMESIZE; while (i-- && ((c = *fname) != ' ')) { if (!(c == '.' || pc_validchar(c))) { return (-1); } if (foldcase) *tp++ = tolower(c); else *tp++ = c; fname++; } if (*ext != ' ') { *tp++ = '.'; i = PCFEXTSIZE; while (i-- && ((c = *ext) != ' ')) { if (!pc_validchar(c)) { return (-1); } if (foldcase) *tp++ = tolower(c); else *tp++ = c; ext++; } } *tp = '\0'; return (0); }