diff --git a/sys/include/libc.h b/sys/include/libc.h index 820d684b2..7a2700539 100644 --- a/sys/include/libc.h +++ b/sys/include/libc.h @@ -314,45 +314,22 @@ extern double fmod(double, double); /* * Time-of-day */ -typedef struct Tzone Tzone; -#pragma incomplete Tzone - typedef struct Tm { - vlong abs; /* seconds since Jan 1 1970, GMT */ - int nsec; /* nseconds (range 0...1e9) */ - int sec; /* seconds (range 0..60) */ - int min; /* minutes (0..59) */ - int hour; /* hours (0..23) */ - int mday; /* day of the month (1..31) */ - int mon; /* month of the year (0..11) */ - int year; /* year A.D. */ - int wday; /* day of week (0..6, Sunday = 0) */ - int yday; /* day of year (0..365) */ - char zone[16]; /* time zone name */ - int tzoff; /* time zone delta from GMT */ - Tzone *tz; /* time zone associated with this date */ + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; } Tm; -typedef -struct Tmfmt { - char *fmt; - Tm *tm; -} Tmfmt; - -#pragma varargck type "τ" Tmfmt - -extern Tzone* tmgetzone(char *name); -extern Tm* tmnow(Tm*, Tzone*); -extern Tm* tmtime(Tm*, vlong, Tzone*); -extern Tm* tmtimens(Tm*, vlong, int, Tzone*); -extern Tm* tmparse(Tm*, char*, char*, Tzone*); -extern Tm* tmnorm(Tm*); -extern Tmfmt tmfmt(Tm*, char*); -extern void tmfmtinstall(void); - extern Tm* gmtime(long); extern Tm* localtime(long); extern char* asctime(Tm*); diff --git a/sys/man/2/tmdate b/sys/man/2/tmdate deleted file mode 100644 index 127a4e186..000000000 --- a/sys/man/2/tmdate +++ /dev/null @@ -1,244 +0,0 @@ -.TH TMDATE 2 -.SH NAME -tmnow, tmgetzone, tmtime, tmparse, tmfmt, tmnorm, - convert date and time -.SH SYNOPSIS -.B #include -.br -.B #include -.PP -.ft L -.nf -.EX -typedef struct Tmd Tmd; -typedef struct Tmfmt Tmfmt; - -struct { - vlong abs; /* seconds since Jan 1 1970, UTC */ - int nsec; /* nanoseconds (range 0..1e9) */ - int sec; /* seconds (range 0..59) */ - int min; /* minutes (0..59) */ - int hour; /* hours (0..23) */ - int mday; /* day of the month (1..31) */ - int mon; /* month of the year (0..11) */ - int year; /* C.E year - 1900 */ - int wday; /* day of week (0..6, Sunday = 0) */ - int yday; /* day of year (0..365) */ - char zone[]; /* time zone name */ - int tzoff; /* time zone delta from GMT, seconds */ -}; - -Tzone *tmgetzone(char *name); -Tm *tmnow(Tm *tm, char *tz); -Tm *tmtime(Tm *tm, vlong abs, Tzone *tz); -Tm *tmtimens(Tm *tm, vlong abs, int ns, Tzone *tz); -Tm *tmparse(Tm *dst, char *fmt, char *tm, Tzone *zone); -void tmnorm(Tm *tm); -Tmfmt tmfmt(Tm *tm, char *fmt); -void tmfmtinstall(void); -.EE -.SH DESCRIPTION -.PP -This family of functions handles simple date and time manipulation. -Times are represented as an absolute instant in time, combined with a time zone. -.PP -Time zones are loaded by as name. -They can be specified as the abbreviated timezone name, -the full timezone name, the path to a timezone file, -or an absolute offset in the HHMM form. -.PP -When given as a timezone, any instant-dependent adjustments such as leap -seconds and daylight savings time will be applied to the derived fields of -struct tm, but will not affect the absolute time. -The time zone name local always refers to the time in /env/timezone. -The nil timezone always refers to GMT. -.PP -Tmgetzone loads a timezone by name. The returned timezone is -cached for the lifetime of the program, and should not be freed. -Loading a timezone repeatedly by name loads from the cache, and -does not leak. -.PP -Tmnow gets the current time of day in the requested time zone. -.PP -Tmtime converts the millisecond-resolution timestamp 'abs' -into a Tm struct in the requested timezone. -Tmtimens does the same, but with a nanosecond accuracy. -.PP -Tmstime is identical to tmtime, but accepts the time in sec- -onds. -.PP -Tmparse parses a time from a string according to the format argument. -The result is returned in the timezone requested. -If there is a timezone in the date, and a timezone is provided -when parsing, then the zone is shifted to the provided timezone. -Parsing is case-insensitive. -.PP -The format argument contains zero or more of the following components: -.TP -.B Y, YY, YYYY -Represents the year. -.I YY -prints the year in 2 digit form. -.TP -.B M, MM, MMM, MMMM -The month of the year, in unpadded numeric, padded numeric, short name, or long name, -respectively. -.TP -.B D, DD -The day of month in unpadded or padded numeric form, respectively. -.TP -.B W, WW -The day of week in short or long name form, respectively. -.TP -.B h, hh -The hour in unpadded or padded form, respectively -.TP -.B m, mm -The minute in unpadded or padded form, respectively -.TP -.B s, ss -The second in unpadded or padded form, respectively -.TP -.B z, Z, ZZ -The timezone in named, [+-]HHMM and [+-]HH:MM form, respectively -.TP -.B a, A -Lower and uppercase 'am' and 'pm' specifiers, respectively. -.TP -.B [...] -Quoted text, copied directly to the output. -.TP -.B _ -When formatting, this inserts padding into the date format. -The padded width of a field is the sum of format and specifier -characters combined. -For example, -.I __h -will format to a width of 3. -.TP -.B ? -When parsing, this makes the following argument match fuzzily. -Fuzzy matching means that all formats are tried, from most to least specific. -For example, -.I ?M -will match -.IR January , -.IR Jan , -.IR 01 , -and -.IR 1 , -in that order of preference. -.TP -.B ~ -When parsing a date, this slackens range enforcement, accepting -out of range values such as January -.IR 32 , -which would get normalized to February 1st. -.PP -Any characters not specified above are copied directly to output, -without modification. - - - -.PP -If the format argument is nil, it makes an -attempt to parse common human readable date formats. These -formats include ISO-8601,RFC-3339 and RFC-2822 dates. -. -.PP -Tmfmt produces a format description structure suitable for passing -to -.IR fmtprint (2) . -If fmt is nil, we default to the format used in -.IR ctime (2). -The format of the format string is identical to -.IR tmparse. - -.PP -When parsing, any amount of whitespace is treated as a single token. -All string matches are case insensitive, and zero padding is optional. - -.PP -Tmnorm takes a manually adjusted Tm structure, and recal- -culates the absolute time from the -.I year, mon, mday, hr, min -and -.I sec -fields. Other fields are ignored. -This recalculation respects the time zone stored in struct tm. -Out of range values are wrapped. For example, December 32nd -becomes January 1st. - -.PP -Tmfmtinstall installs a time format specifier %τ. The time -format behaves as in tmfmt - -.SH Examples -.PP -All examples assume tmfmtinstall has been called. -.PP -Get the current date in the local timezone, UTC, and -US_Pacific time. Print it using the default format. - -.IP -.EX -Tm t; -Tzone *zl, *zp; -if((zl = tmgetzone("local") == nil) - sysfatal("load zone: %r"); -if((zp = tmgetzone("US_Pacific") == nil) - sysfatal("load zone: %r"); -print("local: %τ\\n", tmfmt(tmnow(&t, zl), nil)); -print("gmt: %τ\\n", tmfmt(tmnow(&t, nil), nil)); -print("eastern: %τ\\n", tmfmt(tmnow(&t, zp), nil)); -.EE -.PP -Compare if two times are the same, regardless of timezone. - -.IP -.EX -Tm a, b; - -tmparse(&a, nil, "Tue Dec 10 12:36:00 PST 2019"); -tmparse(&b, nil, "Tue Dec 10 15:36:00 EST 2019"); -if(a.abs == b.abs) - print("same\\n"); -else - print("different\\n"); -.EE - -.PP -Convert from one timezone to another. - -.IP -.EX -Tm here, there; -Tzone *zl, *zp; -if((zl = tmgetzone("local")) == nil) - sysfatal("load zone: %r"); -if((zp = tmgetzone("US_Pacific")) == nil) - sysfatal("load zone: %r"); -if(tmnow(&here, zl) == nil) - sysfatal("get time: %r"); -if(tmtime(&there, here.abs, zp) == nil) - sysfatal("shift time: %r"); -.EE - -.PP -Add a day to two times. Because we picked daylight savings -time to adjust over, only 23 hours are added. - -.EX -Tm t; -tmparse(&t, "W MMM D hh:mm:ss z YYYY, "Sun Nov 2 13:11:11 PST 2019"); -tm.day++; -tmrecalc(&t); -print("%τ", &t); /* Mon Nov 3 13:11:11 PST 2019 */ -.EE - -.SH BUGS -.PP -There is no way to format specifier for subsecond precision. -.PP -The timezone information that we ship is out of date. -.PP -The plan 9 timezone format can't express leap seconds. diff --git a/sys/src/cmd/seconds.c b/sys/src/cmd/seconds.c index 2c5fc3333..69e243301 100644 --- a/sys/src/cmd/seconds.c +++ b/sys/src/cmd/seconds.c @@ -1,37 +1,237 @@ -#include -#include - /* * seconds absolute_date ... - convert absolute_date to seconds since epoch */ -char *formats[] = { - /* asctime */ - "W MMM DD hh:mm:ss ?Z YYYY", - /* RFC5322 */ - "?W ?DD ?MMM ?YYYY hh:mm:ss ?Z", - "?W, DD-?MM-YY hh:mm:ss ?Z", - /* RFC822/RFC8222 */ - "DD MMM YY hh:mm ZZZ", - "DD MMM YY hh:mm Z", - /* RFC850 */ - "W, DD-MMM-YY hh:mm:ss MST", - /* RFC1123 */ - "WW, DD MMM YYYY hh:mm:ss ZZZ", - /* RFC1123Z */ - "WW, DD MMM YYYY hh:mm:ss ZZ", - /* RFC3339 */ - "YYYY-MM-DD[T]hh:mm:ss[Z]ZZ", - "YYYY-MM-DD[T]hh:mm:ss[Z]Z", - "YYYY-MM-DD[T]hh:mm:ss ZZ", - "YYYY-MM-DD[T]hh:mm:ss Z", - /* RFC 3339 and human-readable variants */ - "YYYY-MM-DD hh:mm:ss", - "YYYY-MM-DD hh:mm:ss ?Z", - "YYYY-MM-DD [@] hh:mm:ss", - "YYYY-MM-DD [@] hh:mm:ss ?Z", - nil + +#include +#include +#include + +typedef ulong Time; + +enum { + AM, PM, HR24, + + /* token types */ + Month = 1, + Year, + Day, + Timetok, + Tz, + Dtz, + Ignore, + Ampm, + + Maxtok = 6, /* only this many chars are stored in datetktbl */ + Maxdateflds = 25, }; +/* + * macros for squeezing values into low 7 bits of "value". + * all timezones we care about are divisible by 10, and the largest value + * (780) when divided is 78. + */ +#define TOVAL(tp, v) ((tp)->value = (v) / 10) +#define FROMVAL(tp) ((tp)->value * 10) /* uncompress */ + +/* keep this struct small since we have an array of them */ +typedef struct { + char token[Maxtok]; + char type; + schar value; +} Datetok; + +int dtok_numparsed; + +/* forwards */ +Datetok *datetoktype(char *s, int *bigvalp); + +static Datetok datetktbl[]; +static unsigned szdatetktbl; + +/* parse 1- or 2-digit number, advance *cpp past it */ +static int +eatnum(char **cpp) +{ + int c, x; + char *cp; + + cp = *cpp; + c = *cp; + if (!isascii(c) || !isdigit(c)) + return -1; + x = c - '0'; + + c = *++cp; + if (isascii(c) && isdigit(c)) { + x = 10*x + c - '0'; + cp++; + } + *cpp = cp; + return x; +} + +/* return -1 on failure */ +int +parsetime(char *time, Tm *tm) +{ + tm->hour = eatnum(&time); + if (tm->hour == -1 || *time++ != ':') + return -1; /* only hour; too short */ + + tm->min = eatnum(&time); + if (tm->min == -1) + return -1; + if (*time++ != ':') { + tm->sec = 0; + return 0; /* no seconds; okay */ + } + + tm->sec = eatnum(&time); + if (tm->sec == -1) + return -1; + + /* this may be considered too strict. garbage at end of time? */ + return *time == '\0' || isascii(*time) && isspace(*time)? 0: -1; +} + +/* + * try to parse pre-split timestr in fields as an absolute date + */ +int +tryabsdate(char **fields, int nf, Tm *now, Tm *tm) +{ + int i, mer = HR24, bigval = -1; + long flg = 0, ty; + Datetok *tp; + + now = localtime(time(0)); /* default to local time (zone) */ + tm->tzoff = now->tzoff; + strncpy(tm->zone, now->zone, sizeof tm->zone); + + tm->mday = tm->mon = tm->year = -1; /* mandatory */ + tm->hour = tm->min = tm->sec = 0; + dtok_numparsed = 0; + + for (i = 0; i < nf; i++) { + if (fields[i][0] == '\0') + continue; + tp = datetoktype(fields[i], &bigval); + ty = (1L << tp->type) & ~(1L << Ignore); + if (flg & ty) + return -1; /* repeated type */ + flg |= ty; + switch (tp->type) { + case Year: + tm->year = bigval; + if (tm->year < 1970 || tm->year > 2106) + return -1; /* can't represent in ulong */ + /* convert 4-digit year to 1900 origin */ + if (tm->year >= 1900) + tm->year -= 1900; + break; + case Day: + tm->mday = bigval; + break; + case Month: + tm->mon = tp->value - 1; /* convert to zero-origin */ + break; + case Timetok: + if (parsetime(fields[i], tm) < 0) + return -1; + break; + case Dtz: + case Tz: + /* tm2sec mangles timezones, so we do our own handling */ + tm->tzoff = FROMVAL(tp); + snprint(tm->zone, sizeof(tm->zone), "GMT"); + break; + case Ignore: + break; + case Ampm: + mer = tp->value; + break; + default: + return -1; /* bad token type: CANTHAPPEN */ + } + } + if (tm->year == -1 || tm->mon == -1 || tm->mday == -1) + return -1; /* missing component */ + if (mer == PM) + tm->hour += 12; + return 0; +} + +int +prsabsdate(char *timestr, Tm *now, Tm *tm) +{ + int nf; + char *fields[Maxdateflds]; + static char delims[] = "- \t\n/,"; + + nf = gettokens(timestr, fields, nelem(fields), delims+1); + if (nf > nelem(fields)) + return -1; + if (tryabsdate(fields, nf, now, tm) < 0) { + char *p = timestr; + + /* + * could be a DEC-date; glue it all back together, split it + * with dash as a delimiter and try again. Yes, this is a + * hack, but so are DEC-dates. + */ + while (--nf > 0) { + while (*p++ != '\0') + ; + p[-1] = ' '; + } + nf = gettokens(timestr, fields, nelem(fields), delims); + if (nf > nelem(fields) || tryabsdate(fields, nf, now, tm) < 0) + return -1; + } + return 0; +} + +int +validtm(Tm *tm) +{ + if (tm->year < 0 || tm->mon < 0 || tm->mon > 11 || + tm->mday < 1 || tm->hour < 0 || tm->hour >= 24 || + tm->min < 0 || tm->min > 59 || + tm->sec < 0 || tm->sec > 61) /* allow 2 leap seconds */ + return 0; + return 1; +} + +Time +seconds(char *timestr) +{ + Tm date; + + memset(&date, 0, sizeof date); + if (prsabsdate(timestr, localtime(time(0)), &date) < 0) + return -1; + return validtm(&date)? tm2sec(&date) - 60*date.tzoff: -1; +} + +int +convert(char *timestr) +{ + char *copy; + Time tstime; + + copy = strdup(timestr); + if (copy == nil) + sysfatal("out of memory"); + tstime = seconds(copy); + free(copy); + if (tstime == -1) { + fprint(2, "%s: `%s' not a valid date\n", argv0, timestr); + return 1; + } + print("%lud\n", tstime); + return 0; +} + static void usage(void) { @@ -42,31 +242,225 @@ usage(void) void main(int argc, char **argv) { - Tm tm; - char **f, *fmt; - int i; + int i, sts; - fmt = nil; + sts = 0; ARGBEGIN{ - case 'f': - fmt = EARGF(usage()); - break; default: usage(); - }ARGEND; - - for(i = 0; i < argc; i++){ - if(fmt != nil){ - if(tmparse(&tm, fmt, argv[i], nil) != nil) - goto Found; - }else{ - for(f = formats; *f != nil; f++) - if(tmparse(&tm, *f, argv[i], nil) != nil) - goto Found; - } - sysfatal("tmparse: %r"); -Found: - print("%lld\n", tm.abs); - } - exits(nil); + }ARGEND + if (argc == 0) + usage(); + for (i = 0; i < argc; i++) + sts |= convert(argv[i]); + exits(sts != 0? "bad": 0); } + +/* + * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this + * is WAY faster than the generic bsearch(). + */ +Datetok * +datebsearch(char *key, Datetok *base, unsigned nel) +{ + int cmp; + Datetok *last = base + nel - 1, *pos; + + while (last >= base) { + pos = base + ((last - base) >> 1); + cmp = key[0] - pos->token[0]; + if (cmp == 0) { + cmp = strncmp(key, pos->token, Maxtok); + if (cmp == 0) + return pos; + } + if (cmp < 0) + last = pos - 1; + else + base = pos + 1; + } + return 0; +} + +Datetok * +datetoktype(char *s, int *bigvalp) +{ + char *cp = s; + char c = *cp; + static Datetok t; + Datetok *tp = &t; + + if (isascii(c) && isdigit(c)) { + int len = strlen(cp); + + if (len > 3 && (cp[1] == ':' || cp[2] == ':')) + tp->type = Timetok; + else { + if (bigvalp != nil) + *bigvalp = atoi(cp); /* won't fit in tp->value */ + if (len == 4) + tp->type = Year; + else if (++dtok_numparsed == 1) + tp->type = Day; + else + tp->type = Year; + } + } else if (c == '-' || c == '+') { + int val = atoi(cp + 1); + int hr = val / 100; + int min = val % 100; + + val = hr*60 + min; + TOVAL(tp, c == '-'? -val: val); + tp->type = Tz; + } else { + char lowtoken[Maxtok+1]; + char *ltp = lowtoken, *endltp = lowtoken+Maxtok; + + /* copy to lowtoken to avoid modifying s */ + while ((c = *cp++) != '\0' && ltp < endltp) + *ltp++ = (isascii(c) && isupper(c)? tolower(c): c); + *ltp = '\0'; + tp = datebsearch(lowtoken, datetktbl, szdatetktbl); + if (tp == nil) { + tp = &t; + tp->type = Ignore; + } + } + return tp; +} + + +/* + * to keep this table reasonably small, we divide the lexval for Tz and Dtz + * entries by 10 and truncate the text field at MAXTOKLEN characters. + * the text field is not guaranteed to be NUL-terminated. + */ +static Datetok datetktbl[] = { +/* text token lexval */ + "acsst", Dtz, 63, /* Cent. Australia */ + "acst", Tz, 57, /* Cent. Australia */ + "adt", Dtz, -18, /* Atlantic Daylight Time */ + "aesst", Dtz, 66, /* E. Australia */ + "aest", Tz, 60, /* Australia Eastern Std Time */ + "ahst", Tz, 60, /* Alaska-Hawaii Std Time */ + "am", Ampm, AM, + "apr", Month, 4, + "april", Month, 4, + "ast", Tz, -24, /* Atlantic Std Time (Canada) */ + "at", Ignore, 0, /* "at" (throwaway) */ + "aug", Month, 8, + "august", Month, 8, + "awsst", Dtz, 54, /* W. Australia */ + "awst", Tz, 48, /* W. Australia */ + "bst", Tz, 6, /* British Summer Time */ + "bt", Tz, 18, /* Baghdad Time */ + "cadt", Dtz, 63, /* Central Australian DST */ + "cast", Tz, 57, /* Central Australian ST */ + "cat", Tz, -60, /* Central Alaska Time */ + "cct", Tz, 48, /* China Coast */ + "cdt", Dtz, -30, /* Central Daylight Time */ + "cet", Tz, 6, /* Central European Time */ + "cetdst", Dtz, 12, /* Central European Dayl.Time */ + "cst", Tz, -36, /* Central Standard Time */ + "dec", Month, 12, + "decemb", Month, 12, + "dnt", Tz, 6, /* Dansk Normal Tid */ + "dst", Ignore, 0, + "east", Tz, -60, /* East Australian Std Time */ + "edt", Dtz, -24, /* Eastern Daylight Time */ + "eet", Tz, 12, /* East. Europe, USSR Zone 1 */ + "eetdst", Dtz, 18, /* Eastern Europe */ + "est", Tz, -30, /* Eastern Standard Time */ + "feb", Month, 2, + "februa", Month, 2, + "fri", Ignore, 5, + "friday", Ignore, 5, + "fst", Tz, 6, /* French Summer Time */ + "fwt", Dtz, 12, /* French Winter Time */ + "gmt", Tz, 0, /* Greenwish Mean Time */ + "gst", Tz, 60, /* Guam Std Time, USSR Zone 9 */ + "hdt", Dtz, -54, /* Hawaii/Alaska */ + "hmt", Dtz, 18, /* Hellas ? ? */ + "hst", Tz, -60, /* Hawaii Std Time */ + "idle", Tz, 72, /* Intl. Date Line, East */ + "idlw", Tz, -72, /* Intl. Date Line, West */ + "ist", Tz, 12, /* Israel */ + "it", Tz, 22, /* Iran Time */ + "jan", Month, 1, + "januar", Month, 1, + "jst", Tz, 54, /* Japan Std Time,USSR Zone 8 */ + "jt", Tz, 45, /* Java Time */ + "jul", Month, 7, + "july", Month, 7, + "jun", Month, 6, + "june", Month, 6, + "kst", Tz, 54, /* Korea Standard Time */ + "ligt", Tz, 60, /* From Melbourne, Australia */ + "mar", Month, 3, + "march", Month, 3, + "may", Month, 5, + "mdt", Dtz, -36, /* Mountain Daylight Time */ + "mest", Dtz, 12, /* Middle Europe Summer Time */ + "met", Tz, 6, /* Middle Europe Time */ + "metdst", Dtz, 12, /* Middle Europe Daylight Time*/ + "mewt", Tz, 6, /* Middle Europe Winter Time */ + "mez", Tz, 6, /* Middle Europe Zone */ + "mon", Ignore, 1, + "monday", Ignore, 1, + "mst", Tz, -42, /* Mountain Standard Time */ + "mt", Tz, 51, /* Moluccas Time */ + "ndt", Dtz, -15, /* Nfld. Daylight Time */ + "nft", Tz, -21, /* Newfoundland Standard Time */ + "nor", Tz, 6, /* Norway Standard Time */ + "nov", Month, 11, + "novemb", Month, 11, + "nst", Tz, -21, /* Nfld. Standard Time */ + "nt", Tz, -66, /* Nome Time */ + "nzdt", Dtz, 78, /* New Zealand Daylight Time */ + "nzst", Tz, 72, /* New Zealand Standard Time */ + "nzt", Tz, 72, /* New Zealand Time */ + "oct", Month, 10, + "octobe", Month, 10, + "on", Ignore, 0, /* "on" (throwaway) */ + "pdt", Dtz, -42, /* Pacific Daylight Time */ + "pm", Ampm, PM, + "pst", Tz, -48, /* Pacific Standard Time */ + "sadt", Dtz, 63, /* S. Australian Dayl. Time */ + "sast", Tz, 57, /* South Australian Std Time */ + "sat", Ignore, 6, + "saturd", Ignore, 6, + "sep", Month, 9, + "sept", Month, 9, + "septem", Month, 9, + "set", Tz, -6, /* Seychelles Time ?? */ + "sst", Dtz, 12, /* Swedish Summer Time */ + "sun", Ignore, 0, + "sunday", Ignore, 0, + "swt", Tz, 6, /* Swedish Winter Time */ + "thu", Ignore, 4, + "thur", Ignore, 4, + "thurs", Ignore, 4, + "thursd", Ignore, 4, + "tue", Ignore, 2, + "tues", Ignore, 2, + "tuesda", Ignore, 2, + "ut", Tz, 0, + "utc", Tz, 0, + "wadt", Dtz, 48, /* West Australian DST */ + "wast", Tz, 42, /* West Australian Std Time */ + "wat", Tz, -6, /* West Africa Time */ + "wdt", Dtz, 54, /* West Australian DST */ + "wed", Ignore, 3, + "wednes", Ignore, 3, + "weds", Ignore, 3, + "wet", Tz, 0, /* Western Europe */ + "wetdst", Dtz, 6, /* Western Europe */ + "wst", Tz, 48, /* West Australian Std Time */ + "ydt", Dtz, -48, /* Yukon Daylight Time */ + "yst", Tz, -54, /* Yukon Standard Time */ + "zp4", Tz, -24, /* GMT +4 hours. */ + "zp5", Tz, -30, /* GMT +5 hours. */ + "zp6", Tz, -36, /* GMT +6 hours. */ +}; +static unsigned szdatetktbl = nelem(datetktbl); diff --git a/sys/src/libc/9sys/mkfile b/sys/src/libc/9sys/mkfile index 2f33a149a..686a7bd49 100644 --- a/sys/src/libc/9sys/mkfile +++ b/sys/src/libc/9sys/mkfile @@ -10,6 +10,7 @@ OFILES=\ convM2S.$O\ convS2M.$O\ cputime.$O\ + ctime.$O\ dial.$O\ dirfstat.$O\ dirfwstat.$O\ @@ -46,6 +47,7 @@ OFILES=\ sysname.$O\ time.$O\ times.$O\ + tm2sec.$O\ truerand.$O\ wait.$O\ waitpid.$O\ diff --git a/sys/src/libc/port/date.c b/sys/src/libc/port/date.c deleted file mode 100644 index 3f06c18cf..000000000 --- a/sys/src/libc/port/date.c +++ /dev/null @@ -1,872 +0,0 @@ -#include -#include - -typedef struct Tzabbrev Tzabbrev; -typedef struct Tzoffpair Tzoffpair; - -#define Ctimefmt "W MMM _D hh:mm:ss ZZZ YYYY\n" -enum { - Tzsize = 150, - Nsec = 1000*1000*1000, - Daysec = (vlong)24*3600, - Days400y = 365*400 + 4*25 - 3, - Days4y = 365*4 + 1, -}; - -enum { - Cend, - Cspace, - Cnum, - Cletter, - Cpunct, -}; - -struct Tzone { - char tzname[16]; - char stname[4]; - char dlname[4]; - long stdiff; - long dldiff; - long dlpairs[Tzsize]; -}; - -static char tfmt[128]; -static QLock zlock; -static int nzones; -static Tzone **zones; -static int mdays[] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; -static char *wday[] = { - "Sunday","Monday","Tuesday", - "Wednesday","Thursday","Friday", - "Saturday", nil, -}; -static char *month[] = { - "January", "February", "March", - "April", "May", "June", "July", - "August", "September", "October", - "November", "December", nil -}; - -struct Tzabbrev { - char *abbr; - char *name; -}; - -struct Tzoffpair { - char *abbr; - int off; -}; - -/* Obsolete time zone names. Hardcoded to match RFC5322 */ -static Tzabbrev tzabbrev[] = { - {"UT", "GMT"}, {"GMT", "GMT"}, {"UTC", "GMT"}, - {"EST", "US_Eastern"}, {"EDT", "US_Eastern"}, - {"CST", "US_Central"}, {"CDT", "US_Central"}, - {"MST", "US_Mountain"}, {"MDT", "US_Mountain"}, - {"PST", "US_Pacific"}, {"PDT", "US_Pacific"}, - {nil}, -}; - -/* Military timezone names */ -static Tzoffpair milabbrev[] = { - {"A", -1*3600}, {"B", -2*3600}, {"C", -3*3600}, - {"D", -4*3600}, {"E", -5*3600}, {"F", -6*3600}, - {"G", -7*3600}, {"H", -8*3600}, {"I", -9*3600}, - {"K", -10*3600}, {"L", -11*3600}, {"M", -12*3600}, - {"N", +1*3600}, {"O", +2*3600}, {"P", +3*3600}, - {"Q", +4*3600}, {"R", +5*3600}, {"S", +6*3600}, - {"T", +7*3600}, {"U", +8*3600}, {"V", +9*3600}, - {"W", +10*3600}, {"X", +11*3600}, {"Y", +12*3600}, - {"Z", 0}, {nil, 0} -}; - -static int -isleap(int y) -{ - return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); -} - -static int -rdname(char **f, char *p) -{ - int c, i; - - while((c = *(*f)++) != 0) - if(c != ' ' && c != '\n') - break; - for(i=0; i<3; i++) { - if(c == ' ' || c == '\n') - return 1; - *p++ = c; - c = *(*f)++; - } - if(c != ' ' && c != '\n') - return 1; - *p = 0; - return 0; -} - -static int -rdlong(char **f, long *p) -{ - int c, s; - long l; - - s = 0; - while((c = *(*f)++) != 0){ - if(c == '-') - s++; - else if(c != ' ' && c != '\n') - break; - } - if(c == 0) { - *p = 0; - return 0; - } - l = 0; - for(;;) { - if(c == ' ' || c == '\n') - break; - if(c < '0' || c > '9') - return 1; - l = l*10 + c-'0'; - c = *(*f)++; - } - if(s) - l = -l; - *p = l; - return 0; -} - -static int -loadzone(Tzone *tz, char *name) -{ - char buf[Tzsize*11+30], path[128], *p; - int i, f, r; - - if(strcmp(name, "local") == 0) - snprint(path, sizeof(path), "/env/timezone"); - else - snprint(path, sizeof(path), "/adm/timezone/%s", name); - memset(buf, 0, sizeof(buf)); - if((f = open(path, 0)) == -1) - return -1; - r = read(f, buf, sizeof(buf)); - close(f); - if(r == sizeof(buf) || r == -1) - return -1; - p = buf; - if(rdname(&p, tz->stname)) - return -1; - if(rdlong(&p, &tz->stdiff)) - return -1; - if(rdname(&p, tz->dlname)) - return -1; - if(rdlong(&p, &tz->dldiff)) - return -1; - for(i=0; i < Tzsize; i++) { - if(rdlong(&p, &tz->dlpairs[i])) - return -1; - if(tz->dlpairs[i] == 0) - return 0; - } - return -1; -} - -Tzone* -tmgetzone(char *tzname) -{ - Tzone *tz, **newzones; - int i; - - if(tzname == nil) - tzname = "GMT"; - qlock(&zlock); - for(i = 0; i < nzones; i++){ - tz = zones[i]; - if(strcmp(tz->stname, tzname) == 0) - goto found; - if(strcmp(tz->dlname, tzname) == 0) - goto found; - if(strcmp(tz->tzname, tzname) == 0) - goto found; - } - - tz = malloc(sizeof(Tzone)); - if(tz == nil) - goto error; - newzones = realloc(zones, (nzones + 1) * sizeof(Tzone*)); - if(newzones == nil) - goto error; - if(loadzone(tz, tzname) != 0) - goto error; - if(snprint(tz->tzname, sizeof(tz->tzname), tzname) >= sizeof(tz->tzname)){ - werrstr("timezone name too long"); - return nil; - } - zones = newzones; - zones[nzones] = tz; - nzones++; -found: - qunlock(&zlock); - return tz; -error: - free(tz); - qunlock(&zlock); - return nil; -} - -static void -getzoneoff(Tzone *tz, vlong abs, Tm *tm) -{ - long dl, *p; - dl = 0; - if(tz == nil){ - snprint(tm->zone, sizeof(tm->zone), "GMT"); - tm->tzoff = 0; - return; - } - for(p = tz->dlpairs; *p; p += 2) - if(abs >= p[0] && abs < p[1]) - dl = 1; - if(dl){ - snprint(tm->zone, sizeof(tm->zone), tz->dlname); - tm->tzoff = tz->dldiff; - }else{ - snprint(tm->zone, sizeof(tm->zone), tz->stname); - tm->tzoff = tz->stdiff; - } -} - -static Tm* -tmfill(Tm *tm, vlong abs, vlong nsec) -{ - vlong zrel, j, y, m, d, t, e; - int i; - - tm->abs = abs; - zrel = abs + tm->tzoff; - t = zrel % Daysec; - e = zrel / Daysec; - if(t < 0){ - t += Daysec; - e -= 1; - } - - t += nsec/Nsec; - tm->sec = t % 60; - t /= 60; - tm->min = t % 60; - t /= 60; - tm->hour = t; - tm->wday = (e + 4) % 7; - - /* - * Split up year, month, day. - * - * Implemented according to "Algorithm 199, - * conversions between calendar date and - * Julian day number", Robert G. Tantzen, - * Air Force Missile Development - * Center, Holloman AFB, New Mex. - * - * Lots of magic. - */ - j = (zrel + 2440588 * Daysec) / (Daysec) - 1721119; - y = (4 * j - 1) / Days400y; - j = 4 * j - 1 - Days400y * y; - d = j / 4; - j = (4 * d + 3) / Days4y; - d = 4 * d + 3 - Days4y * j; - d = (d + 4) / 4 ; - m = (5 * d - 3) / 153; - d = 5 * d - 3 - 153 * m; - d = (d + 5) / 5; - y = 100 * y + j; - - if(m < 10) - m += 3; - else{ - m -= 9; - y++; - } - - /* there's no year 0 */ - if(y <= 0) - y--; - /* and if j negative, the day and month are also negative */ - if(m < 0) - m += 12; - if(d < 0) - d += mdays[m - 1]; - - tm->yday = d; - for(i = 0; i < m - 1; i++) - tm->yday += mdays[i]; - if(m > 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) - tm->yday++; - tm->year = y - 1900; - tm->mon = m - 1; - tm->mday = d; - tm->nsec = nsec%Nsec; - return tm; -} - - -Tm* -tmtime(Tm *tm, vlong abs, Tzone *tz) -{ - return tmtimens(tm, abs, 0, tz); -} - -Tm* -tmtimens(Tm *tm, vlong abs, int ns, Tzone *tz) -{ - tm->tz = tz; - getzoneoff(tz, abs, tm); - return tmfill(tm, abs, ns); -} - -Tm* -tmnow(Tm *tm, Tzone *tz) -{ - vlong ns; - - ns = nsec(); - return tmtimens(tm, nsec()/Nsec, ns%Nsec, tz); -} - -Tm* -tmnorm(Tm *tm) -{ - vlong c, yadj, j, abs, y, m, d; - - if(tm->mon > 1){ - m = tm->mon - 2; - y = tm->year + 1900; - }else{ - m = tm->mon + 10; - y = tm->year - 1901; - } - d = tm->mday; - c = y / 100; - yadj = y - 100 * c; - j = (c * Days400y / 4 + - Days4y * yadj / 4 + - (153 * m + 2)/5 + d - - 719469); - abs = j * Daysec; - abs += tm->hour * 3600; - abs += tm->min * 60; - abs += tm->sec; - abs -= tm->tzoff; - return tmfill(tm, abs, tm->nsec); -} - -static int -τconv(Fmt *f) -{ - int depth, n, w, h, m, c0, sgn, pad, off; - char *p, *am; - Tmfmt tf; - Tm *tm; - - n = 0; - tf = va_arg(f->args, Tmfmt); - tm = tf.tm; - p = tf.fmt; - if(p == nil) - p = Ctimefmt; - while(*p){ - w = 1; - pad = 0; - while(*p == '_'){ - pad++; - p++; - } - c0 = *p++; - while(c0 && *p == c0){ - w++; - p++; - } - pad += w; - switch(c0){ - case 0: - break; - case 'Y': - switch(w){ - case 1: n += fmtprint(f, "%*d", pad, tm->year + 1900); break; - case 2: n += fmtprint(f, "%*d", pad, tm->year % 100); break; - case 4: n += fmtprint(f, "%*d", pad, tm->year + 1900); break; - default: goto badfmt; - } - break; - case 'M': - switch(w){ - case 1: n += fmtprint(f, "%*d", pad, tm->mon + 1); break; - case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->mon + 1); break; - case 3: n += fmtprint(f, "%*.3s", pad, month[tm->mon]); break; - case 4: n += fmtprint(f, "%*s", pad, month[tm->mon]); break; - default: goto badfmt; - } - break; - case 'D': - switch(w){ - case 1: n += fmtprint(f, "%*d", pad, tm->mday); break; - case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->mday); break; - default: goto badfmt; - } - break; - case 'W': - switch(w){ - case 1: n += fmtprint(f, "%*.3s", pad, wday[tm->wday]); break; - case 2: n += fmtprint(f, "%*s", pad, wday[tm->wday]); break; - default: goto badfmt; - } - break; - case 'H': - switch(w){ - case 1: n += fmtprint(f, "%*d", pad, tm->hour % 12); break; - case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->hour % 12); break; - default: goto badfmt; - } - break; - case 'h': - switch(w){ - case 1: n += fmtprint(f, "%*d", pad, tm->hour); break; - case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->hour); break; - default: goto badfmt; - } - break; - case 'm': - switch(w){ - case 1: n += fmtprint(f, "%*d", pad, tm->min); break; - case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->min); break; - default: goto badfmt; - } - break; - case 's': - switch(w){ - case 1: n += fmtprint(f, "%*d", pad, tm->sec); break; - case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->sec); break; - default: goto badfmt; - } - break; - case 'z': - if(w != 1) - goto badfmt; - case 'Z': - sgn = (tm->tzoff < 0) ? '-' : '+'; - off = (tm->tzoff < 0) ? -tm->tzoff : tm->tzoff; - h = off/3600; - m = (off/60)%60; - if(w < 3 && pad < 5) - pad = 5; - switch(w){ - case 1: n += fmtprint(f, "%*s%c%02d%02d", pad-5, "", sgn, h, m); break; - case 2: n += fmtprint(f, "%*s%c%02d:%02d", pad-5, "", sgn, h, m); break; - case 3: n += fmtprint(f, "%*s", pad, tm->zone); break; - } - break; - case 'A': - case 'a': - if(w != 1) - goto badfmt; - if(c0 == 'a') - am = (tm->hour < 12) ? "am" : "pm"; - else - am = (tm->hour < 12) ? "AM" : "PM"; - n += fmtprint(f, "%*s", pad, am); - break; - case '[': - depth = 1; - while(*p){ - if(*p == '[') - depth++; - if(*p == ']') - depth--; - if(*p == '\\') - p++; - if(depth == 0) - break; - fmtrune(f, *p++); - } - if(*p++ != ']') - goto badfmt; - break; - default: - n += fmtrune(f, c0); - break; - } - } - return n; -badfmt: - werrstr("garbled format %s", tf.fmt); - return -1; -} - -static int -getnum(char **ps, int maxw, int *ok) -{ - char *s, *e; - int n; - - n = 0; - e = *ps + maxw; - for(s = *ps; s != e && *s >= '0' && *s <= '9'; s++){ - n *= 10; - n += *s - '0'; - } - *ok = s != *ps; - *ps = s; - return n; -} - -static int -lookup(char **s, char **tab, int len, int *ok) -{ - int nc, i; - - *ok = 0; - for(i = 0; *tab; tab++){ - nc = (len != -1) ? len : strlen(*tab); - if(cistrncmp(*s, *tab, nc) == 0){ - *s += nc; - *ok = 1; - return i; - } - i++; - } - *ok = 0; - return -1; -} - -Tm* -tmparse(Tm *tm, char *fmt, char *str, Tzone *tz) -{ - int depth, w, c0, zs, z0, z1, ampm, zoned, sloppy, tzo, ok; - char *s, *p, *q; - Tzone *zparsed; - Tzabbrev *a; - Tzoffpair *m; - - p = fmt; - s = str; - tzo = 0; - ampm = -1; - zoned = 0; - zparsed = nil; - sloppy = 0; - /* Default all fields */ - tmtime(tm, 0, nil); - if(*p == '~'){ - sloppy = 1; - p++; - } - while(*p){ - w = 1; - c0 = *p++; - if(c0 == '?'){ - w = -1; - c0 = *p++; - } - while(*p == c0){ - if(w != -1) w++; - p++; - } - ok = 1; - switch(c0){ - case 'Y': - switch(w){ - case -1: - tm->year = getnum(&s, 4, &ok); - if(tm->year > 100) tm->year -= 1900; - break; - case 1: tm->year = getnum(&s, 4, &ok) - 1900; break; - case 2: tm->year = getnum(&s, 2, &ok); break; - case 3: - case 4: tm->year = getnum(&s, 4, &ok) - 1900; break; - default: goto badfmt; - } - break; - case 'M': - switch(w){ - case -1: - tm->mon = getnum(&s, 2, &ok) - 1; - if(!ok) tm->mon = lookup(&s, month, -1, &ok); - if(!ok) tm->mon = lookup(&s, month, 3, &ok); - break; - case 1: - case 2: tm->mon = getnum(&s, 2, &ok) - 1; break; - case 3: tm->mon = lookup(&s, month, 3, &ok); break; - case 4: tm->mon = lookup(&s, month, -1, &ok); break; - default: goto badfmt; - } - break; - case 'D': - switch(w){ - case -1: - case 1: - case 2: tm->mday = getnum(&s, 2, &ok); break; - default: goto badfmt; - } - break; - case 'W': - switch(w){ - case -1: - tm->wday = lookup(&s, wday, -1, &ok); - if(!ok) tm->wday = lookup(&s, wday, 3, &ok); - break; - case 1: tm->wday = lookup(&s, wday, 3, &ok); break; - case 2: tm->wday = lookup(&s, wday, -1, &ok); break; - default: goto badfmt; - } - break; - case 'h': - switch(w){ - case -1: - case 1: - case 2: tm->hour = getnum(&s, 2, &ok); break; - default: goto badfmt; - } - break; - case 'm': - switch(w){ - case -1: - case 1: - case 2: tm->min = getnum(&s, 2, &ok); break; - default: goto badfmt; - } - break; - case 's': - switch(w){ - case -1: - case 1: - case 2: tm->sec = getnum(&s, 2, &ok); break; - default: goto badfmt; - } - break; - case 'z': - if(w != 1) - goto badfmt; - case 'Z': - zs = 0; - zoned = 1; - switch(*s++){ - case '+': zs = 1; break; - case '-': zs = -1; break; - default: s--; break; - } - switch(w){ - case -1: - case 3: - for(a = tzabbrev; a->abbr; a++) - if(strncmp(s, a->abbr, strlen(a->abbr)) == 0) - break; - if(a->abbr != nil){ - s += strlen(a->abbr); - zparsed = tmgetzone(a->name); - if(zparsed == nil){ - werrstr("unloadable zone %s (%s)", a->abbr, a->name); - return nil; - } - break; - } - for(m = milabbrev; m->abbr != nil; m++) - if(strncmp(s, m->abbr, strlen(m->abbr)) == 0) - break; - if(m->abbr != nil){ - snprint(tm->zone, sizeof(tm->zone), "%s", m->abbr); - tzo = m->off; - break; - } - /* fall through */ - case 1: - /* offset: [+-]hhmm */ - q = s; - z0 = getnum(&s, 4, &ok); - if(s - q == 4){ - z1 = z0 % 100; - if(z0/100 > 13 || z1 >= 60) - goto baddate; - tzo = zs*(3600*z0/100 + 60*z1); - snprint(tm->zone, sizeof(tm->zone), "%c%02d%02d", zs<0?'-':'+', z0/100, z1); - break; - } - if(w != -1) - goto baddate; - s = q; - /* fall through */ - case 2: - /* offset: [+-]hh:mm */ - z0 = getnum(&s, 2, &ok); - if(*s++ != ':') - goto baddate; - z1 = getnum(&s, 2, &ok); - if(z1 > 60) - goto baddate; - tzo = zs*(3600*z0 + 60*z1); - snprint(tm->zone, sizeof(tm->zone), "%c%d02:%02d", zs<0?'-':'+', z0, z1); - break; - } - break; - case 'A': - case 'a': - if(cistrncmp(s, "am", 2) == 0) - ampm = 0; - else if(cistrncmp(s, "pm", 2) == 0) - ampm = 1; - else - goto baddate; - break; - case '[': - depth = 1; - while(*p){ - if(*p == '[') - depth++; - if(*p == ']') - depth--; - if(*p == '\\') - p++; - if(depth == 0) - break; - if(*s == 0) - goto baddate; - if(*s++ != *p++) - goto baddate; - } - if(*p != ']') - goto badfmt; - p++; - break; - case ',': - case ' ': - case '\t': - if(*s != ' ' && *s != '\t' && *s != ',') - goto baddate; - while(*p == ' ' || *p == '\t' || *p == ',') - p++; - while(*s == ' ' || *s == '\t' || *s == ',') - s++; - break; - default: - if(*s == 0) - goto baddate; - if(*s++ != c0) - goto baddate; - break; - } - if(!ok) - goto baddate; - } - - if(!sloppy && ampm != -1 && tm->hour > 12) - goto baddate; - if(ampm == 1) - tm->hour += 12; - /* - * If we're allowing sloppy date ranges, - * we'll normalize out of range values. - */ - if(!sloppy){ - if(tm->yday < 0 && tm->yday > 365 + isleap(tm->year + 1900)) - goto baddate; - if(tm->wday < 0 && tm->wday > 6) - goto baddate; - if(tm->mon < 0 || tm->mon > 11) - goto baddate; - if(tm->mday < 0 || tm->mday > mdays[tm->mon]) - goto baddate; - if(tm->hour < 0 || tm->hour > 24) - goto baddate; - if(tm->min < 0 || tm->min > 59) - goto baddate; - if(tm->sec < 0 || tm->sec > 60) - goto baddate; - if(tm->nsec < 0 || tm->nsec > Nsec) - goto baddate; - } - - /* - * Normalizing gives us the local time, - * but because we havnen't applied the - * timezone, we think we're GMT. So, we - * need to shift backwards. Then, we move - * the "GMT that was local" back to local - * time. - */ - tmnorm(tm); - tm->tzoff = tzo; - if(!zoned) - getzoneoff(tz, tm->abs, tm); - else if(zparsed != nil) - getzoneoff(zparsed, tm->abs, tm); - tm->abs -= tm->tzoff; - if(tz != nil || !zoned) - tmtime(tm, tm->abs, tz); - return tm; -baddate: - werrstr("invalid date %s near '%s'", str, s); - return nil; -badfmt: - werrstr("garbled format %s near '%s'", fmt, p); - return nil; -} - -Tmfmt -tmfmt(Tm *d, char *fmt) -{ - return (Tmfmt){fmt, d}; -} - -void -tmfmtinstall(void) -{ - fmtinstall(L'τ', τconv); -} - -/* These legacy functions need access to τconv */ -static char* -dotmfmt(Fmt *f, ...) -{ - static char buf[30]; - va_list ap; - - va_start(ap, f); - f->runes = 0; - f->start = buf; - f->to = buf; - f->stop = buf + sizeof(buf) - 1; - f->flush = nil; - f->farg = nil; - f->nfmt = 0; - f->args = ap; - τconv(f); - va_end(ap); - buf[sizeof(buf) - 1] = 0; - return buf; -} - -char* -asctime(Tm* tm) -{ - Tmfmt tf; - Fmt f; - - tf = tmfmt(tm, nil); - return dotmfmt(&f, tf); -} - -char* -ctime(long abs) -{ - Tzone *tz; - Tm tm; - - /* No error checking: the API doesn't allow it. */ - tz = tmgetzone("local"); - tmtime(&tm, abs, tz); - return asctime(&tm); -} diff --git a/sys/src/libc/port/gmtime.c b/sys/src/libc/port/gmtime.c deleted file mode 100644 index c13234cce..000000000 --- a/sys/src/libc/port/gmtime.c +++ /dev/null @@ -1,27 +0,0 @@ -#include -#include - -Tm* -gmtime(long abs) -{ - static Tm tm; - return tmtime(&tm, abs, nil); -} - -Tm* -localtime(long abs) -{ - Tzone *tz; - static Tm tm; - - /* No error checking: the API doesn't allow it. */ - tz = tmgetzone("local"); - return tmtime(&tm, abs, tz); -} - -long -tm2sec(Tm *tm) -{ - tmnorm(tm); - return tm->abs; -} diff --git a/sys/src/libc/port/mkfile b/sys/src/libc/port/mkfile index 8c9eb0574..402990422 100644 --- a/sys/src/libc/port/mkfile +++ b/sys/src/libc/port/mkfile @@ -20,7 +20,6 @@ CFILES=\ cleanname.c\ crypt.c\ ctype.c\ - date.c\ encodefmt.c\ execl.c\ exits.c\ @@ -33,7 +32,6 @@ CFILES=\ getcallerpc.c\ getfields.c\ getuser.c\ - gmtime.c\ hangup.c\ hypot.c\ lnrand.c\