Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: time conversion / time zone system Message-ID: <1479@panda.UUCP> Date: 7 Mar 86 22:43:08 GMT Sender: jpn@panda.UUCP Lines: 2747 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 14 Submitted by: talcott!seismo!elsie!ado #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # README # Makefile # settz.3 # tzfile.5 # tzcomp.8 # tzfile.h # tzcomp.c # scheck.c # strchr.c # mkdir.c # tzdump.c # settz.c # years.sh # asia # australasia # europe # etcetera # northamerica # pacificnew # This archive created: Fri Mar 7 17:02:24 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'README'" '(620 characters)' if test -f 'README' then echo shar: will not over-write existing file "'README'" else cat << \SHAR_EOF > 'README' @(#)README 2.1 Please send comments or information to seismo!elsie!ado (with, perhaps, carbon copes to cbosgd!mark, seismo!linus!encore!necis!geo, and seismo!munnari!kre, who are responsible for any good ideas that show up here). Historical local time information has been included here not because it should be part of the standard (or, indeed, anyone's product), but rather to: * give people an idea of the variety of local time rules that have existed in the past and thus an idea of the variety that may be expected in the future; * provide a test of the generality of the local time rule description system. SHAR_EOF if test 620 -ne "`wc -c < 'README'`" then echo shar: error transmitting "'README'" '(should have been 620 characters)' fi fi echo shar: extracting "'Makefile'" '(1713 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \SHAR_EOF > 'Makefile' # @(#)Makefile 2.1 # If you want something other than Eastern United States time used on your # system, change the line below (after finding the zone you want in the # time zone files, or adding it to a time zone file). # Alternately, if you discover you've got the wrong time zone, you can just # tzcomp -l rightzone LOCALTIME= Eastern # Use an absolute path name for TZDIR unless you're just testing the software. TZDIR= /etc/tzdir # LINTFLAGS is set for 4.1bsd systems. If you're using System V, you'll want # to comment out the "LINTFLAGS=" line. LINTFLAGS= -phbaaxc DEBUG= LFLAGS= CFLAGS= $(DEBUG) -O -DOBJECTID -DTZDIR=\"$(TZDIR)\" TZCSRCS= tzcomp.c scheck.c strchr.c mkdir.c TZCOBJS= tzcomp.o scheck.o strchr.o mkdir.o TZDSRCS= tzdump.c settz.c TZDOBJS= tzdump.o settz.o DOCS= README Makefile settz.3 tzfile.5 tzcomp.8 SOURCES= tzfile.h $(TZCSRCS) $(TZDSRCS) years.sh DATA= asia australasia europe etcetera northamerica pacificnew ENCHILADA= $(DOCS) $(SOURCES) $(DATA) all: REDID_BINARIES tzdump REDID_BINARIES: $(TZDIR) tzcomp $(DATA) years tzcomp -l $(LOCALTIME) -d $(TZDIR) $(DATA) cp /dev/null $@ tzdump: $(TZDOBJS) $(CC) $(LFLAGS) $(TZDOBJS) -o $@ tzcomp: $(TZCOBJS) $(CC) $(LFLAGS) $(TZCOBJS) -o $@ $(TZDIR): mkdir $@ years: years.sh rm -f $@ cp $? $@ chmod 555 $@ BUNDLE1: $(DOCS) bundle $(DOCS) > BUNDLE1 BUNDLE2: $(SOURCES) bundle $(SOURCES) > BUNDLE2 BUNDLE3: $(DATA) bundle $(DATA) > BUNDLE3 $(ENCHILADA): sccs get $(REL) $(REV) $@ sure: $(TZCSRCS) $(TZDSRCS) lint $(LINTFLAGS) $(TZCSRCS) lint $(LINTFLAGS) $(TZDSRCS) clean: rm -f core *.o *.out REDID_BINARIES years tzdump tzcomp BUNDLE \#* CLEAN: clean sccs clean tzdump.o tzcomp.o settz.o: tzfile.h SHAR_EOF if test 1713 -ne "`wc -c < 'Makefile'`" then echo shar: error transmitting "'Makefile'" '(should have been 1713 characters)' fi fi echo shar: extracting "'settz.3'" '(1606 characters)' if test -f 'settz.3' then echo shar: will not over-write existing file "'settz.3'" else cat << \SHAR_EOF > 'settz.3' .TH SETTZ 3 .SH NAME settz, newctime, newlocaltime \- convert date and time to ASCII .SH SYNOPSIS .nf .B settz(zonename) .B char *zonename; .PP .B char *newctime(clock) .B long *clock; .PP .B #include "time.h" .PP .B struct tm *newlocaltime(clock) .B long *clock; .PP .B char *tz_abbr; .SH DESCRIPTION .I Settz sets time conversion information used by .IR newlocaltime . If .I zonename begins with a slash, it is used as the absolute pathname of the .IR tzfile (5)-format file from which to read the time conversion information; if .I zonename begins with some other character, it is used as a pathname relative to the standard time conversion information directory. A call of the form .ti +.5i .B settz("") .br causes built-in information about GMT to be used; a call of the form .ti +.5i .B settz((char *) 0) .br is treated as if it were a call of the form .ti +.5i .B settz("localtime") .PP .I Newlocaltime has the same argument and return value as .IR localtime . If .I newlocaltime is called before .I settz is called, .I newlocaltime calls .I settz with the value returned by .B getenv("TZ"). .I Newlocaltime sets tz_abbr to a pointer to an ASCII string that's the time zone abbreviation to be used with .IR newlocaltime 's return value. .PP .I Newctime returns .IR "asctime(newlocaltime(*clock))" . .SH DIAGNOSTICS .I Settz returns zero if all seems well; it returns negative one otherwise (and sets things up so that its built-in information about GMT is used). .SH FILES /etc/tzdir standard time conversion information directory .SH "SEE ALSO" ctime(3), getenv(3), tzfile(5) .. @(#)settz.3 2.1 SHAR_EOF if test 1606 -ne "`wc -c < 'settz.3'`" then echo shar: error transmitting "'settz.3'" '(should have been 1606 characters)' fi fi echo shar: extracting "'tzfile.5'" '(2100 characters)' if test -f 'tzfile.5' then echo shar: will not over-write existing file "'tzfile.5'" else cat << \SHAR_EOF > 'tzfile.5' .TH TZFILE 5 .SH NAME tzfile \- time zone information .SH SYNOPSIS .B #include "tzfile.h" .SH DESCRIPTION The time zone information files used by .IR settz (3) and .IR newlocaltime (3) begin with a .I tzinfo structure (as defined in the include file .B "tzfile.h"\c ): .sp .nf .in +.5i .ta .5i +\w'unsigned short 'u struct tzhead { char tzh_reserved[14]; unsigned short tzh_timecnt; unsigned short tzh_typecnt; unsigned short tzh_charcnt; }; .in -.5i .fi .PP The .B tzh_reserved element is currently unused. The .B tzh_timecnt element gives the number of "transition times" for which data is stored in the file; the .B tzh_typecnt (which must not be zero) element gives the number of "local time types" for which data is stored in the file; and the .B tzh_charcnt element gives the number of characters of "time zone abbreviation strings" stored in the file. .PP The above header is followed by .B tzh_timecnt values of type .BR long , sorted in ascending order; each is used as a transition time (as returned by .IR time (2)) at which the rules for computing local time change. Next come .B tzh_timecnt values of type .BR "unsigned char" ; each one tells which of the different types of "local time" described in the file is associated with the same-indexed transition time. These values serve as indices into an array of .B ttinfo structures that appears next in the file; these structures are defined as follows: .in +.5i .sp .nf .ta .5i +\w'unsigned short 'u struct ttinfo { long tt_gmtoff; int tt_isdst; unsigned int tt_abbrind; }; .in -.5i .fi .sp In each structure, .B tt_gmtoff gives the number of seconds to be added to GMT, .B tt_isdst tells whether .B tm_isdst should be set by .IR newlocaltime (3), and .B tt_abbrind serves as an index into the array of time zone abbreviation chaaracters that follow the .B ttinfo structure(s) in the file. .PP .I Newlocaltime uses the first .B ttinfo structure in the file if either .B tzh_timecnt is zero or .IR newlocaltime 's argument is less than the first transition time recorded in the file. .SH SEE ALSO settz(3) .. @(#)tzfile.5 2.1 SHAR_EOF if test 2100 -ne "`wc -c < 'tzfile.5'`" then echo shar: error transmitting "'tzfile.5'" '(should have been 2100 characters)' fi fi echo shar: extracting "'tzcomp.8'" '(5479 characters)' if test -f 'tzcomp.8' then echo shar: will not over-write existing file "'tzcomp.8'" else cat << \SHAR_EOF > 'tzcomp.8' .TH TZCOMP 8 .SH NAME tzcomp \- time zone compiler .SH SYNOPSIS .B tzcomp [ .B \-d directory ] [ .B \-l localtime ] [ filename ... ] .SH DESCRIPTION .I Tzcomp reads text from the file(s) named on the command line and creates the time conversion information files specified in this input. If a .I filename is .BR ` - ', the standard input is read. .PP These options are available: .TP .BI "\-d " directory Create time conversion information files in the named directory rather than in the standard directory named below. .TP .BI "\-l " localtime Use the given time zone as local time. .PP Input lines are made up of fields. Fields are separated from one another by any number of white space characters. Leading and trailing white space on input lines is ignored. An unquoted sharp character (#) in the input introduces a comment which extends to the end of the line the sharp character appears on. White space characters and sharp characters may be enclosed in double quotes (") if they're to be used as part of a field. Any line which is blank (after comment stripping) is ignored. Non-blank lines are expected to be of one of three types: rule lines, zone lines, and link lines. .PP A rule line has the form .nf .B .ti +.5i .ta \w'Rule 'u +\w'MostNA 'u +\w'FROM 'u +\w'1973 'u +\w'TYPE 'u +\w'Apr 'u +\w'lastSun 'u +\w'2:00 'u +\w'SAVE 'u .sp Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S .sp For example: .ti +.5i .sp Rule MostNA 1969 1973 - Apr lastSun 2:00 1:00 D .sp .fi The fields that make up a rule line are: .TP .B NAME Gives the (arbitrary) name of the set of rules this rule is part of. .TP .B FROM Gives the first year in which the rule applies. .TP .B TO Gives the last year in which the rule applies. The word .RB ` only ' may be used to repeat the value of the .B FROM field. .TP .B TYPE Gives the type of year in which the year applies. If .B TYPE is .B "-" then the rule applies in all years between .B FROM and .B TO inclusive. If .B TYPE is something else, then the command .B .ti +.5i years from to type .br is executed with arguments .IR from , .IR to , and .I type taken from the rule line; the rule applies only in those years printed by the .I years command. The distributed .I years command is a shell script that handles year types .B uspres (United States presidential election years) and .B nonpres (all other years); other year types may be added by changing the script. .TP .B IN Names the month in which the rule takes effect. Month names may be abbreviated. .TP .B ON Gives the day on which the rule takes effect. Recognized forms include: .nf .in +.5i .sp .ta \w'lastSun 'u 5 the fifth of the month lastSun the last Sunday in the month lastMon the last Monday in the month Sun>=8 first Sunday on or after the eighth Sun<=25 last Sunday on or before the 25th .fi .in -.5i .sp Names of days of the week may be abbreviated or spelled out in full. Note that there must be no spaces within the .B ON field. .TP .B AT Gives the time of day at which the rule takes affect. Recognized forms include: .nf .in +.5i .sp .ta \w'1:28:13 'u 2 time in hours 2:00 time in hours and minutes 15:00 24-hour format time (for times after noon) 1:28:14 time in hours, minutes, and seconds .fi .in -.5i .sp Any of these forms may be followed by the letter 'w' if the given time is local "wall clock" time or 's' if the given time is local "standard" time; in the absence of 'w' or 's', wall clock time is assumed. .TP .B SAVE Gives the amount of time to be added to local standard time when the rule is in effect (although, of course, the 'w' and 's' suffixes are not used). This field has the same format as the .B AT field. .TP .B LETTER/S Gives the "variable part" (for example, the 'S' or 'D' in "EST" or "EDT") of time zone abbreviations to be used when this rule is in effect. If this field is .B "-", the variable part is null. .PP A zone line has the form .sp .nf .ti +.5i .ta \w'Zone 'u +\w'Eastern 'u +\w'GMTOFF 'u +\w'MostNA 'u Zone NAME GMTOFF RULES FORMAT .sp For example: .sp .ti +.5i Zone Eastern -5:00 MostNA E%sT .sp .fi The fields that make up a zone line are: .TP .B NAME The name of the time zone. This is the name used in creating the time conversion information file for the zone. .TP .B GMTOFF The amount of time to add to GMT to get standard time in this zone. This field has the same format as the .B AT and .B SAVE fields of rule lines; begin the field with a minus sign if time must be subtracted from GMT. .TP .B RULES The name of the rule(s) that apply in the time zone. If this field is .B "-" then standard time always applies in the time zone. .TP .B FORMAT The format for time zone abbreviations in this time zone. The pair of characters .B "%s" is used to show where the "variable part" of the time zone abbreviation goes. .PP A link line has the form .sp .nf .ti +.5i .ta \w'Link 'u +\w'LINK-FROM 'u Link LINK-FROM LINK-TO .sp For example: .sp .ti +.5i Link Eastern EST5EDT .sp .fi The .B LINK-FROM field should appear as the .B NAME field in some zone line; the .B LINK-TO field is used as an alternate name for that zone. .PP Lines may appear in any order in the input. .SH NOTE For areas with more than two types of local time, you may get to use local standard time in "AT" field of the earliest transition time's rule to ensure that the earliest transition time recorded in the compiled file is correct. .SH FILES /etc/tzdir standard directory used for created files .SH "SEE ALSO" settz(3), tzfile(5) .. @(#)tzcomp.8 2.1 SHAR_EOF if test 5479 -ne "`wc -c < 'tzcomp.8'`" then echo shar: error transmitting "'tzcomp.8'" '(should have been 5479 characters)' fi fi echo shar: extracting "'tzfile.h'" '(1409 characters)' if test -f 'tzfile.h' then echo shar: will not over-write existing file "'tzfile.h'" else cat << \SHAR_EOF > 'tzfile.h' /* @(#)tzfile.h 2.1 */ /* ** Information about time zone files. */ #ifndef TZDIR #define TZDIR "/etc/tzdir" /* Time zone object file directory */ #endif #ifndef TZDEFAULT #define TZDEFAULT "localtime" #endif struct ttinfo { /* time type information */ long tt_gmtoff; /* GMT offset in seconds */ int tt_isdst; /* used to set tm_isdst */ unsigned int tt_abbrind; /* abbreviation list index */ }; /* ** Each file begins with. . . */ struct tzhead { char tzh_reserved[14]; /* reserved for future use */ unsigned short tzh_timecnt; /* number of transition times */ unsigned short tzh_typecnt; /* number of local time types */ unsigned short tzh_charcnt; /* number of abbr. chars */ }; /* ** . . .followed by. . . ** ** tzh_timecnt (long)s transition times as returned by time(2) ** tzh_timecnt (unsigned char)s types of local time starting at above ** tzh_typecnt (struct ttinfo)s information for each time type ** tzh_charcnt (char)s '\0'-terminated zone abbreviaton strings */ /* ** In the current implementation, "settz()" refuses to deal with files that ** exceed any of the limits below. */ #ifndef TZ_MAX_TIMES #define TZ_MAX_TIMES 170 /* Maximum number of transition times */ #endif #ifndef TZ_MAX_TYPES #define TZ_MAX_TYPES 10 /* Maximum number of local time types */ #endif #ifndef TZ_MAX_CHARS #define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ #endif SHAR_EOF if test 1409 -ne "`wc -c < 'tzfile.h'`" then echo shar: error transmitting "'tzfile.h'" '(should have been 1409 characters)' fi fi echo shar: extracting "'tzcomp.c'" '(24558 characters)' if test -f 'tzcomp.c' then echo shar: will not over-write existing file "'tzcomp.c'" else cat << \SHAR_EOF > 'tzcomp.c' # #include "stdio.h" #ifdef OBJECTID static char sccsid[] = "@(#)tzcomp.c 2.1"; #endif #include "tzfile.h" #include "ctype.h" #ifndef alloc_t #define alloc_t unsigned #endif #ifndef MAL #define MAL NULL #endif #ifndef BUFSIZ #define BUFSIZ 1024 #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #ifdef lint #define scheck(string, format) (format) #endif #ifndef lint extern char * scheck(); #endif extern char * calloc(); extern char * malloc(); extern char * optarg; extern int optind; extern FILE * popen(); extern char * realloc(); extern char * sprintf(); extern char * strcat(); extern char * strchr(); extern char * strcpy(); static int errors; static char * filename; static char ** getfields(); static int linenum; static char * progname; static long rpytime(); static long tadd(); #define SECS_PER_MIN 60L #define MINS_PER_HOUR 60L #define HOURS_PER_DAY 24L #define DAYS_PER_YEAR 365L /* Except in leap years */ #define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR) #define SECS_PER_DAY (SECS_PER_HOUR * HOURS_PER_DAY) #define SECS_PER_YEAR (SECS_PER_DAY * DAYS_PER_YEAR) #define EPOCH_YEAR 1970 #define EPOCH_WDAY TM_THURSDAY /* ** Values a la localtime(3) */ #define TM_JANUARY 0 #define TM_FEBRUARY 1 #define TM_MARCH 2 #define TM_APRIL 3 #define TM_MAY 4 #define TM_JUNE 5 #define TM_JULY 6 #define TM_AUGUST 7 #define TM_SEPTEMBER 8 #define TM_OCTOBER 9 #define TM_NOVEMBER 10 #define TM_DECEMBER 11 #define TM_SUNDAY 0 #define TM_MONDAY 1 #define TM_TUESDAY 2 #define TM_WEDNESDAY 3 #define TM_THURSDAY 4 #define TM_FRIDAY 5 #define TM_SATURDAY 6 /* ** Line codes. */ #define LC_RULE 0 #define LC_ZONE 1 #define LC_LINK 2 /* ** Which fields are which on a Zone line. */ #define ZF_NAME 1 #define ZF_GMTOFF 2 #define ZF_RULE 3 #define ZF_FORMAT 4 #define ZONE_FIELDS 5 /* ** Which files are which on a Rule line. */ #define RF_NAME 1 #define RF_LOYEAR 2 #define RF_HIYEAR 3 #define RF_COMMAND 4 #define RF_MONTH 5 #define RF_DAY 6 #define RF_TOD 7 #define RF_STDOFF 8 #define RF_ABBRVAR 9 #define RULE_FIELDS 10 /* ** Which fields are which on a Link line. */ #define LF_FROM 1 #define LF_TO 2 #define LINK_FIELDS 3 struct rule { char * r_filename; int r_linenum; char * r_name; long r_loyear; /* for example, 1986 */ long r_hiyear; /* for example, 1986 */ char * r_yrtype; long r_month; /* 0..11 */ int r_dycode; /* see below */ long r_dayofmonth; long r_wday; long r_tod; /* time from midnight */ int r_todisstd; /* above is standard time if TRUE */ /* above is wall clock time if FALSE */ long r_stdoff; /* offset from standard time */ char * r_abbrvar; /* variable part of time zone abbreviation */ int r_type; /* used when creating output files */ }; /* ** r_dycode r_dayofmonth r_wday */ #define DC_DOM 0 /* 1..31 */ /* unused */ #define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */ #define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */ static struct rule * rules; static int nrules; /* number of rules */ struct zone { char * z_filename; int z_linenum; char * z_name; long z_gmtoff; char * z_rule; char * z_format; struct rule * z_rules; int z_nrules; }; static struct zone * zones; static int nzones; /* number of zones */ struct link { char * l_filename; int l_linenum; char * l_from; char * l_to; }; static struct link * links; static int nlinks; struct lookup { char * l_word; long l_value; }; static struct lookup * byword(); static struct lookup line_codes[] = { "Rule", LC_RULE, "Zone", LC_ZONE, "Link", LC_LINK, NULL, 0 }; static struct lookup mon_names[] = { "January", TM_JANUARY, "February", TM_FEBRUARY, "March", TM_MARCH, "April", TM_APRIL, "May", TM_MAY, "June", TM_JUNE, "July", TM_JULY, "August", TM_AUGUST, "September", TM_SEPTEMBER, "October", TM_OCTOBER, "November", TM_NOVEMBER, "December", TM_DECEMBER, NULL, 0 }; static struct lookup wday_names[] = { "Sunday", TM_SUNDAY, "Monday", TM_MONDAY, "Tuesday", TM_TUESDAY, "Wednesday", TM_WEDNESDAY, "Thursday", TM_THURSDAY, "Friday", TM_FRIDAY, "Saturday", TM_SATURDAY, NULL, 0 }; static struct lookup lasts[] = { "last-Sunday", TM_SUNDAY, "last-Monday", TM_MONDAY, "last-Tuesday", TM_TUESDAY, "last-Wednesday", TM_WEDNESDAY, "last-Thursday", TM_THURSDAY, "last-Friday", TM_FRIDAY, "last-Saturday", TM_SATURDAY, NULL, 0 }; static long mon_lengths[] = { /* ". . .knuckles are 31. . ." */ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static struct tzhead h; static long ats[TZ_MAX_TIMES]; static unsigned char types[TZ_MAX_TIMES]; static struct ttinfo ttis[TZ_MAX_TYPES]; static char chars[TZ_MAX_CHARS]; /* ** Exits. */ static tameexit() { exit(0); } static wild2exit(part1, part2) char * part1; char * part2; { register char * between; if (part1 == NULL) part1 = ""; if (part2 == NULL) part2 = ""; between = (*part1 == '\0' || *part2 == '\0') ? "" : " "; (void) fprintf(stderr, "%s: wild %s%s%s\n", progname, part1, between, part2); exit(1); } static wildexit(string) char * string; { wild2exit(string, (char *) NULL); } static wildrexit(string) char * string; { wild2exit("result from", string); } /* ** Memory allocation. */ static char * emalloc(size) { register char * cp; if ((cp = malloc((alloc_t) size)) == NULL || cp == MAL) wildrexit("malloc"); return cp; } static char * erealloc(ptr, size) char * ptr; { register char * cp; if ((cp = realloc(ptr, (alloc_t) size)) == NULL) wildrexit("realloc"); return cp; } static char * eallocpy(old) char * old; { register char * new; if (old == NULL) old = ""; new = emalloc(strlen(old) + 1); (void) strcpy(new, old); return new; } static usage() { (void) fprintf(stderr, "%s: usage is %s [ -l localtime ] [ -d directory ] [ filename ... ]\n", progname, progname); exit(1); } static char * localtime = NULL; static char * directory = NULL; main(argc, argv) int argc; char * argv[]; { register int i; register int c; progname = argv[0]; while ((c = getopt(argc, argv, "d:l:")) != EOF) switch (c) { default: usage(); case 'd': if (directory == NULL) directory = optarg; else wildexit("multiple command line -d's"); break; case 'l': if (localtime == NULL) localtime = optarg; else wildexit("multiple command line -l's"); } if (directory == NULL) directory = TZDIR; if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) usage(); /* usage message by request */ zones = (struct zone *) emalloc(0); rules = (struct rule *) emalloc(0); links = (struct link *) emalloc(0); for (i = optind; i < argc; ++i) infile(argv[i]); if (errors) wildexit("input data"); associate(); for (i = 0; i < nzones; ++i) outzone(&zones[i]); /* ** We'll take the easy way out on this last part. */ if (chdir(directory) != 0) wild2exit("result from chdir to", directory); for (i = 0; i < nlinks; ++i) { (void) unlink(links[i].l_to); if (link(links[i].l_from, links[i].l_to) != 0) wild2exit("result creating", links[i].l_to); } if (localtime != NULL) { (void) unlink(TZDEFAULT); if (link(localtime, TZDEFAULT) != 0) wild2exit("result creating", TZDEFAULT); } tameexit(); } /* ** Associate sets of rules with zones. */ /* ** Sort by rule name, and by magnitude of standard time offset for rules of ** the same name. The second sort gets standard time entries to the start ** of the dsinfo table (and we want a standard time entry at the start of ** the table, since the first entry gets used for times not covered by the ** rules). */ static rcomp(cp1, cp2) char * cp1; char * cp2; { register struct rule * rp1; register struct rule * rp2; register long l1, l2; register int diff; rp1 = (struct rule *) cp1; rp2 = (struct rule *) cp2; if ((diff = strcmp(rp1->r_name, rp2->r_name)) != 0) return diff; if ((l1 = rp1->r_stdoff) < 0) l1 = -l1; if ((l2 = rp2->r_stdoff) < 0) l2 = -l2; if (l1 > l2) return 1; else if (l1 < l2) return -1; else return 0; } static associate() { register struct zone * zp; register struct rule * rp; register int base, out; register int i; if (nrules != 0) (void) qsort((char *) rules, nrules, sizeof *rules, rcomp); base = 0; for (i = 0; i < nzones; ++i) { zp = &zones[i]; zp->z_rules = NULL; zp->z_nrules = 0; } while (base < nrules) { rp = &rules[base]; for (out = base + 1; out < nrules; ++out) if (strcmp(rp->r_name, rules[out].r_name) != 0) break; for (i = 0; i < nzones; ++i) { zp = &zones[i]; if (strcmp(zp->z_rule, rp->r_name) != 0) continue; zp->z_rules = rp; zp->z_nrules = out - base; } base = out; } for (i = 0; i < nzones; ++i) { zp = &zones[i]; if (*zp->z_rule != '\0' && zp->z_nrules == 0) { filename = zp->z_filename; linenum = zp->z_linenum; error("unruly zone"); } } if (errors) wildexit("unruly zone(s)"); } static error(string) char * string; { (void) fprintf(stderr, "%s: file \"%s\", line %d: wild %s\n", progname, filename, linenum, string); ++errors; } static infile(name) char * name; { register FILE * fp; register char ** fields; register char * cp; register struct lookup * lp; register int nfields; char buf[BUFSIZ]; if (strcmp(name, "-") == 0) { name = "standard input"; fp = stdin; } else if ((fp = fopen(name, "r")) == NULL) wild2exit("result opening", name); filename = eallocpy(name); for (linenum = 1; ; ++linenum) { if (fgets(buf, sizeof buf, fp) != buf) break; cp = strchr(buf, '\n'); if (cp == NULL) { error("long line"); wildexit("input data"); } *cp = '\0'; if ((fields = getfields(buf)) == NULL) wildrexit("getfields"); nfields = 0; while (fields[nfields] != NULL) { if (ciequal(fields[nfields], "-")) fields[nfields] = ""; ++nfields; } if (nfields > 0) /* non-blank line */ if ((lp = byword(fields[0], line_codes)) == NULL) error("input line of unknown type"); else switch (lp->l_value) { case LC_RULE: inrule(fields, nfields); break; case LC_ZONE: inzone(fields, nfields); break; case LC_LINK: inlink(fields, nfields); break; default: /* "cannot happen" */ wildrexit("lookup"); } free((char *) fields); } if (ferror(fp)) wild2exit("result reading", filename); if (fp != stdin && fclose(fp)) wild2exit("result closing", filename); } /* ** Convert a string of one of the forms ** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss ** into a number of seconds. ** Call error with errstring and return zero on errors. */ static long getoff(string, errstring) char * string; char * errstring; { long hh, mm, ss, sign; if (*string == '-') { sign = -1; ++string; } else sign = 1; if (sscanf(string, scheck(string, "%ld"), &hh) == 1) mm = ss = 0; else if (sscanf(string, scheck(string, "%ld:%ld"), &hh, &mm) == 2) ss = 0; else if (sscanf(string, scheck(string, "%ld:%ld:%ld"), &hh, &mm, &ss) != 3) { error(errstring); return 0; } if (hh < 0 || hh >= HOURS_PER_DAY || mm < 0 || mm >= MINS_PER_HOUR || ss < 0 || ss >= SECS_PER_MIN) { error(errstring); return 0; } return (long) sign * (((hh * MINS_PER_HOUR) + mm) * SECS_PER_MIN + ss); } static inrule(fields, nfields) char ** fields; { register struct lookup * lp; register char * cp; struct rule r; if (nfields != RULE_FIELDS) { error("number of fields on Rule line"); return; } r.r_filename = filename; r.r_linenum = linenum; if ((lp = byword(fields[RF_MONTH], mon_names)) == NULL) { error("month name"); return; } r.r_month = lp->l_value; r.r_todisstd = FALSE; cp = fields[RF_TOD]; if (strlen(cp) > 0) { cp += strlen(cp) - 1; switch (lowerit(*cp)) { case 's': r.r_todisstd = TRUE; *cp = '\0'; break; case 'w': r.r_todisstd = FALSE; *cp = '\0'; break; } } r.r_tod = getoff(fields[RF_TOD], "time of day"); r.r_stdoff = getoff(fields[RF_STDOFF], "Standard Time offset"); /* ** Year work. */ cp = fields[RF_LOYEAR]; if (sscanf(cp, scheck(cp, "%ld"), &r.r_loyear) != 1 || r.r_loyear <= 0) { error("low year"); return; } cp = fields[RF_HIYEAR]; if (*cp == '\0' || ciequal(cp, "only")) r.r_hiyear = r.r_loyear; else if (sscanf(cp, scheck(cp, "%ld"), &r.r_hiyear) != 1 || r.r_hiyear <= 0) { error("high year"); return; } if (r.r_loyear > r.r_hiyear) { error("low year (greater than high year)"); return; } if (*fields[RF_COMMAND] == '\0') r.r_yrtype = NULL; else { if (r.r_loyear == r.r_hiyear) { error("typed single year"); return; } r.r_yrtype = eallocpy(fields[RF_COMMAND]); } /* ** Day work. ** Accept things such as: ** 1 ** last-Sunday ** Sun<=20 ** Sun>=7 */ cp = fields[RF_DAY]; if ((lp = byword(cp, lasts)) != NULL) { r.r_dycode = DC_DOWLEQ; r.r_wday = lp->l_value; r.r_dayofmonth = mon_lengths[r.r_month]; if (r.r_month == TM_FEBRUARY) ++r.r_dayofmonth; } else { if ((cp = strchr(fields[RF_DAY], '<')) != 0) r.r_dycode = DC_DOWLEQ; else if ((cp = strchr(fields[RF_DAY], '>')) != 0) r.r_dycode = DC_DOWGEQ; else { cp = fields[RF_DAY]; r.r_dycode = DC_DOM; } if (r.r_dycode != DC_DOM) { *cp++ = 0; if (*cp++ != '=') { error("day of month"); return; } if ((lp = byword(fields[RF_DAY], wday_names)) == NULL) { error("weekday name"); return; } r.r_wday = lp->l_value; } if (sscanf(cp, scheck(cp, "%ld"), &r.r_dayofmonth) != 1 || r.r_dayofmonth <= 0 || (r.r_dayofmonth > mon_lengths[r.r_month] && r.r_month != TM_FEBRUARY && r.r_dayofmonth != 29)) { error("day of month"); return; } } if (*fields[RF_NAME] == '\0') { error("nameless rule"); return; } r.r_name = eallocpy(fields[RF_NAME]); r.r_abbrvar = eallocpy(fields[RF_ABBRVAR]); rules = (struct rule *) erealloc((char *) rules, (nrules + 1) * sizeof *rules); rules[nrules++] = r; } static inzone(fields, nfields) char ** fields; { register char * cp; register int i; struct zone z; char buf[132]; if (nfields != ZONE_FIELDS) { error("number of fields on Zone line"); return; } z.z_filename = filename; z.z_linenum = linenum; for (i = 0; i < nzones; ++i) if (strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) { (void) sprintf(buf, "duplicate zone name %s (file \"%s\", line %d)", fields[ZF_NAME], zones[i].z_filename, zones[i].z_linenum); error(buf); return; } z.z_gmtoff = getoff(fields[ZF_GMTOFF], "GMT offset"); if ((cp = strchr(fields[ZF_FORMAT], '%')) != 0) { if (*++cp != 's' || strchr(cp, '%') != 0) { error("format"); return; } } z.z_name = eallocpy(fields[ZF_NAME]); z.z_rule = eallocpy(fields[ZF_RULE]); z.z_format = eallocpy(fields[ZF_FORMAT]); zones = (struct zone *) erealloc((char *) zones, (nzones + 1) * sizeof *zones); zones[nzones++] = z; } static inlink(fields, nfields) char ** fields; { struct link l; if (nfields != LINK_FIELDS) { error("number of fields on Link line"); return; } if (*fields[LF_FROM] == '\0') { error("blank FROM field on Link line"); return; } if (*fields[LF_TO] == '\0') { error("blank TO field on Link line"); return; } l.l_filename = filename; l.l_linenum = linenum; l.l_from = eallocpy(fields[LF_FROM]); l.l_to = eallocpy(fields[LF_TO]); links = (struct link *) erealloc((char *) links, (nlinks + 1) * sizeof *links); links[nlinks++] = l; } static writezone(name) char * name; { register FILE * fp; register int i; char fullname[BUFSIZ]; if (strlen(directory) + 1 + strlen(name) >= BUFSIZ) wild2exit("long directory/file", filename); (void) sprintf(fullname, "%s/%s", directory, name); if ((fp = fopen(fullname, "w")) == NULL) { mkdirs(fullname); if ((fp = fopen(fullname, "w")) == NULL) wild2exit("result creating", fullname); } if (fwrite((char *) &h, sizeof h, 1, fp) != 1) goto wreck; if ((i = h.tzh_timecnt) != 0) { if (fwrite((char *) ats, sizeof ats[0], i, fp) != i) goto wreck; if (fwrite((char *) types, sizeof types[0], i, fp) != i) goto wreck; } if ((i = h.tzh_typecnt) != 0) if (fwrite((char *) ttis, sizeof ttis[0], i, fp) != i) goto wreck; if ((i = h.tzh_charcnt) != 0) if (fwrite(chars, sizeof chars[0], i, fp) != i) goto wreck; if (fclose(fp)) wild2exit("result closing", fullname); return; wreck: wild2exit("result writing", fullname); } struct temp { long t_time; struct rule * t_rp; }; static struct temp temps[TZ_MAX_TIMES]; static int ntemps; static tcomp(cp1, cp2) char * cp1; char * cp2; { register struct temp * tp1; register struct temp * tp2; register char * cp; register long diff; tp1 = (struct temp *) cp1; tp2 = (struct temp *) cp2; if (tp1->t_time > 0 && tp2->t_time <= 0) return 1; if (tp1->t_time <= 0 && tp2->t_time > 0) return -1; if ((diff = tp1->t_time - tp2->t_time) > 0) return 1; else if (diff < 0) return -1; /* ** Oops! */ if (tp1->t_rp->r_type == tp2->t_rp->r_type) cp = "duplicate rule?!"; else cp = "inconsistent rules?!"; filename = tp1->t_rp->r_filename; linenum = tp1->t_rp->r_linenum; error(cp); filename = tp2->t_rp->r_filename; linenum = tp2->t_rp->r_linenum; error(cp); wildexit(cp); /*NOTREACHED*/ } static addrule(rp, y) register struct rule * rp; long y; { if (ntemps >= TZ_MAX_TIMES) { error("too many transitions?!"); wildexit("large number of transitions"); } temps[ntemps].t_time = rpytime(rp, y); temps[ntemps].t_rp = rp; ++ntemps; } static outzone(zp) register struct zone * zp; { register struct rule * rp; register int i, j; register long y; register long gmtoff; char buf[BUFSIZ]; h.tzh_timecnt = 0; h.tzh_typecnt = 0; h.tzh_charcnt = 0; if (zp->z_nrules == 0) { /* Piece of cake! */ h.tzh_timecnt = 0; h.tzh_typecnt = 1; ttis[0].tt_gmtoff = zp->z_gmtoff; ttis[0].tt_isdst = 0; ttis[0].tt_abbrind = 0; newabbr(zp->z_format); writezone(zp->z_name); return; } /* ** See what the different local time types are. ** Plug the indices into the rules. */ for (i = 0; i < zp->z_nrules; ++i) { rp = &zp->z_rules[i]; (void) sprintf(buf, zp->z_format, rp->r_abbrvar); gmtoff = tadd(zp->z_gmtoff, rp->r_stdoff); for (j = 0; j < h.tzh_typecnt; ++j) { if (gmtoff == ttis[j].tt_gmtoff && strcmp(buf, &chars[ttis[j].tt_abbrind]) == 0) break; } if (j >= h.tzh_typecnt) { if (h.tzh_typecnt >= TZ_MAX_TYPES) { filename = zp->z_filename; linenum = zp->z_linenum; error("large number of local time types"); wildexit("input data"); } ttis[j].tt_gmtoff = gmtoff; ttis[j].tt_isdst = rp->r_stdoff != 0; ttis[j].tt_abbrind = h.tzh_charcnt; newabbr(buf); ++h.tzh_typecnt; } rp->r_type = j; } /* ** Now. . .finally. . .generate some useable data! */ ntemps = 0; for (i = 0; i < zp->z_nrules; ++i) { rp = &zp->z_rules[i]; filename = rp->r_filename; linenum = rp->r_linenum; if (rp->r_yrtype != NULL && *rp->r_yrtype != '\0') hard(rp); else for (y = rp->r_loyear; y <= rp->r_hiyear; ++y) addrule(rp, y); } h.tzh_timecnt = ntemps; (void) qsort((char *) temps, ntemps, sizeof *temps, tcomp); for (i = 0; i < ntemps; ++i) { rp = temps[i].t_rp; filename = rp->r_filename; linenum = rp->r_linenum; types[i] = rp->r_type; ats[i] = tadd(temps[i].t_time, -zp->z_gmtoff); if (!rp->r_todisstd) { /* ** Credit to munnari!kre for pointing out the need for ** the following. (This can still mess up on the ** earliest rule; who's got the solution? It can also ** mess up if a time switch results in a day switch; ** this is left as an exercise for the reader.) */ if (i == 0) { /* ** Kludge--not guaranteed to work. */ if (ntemps > 1) ats[0] = tadd(ats[0], -temps[1].t_rp->r_stdoff); } else ats[i] = tadd(ats[i], -temps[i - 1].t_rp->r_stdoff); } } writezone(zp->z_name); return; } static hard(rp) register struct rule * rp; { register FILE * fp; register int n; long y; char buf[BUFSIZ]; char command[BUFSIZ]; (void) sprintf(command, "years %ld %ld %s", rp->r_loyear, rp->r_hiyear, rp->r_yrtype); if ((fp = popen(command, "r")) == NULL) wild2exit("result opening pipe to", command); for (n = 0; fgets(buf, sizeof buf, fp) == buf; ++n) { if (strchr(buf, '\n') == 0) wildrexit(command); *strchr(buf, '\n') = '\0'; if (sscanf(buf, scheck(buf, "%ld"), &y) != 1) wildrexit(command); if (y < rp->r_loyear || y > rp->r_hiyear) wildrexit(command); addrule(rp, y); } if (ferror(fp)) wild2exit("result reading from", command); if (pclose(fp)) wild2exit("result closing pipe to", command); if (n == 0) { error("no year in range matches type"); wildexit("input data"); } } static lowerit(a) { return (isascii(a) && isupper(a)) ? tolower(a) : a; } static ciequal(ap, bp) /* case-insensitive equality */ register char * ap; register char * bp; { while (lowerit(*ap) == lowerit(*bp++)) if (*ap++ == '\0') return TRUE; return FALSE; } static isabbr(abbr, word) register char * abbr; register char * word; { if (lowerit(*abbr) != lowerit(*word)) return FALSE; ++word; while (*++abbr != '\0') do if (*word == '\0') return FALSE; while (lowerit(*word++) != lowerit(*abbr)); return TRUE; } static struct lookup * byword(word, table) register char * word; register struct lookup * table; { register struct lookup * foundlp; register struct lookup * lp; if (word == NULL || table == NULL) return NULL; foundlp = NULL; for (lp = table; lp->l_word != NULL; ++lp) if (ciequal(word, lp->l_word)) /* "exact" match */ return lp; else if (!isabbr(word, lp->l_word)) continue; else if (foundlp == NULL) foundlp = lp; else return NULL; /* two inexact matches */ return foundlp; } static char ** getfields(cp) register char * cp; { register char * dp; register char ** array; register int nsubs; if (cp == NULL) return NULL; array = (char **) emalloc((strlen(cp) + 1) * sizeof *array); nsubs = 0; for ( ; ; ) { while (isascii(*cp) && isspace(*cp)) ++cp; if (*cp == '\0' || *cp == '#') break; array[nsubs++] = dp = cp; do { if ((*dp = *cp++) != '"') ++dp; else while ((*dp = *cp++) != '"') if (*dp != '\0') ++dp; else error("Odd number of quotation marks"); } while (*cp != '\0' && *cp != '#' && (!isascii(*cp) || !isspace(*cp))); if (isascii(*cp) && isspace(*cp)) ++cp; *dp++ = '\0'; } array[nsubs] = NULL; return array; } static long tadd(t1, t2) long t1; long t2; { register long t; t = t1 + t2; if (t1 > 0 && t2 > 0 && t <= 0 || t1 < 0 && t2 < 0 && t >= 0) { error("time overflow"); wildexit("time overflow"); } return t; } static isleap(y) long y; { return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); } static long rpytime(rp, wantedy) register struct rule * rp; register long wantedy; { register long i, y, wday, t, m; register long dayoff; /* with a nod to Margaret O. */ dayoff = 0; m = TM_JANUARY; y = EPOCH_YEAR; while (wantedy != y) { if (wantedy > y) { i = DAYS_PER_YEAR; if (isleap(y)) ++i; ++y; } else { --y; i = -DAYS_PER_YEAR; if (isleap(y)) --i; } dayoff = tadd(dayoff, i); } while (m != rp->r_month) { i = mon_lengths[m]; if (m == TM_FEBRUARY && isleap(y)) ++i; dayoff = tadd(dayoff, i); ++m; } i = rp->r_dayofmonth; if (m == TM_FEBRUARY && i == 29 && !isleap(y)) { if (rp->r_dycode == DC_DOWLEQ) --i; else { error("use of 2/29 in non leap-year"); for ( ; ; ) wildexit("data"); } } --i; dayoff = tadd(dayoff, i); if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { wday = EPOCH_WDAY; /* ** Don't trust mod of negative numbers. */ if (dayoff >= 0) wday = (wday + dayoff) % 7; else { wday -= ((-dayoff) % 7); if (wday < 0) wday += 7; } while (wday != rp->r_wday) { if (rp->r_dycode == DC_DOWGEQ) i = 1; else i = -1; dayoff = tadd(dayoff, i); wday = (wday + i + 7) % 7; } } t = dayoff * SECS_PER_DAY; /* ** Cheap overflow check. */ if (t / SECS_PER_DAY != dayoff) error("time overflow"); return tadd(t, rp->r_tod); } static newabbr(string) char * string; { register int i; i = strlen(string); if (h.tzh_charcnt + i >= TZ_MAX_CHARS) error("long time zone abbreviations"); (void) strcpy(&chars[h.tzh_charcnt], string); h.tzh_charcnt += i + 1; } static mkdirs(name) char * name; { register char * cp; if ((cp = name) == NULL || *cp == '\0') return; while ((cp = strchr(cp + 1, '/')) != 0) { *cp = '\0'; (void) mkdir(name); *cp = '/'; } } SHAR_EOF if test 24558 -ne "`wc -c < 'tzcomp.c'`" then echo shar: error transmitting "'tzcomp.c'" '(should have been 24558 characters)' fi fi echo shar: extracting "'scheck.c'" '(1141 characters)' if test -f 'scheck.c' then echo shar: will not over-write existing file "'scheck.c'" else cat << \SHAR_EOF > 'scheck.c' # /*LINTLIBRARY*/ #include "stdio.h" #ifdef OBJECTID static char sccsid[] = "@(#)scheck.c 7.9"; #endif #include "ctype.h" #ifndef alloc_t #define alloc_t unsigned #endif #ifndef MAL #define MAL NULL #endif extern char * malloc(); char * scheck(string, format) char * string; char * format; { register char * fbuf; register char * fp; register char * tp; register int c; register char * result; char dummy; result = ""; if (string == NULL || format == NULL) return result; fbuf = malloc((alloc_t) (2 * strlen(format) + 4)); if (fbuf == MAL) return result; fp = format; tp = fbuf; while ((*tp++ = c = *fp++) != '\0') { if (c != '%') continue; if (*fp == '%') { *tp++ = *fp++; continue; } *tp++ = '*'; if (*fp == '*') ++fp; while (isascii(*fp) && isdigit(*fp)) *tp++ = *fp++; if (*fp == 'l' || *fp == 'h') *tp++ = *fp++; else if (*fp == '[') do *tp++ = *fp++; while (*fp != '\0' && *fp != ']'); if ((*tp++ = *fp++) == '\0') break; } *(tp - 1) = '%'; *tp++ = 'c'; *tp++ = '\0'; if (sscanf(string, fbuf, &dummy) != 1) result = format; free(fbuf); return result; } SHAR_EOF if test 1141 -ne "`wc -c < 'scheck.c'`" then echo shar: error transmitting "'scheck.c'" '(should have been 1141 characters)' fi fi echo shar: extracting "'strchr.c'" '(419 characters)' if test -f 'strchr.c' then echo shar: will not over-write existing file "'strchr.c'" else cat << \SHAR_EOF > 'strchr.c' # /*LINTLIBRARY*/ #include "stdio.h" #ifdef OBJECTID static char sccsid[] = "@(#)strchr.c 7.3"; #endif /* ** For the benefit of BSD folks. ** This is written from the manual description, ** so there's no guarantee that it works the same as the "real thing." */ char * strchr(string, c) register char * string; register int c; { do if (*string == c) return string; while (*string++ != '\0'); return NULL; } SHAR_EOF if test 419 -ne "`wc -c < 'strchr.c'`" then echo shar: error transmitting "'strchr.c'" '(should have been 419 characters)' fi fi echo shar: extracting "'mkdir.c'" '(524 characters)' if test -f 'mkdir.c' then echo shar: will not over-write existing file "'mkdir.c'" else cat << \SHAR_EOF > 'mkdir.c' # /*LINTLIBRARY*/ #include "stdio.h" #ifdef OJBECTID static char sccsid[] = "@(#)mkdir.c 7.2"; #endif extern FILE * popen(); mkdir(name) char * name; { register FILE * fp; register int c; register int oops; if ((fp = popen("sh", "w")) == NULL) return -1; (void) fputs("mkdir 2>&- '", fp); if (name != NULL) while ((c = *name++) != '\0') if (c == '\'') (void) fputs("'\\''", fp); else (void) fputc(c, fp); (void) fputs("'\n", fp); oops = ferror(fp); return (pclose(fp) == 0 && !oops) ? 0 : -1; } SHAR_EOF if test 524 -ne "`wc -c < 'mkdir.c'`" then echo shar: error transmitting "'mkdir.c'" '(should have been 524 characters)' fi fi echo shar: extracting "'tzdump.c'" '(2783 characters)' if test -f 'tzdump.c' then echo shar: will not over-write existing file "'tzdump.c'" else cat << \SHAR_EOF > 'tzdump.c' # #include "stdio.h" #ifdef OBJECTID static char sccsid[] = "@(#)tzdump.c 2.1"; #endif #include "time.h" #include "tzfile.h" #ifndef alloc_t #define alloc_t unsigned #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif extern char * asctime(); extern char * calloc(); extern struct tm * gmtime(); extern char * newctime(); extern int optind; extern char * sprintf(); extern long time(); extern char * tz_abbr; static int longest; main(argc, argv) int argc; char * argv[]; { register FILE * fp; register long * tp; register int i, j, c; register int vflag; long now; struct tzhead h; char buf[BUFSIZ]; vflag = 0; while ((c = getopt(argc, argv, "v")) == 'v') vflag = 1; if (c != EOF || optind == argc - 1 && strcmp(argv[optind], "=") == 0) { (void) fprintf(stderr, "%s: usage is %s [ -v ] zonename ...\n", argv[0], argv[0]); exit(1); } (void) time(&now); longest = 0; for (i = optind; i < argc; ++i) if (strlen(argv[i]) > longest) longest = strlen(argv[i]); for (i = optind; i < argc; ++i) { if (settz(argv[i]) != 0) { (void) fprintf(stderr, "%s: wild result from settz(\"%s\")\n", argv[0], argv[i]); exit(1); } show(argv[i], now, FALSE); if (!vflag) continue; if (argv[i][0] == '/') fp = fopen(argv[i], "r"); else { j = strlen(TZDIR) + 1 + strlen(argv[i]) + 1; if (j > sizeof buf) { (void) fprintf(stderr, "%s: wild long timezone name %s\n", argv[0], argv[i]); exit(1); } (void) sprintf(buf, "%s/%s", TZDIR, argv[i]); fp = fopen(buf, "r"); } if (fp == NULL) { (void) fprintf(stderr, "%s: wild result opening %s file\n", argv[0], argv[i]); exit(1); } if (fread((char *) &h, sizeof h, 1, fp) != 1) { (void) fprintf(stderr, "%s: wild result reading %s file\n", argv[0], argv[i]); exit(1); } tp = (long *) calloc((alloc_t) h.tzh_timecnt, sizeof *tp); if (tp == NULL) { (void) fprintf(stderr, "%s: wild result from calloc\n", argv[0]); exit(1); } if (h.tzh_timecnt != 0) if (fread((char *) tp, sizeof *tp, (int) h.tzh_timecnt, fp) != h.tzh_timecnt) { (void) fprintf(stderr, "%s: wild result reading %s file\n", argv[0], argv[i]); exit(1); } if (fclose(fp)) { (void) fprintf(stderr, "%s: wild result closing %s file\n", argv[0], argv[i]); exit(1); } for (j = 0; j < h.tzh_timecnt; ++j) { show(argv[i], tp[j] - 1, TRUE); show(argv[i], tp[j], TRUE); } free((char *) tp); } return 0; } static show(zone, t, v) char * zone; long t; { (void) printf("%-*s ", longest, zone); if (v) (void) printf("%.24s GMT = ", asctime(gmtime(&t))); (void) printf("%.24s", newctime(&t)); if (*tz_abbr != '\0') (void) printf(" %s", tz_abbr); (void) printf("\n"); } SHAR_EOF if test 2783 -ne "`wc -c < 'tzdump.c'`" then echo shar: error transmitting "'tzdump.c'" '(should have been 2783 characters)' fi fi echo shar: extracting "'settz.c'" '(4052 characters)' if test -f 'settz.c' then echo shar: will not over-write existing file "'settz.c'" else cat << \SHAR_EOF > 'settz.c' # /*LINTLIBRARY*/ /* ** Should there be any built-in rules other than GMT? ** In particular, should zones such as "EST5" (abbreviation is always "EST", ** GMT offset is always 5 hours) be built in? */ #include "tzfile.h" #include "time.h" #ifdef OBJECTID static char sccsid[] = "@(#)settz.c 2.1"; #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif extern char * asctime(); extern struct tm * gmtime(); extern char * strcpy(); extern char * strcat(); extern char * getenv(); static struct tzhead h; static long ats[TZ_MAX_TIMES]; static unsigned char types[TZ_MAX_TIMES]; static struct ttinfo ttis[TZ_MAX_TYPES]; static char chars[TZ_MAX_CHARS + 1]; #define TZ_MAX_TOTAL (sizeof h + sizeof ats + sizeof types + \ sizeof ttis + sizeof chars) static char isset; char * tz_abbr; /* set by localtime; available to all */ /* ** Not available west of the Rockies. . . */ static char * memcpy(to, from, n) char * to; char * from; { register int i; for (i = 0; i < n; ++i) to[i] = from[i]; return to; } static tzload(tzname) register char * tzname; { register char * p; register int fid; register int i; register int doaccess; char buf[(TZ_MAX_TOTAL>MAXPATHLEN)?TZ_MAX_TOTAL:MAXPATHLEN]; if (tzname == 0 && (tzname = TZDEFAULT) == 0) return -1; doaccess = tzname[0] == '/'; if (!doaccess) { if ((p = TZDIR) == 0) return -1; if ((strlen(p) + strlen(tzname) + 1) >= sizeof buf) return -1; (void) strcpy(buf, p); (void) strcat(buf, "/"); (void) strcat(buf, tzname); /* ** Set doaccess if '.' (as in "../") shows up in name. */ while (*tzname != '\0') if (*tzname++ == '.') doaccess = TRUE; tzname = buf; } if (doaccess && access(tzname, 4) != 0) return -1; if ((fid = open(tzname, 0)) == -1) return -1; p = buf; i = read(fid, p, sizeof buf); if (close(fid) != 0 || i < sizeof h) return -1; (void) memcpy((char *) &h, p, sizeof h); if (h.tzh_timecnt > TZ_MAX_TIMES || h.tzh_typecnt == 0 || h.tzh_typecnt > TZ_MAX_TYPES || h.tzh_charcnt > TZ_MAX_CHARS) return -1; if (i < sizeof h + h.tzh_timecnt * (sizeof ats[0] + sizeof types[0]) + h.tzh_typecnt * sizeof ttis[0] + h.tzh_charcnt * sizeof chars[0]) return -1; p += sizeof h; if ((i = h.tzh_timecnt) > 0) { (void) memcpy((char *) ats, p, i * sizeof ats[0]); p += i * sizeof ats[0]; (void) memcpy((char *) types, p, i * sizeof types[0]); p += i * sizeof types[0]; } if ((i = h.tzh_typecnt) > 0) { (void) memcpy((char *) ttis, p, i * sizeof ttis[0]); p += i * sizeof ttis[0]; } if ((i = h.tzh_charcnt) > 0) (void) memcpy((char *) chars, p, i * sizeof chars[0]); chars[h.tzh_charcnt] = '\0'; /* ensure '\0'-termination */ for (i = 0; i < h.tzh_timecnt; ++i) if (types[i] >= h.tzh_typecnt) return -1; for (i = 0; i < h.tzh_typecnt; ++i) if (ttis[i].tt_abbrind >= h.tzh_charcnt) return -1; return 0; } /* ** settz("") Use built-in GMT. ** settz((char *) 0) Use TZDEFAULT. ** settz(otherwise) Use otherwise. */ settz(tzname) char * tzname; { register int answer; isset = TRUE; if (tzname != 0 && *tzname == '\0') answer = 0; /* Use built-in GMT */ else { if (tzload(tzname) == 0) return 0; /* ** If we want to try for local time on errors. . . if (tzload((char *) 0) == 0) return -1; */ answer = -1; } h.tzh_timecnt = 0; ttis[0].tt_gmtoff = 0; ttis[0].tt_abbrind = 0; (void) strcpy(chars, "GMT"); return answer; } struct tm * newlocaltime(timep) long * timep; { register struct ttinfo * ttip; register struct tm * tmp; register int i; long t; t = *timep; if (!isset) (void) settz(getenv("TZ")); if (h.tzh_timecnt == 0 || t < ats[0]) i = 0; else { for (i = 1; i < h.tzh_timecnt; ++i) if (t < ats[i]) break; i = types[i - 1]; } ttip = &ttis[i]; t += ttip->tt_gmtoff; tmp = gmtime(&t); tmp->tm_isdst = ttip->tt_isdst; tz_abbr = &chars[ttip->tt_abbrind]; return tmp; } char * newctime(timep) long * timep; { return asctime(newlocaltime(timep)); } SHAR_EOF if test 4052 -ne "`wc -c < 'settz.c'`" then echo shar: error transmitting "'settz.c'" '(should have been 4052 characters)' fi fi echo shar: extracting "'years.sh'" '(382 characters)' if test -f 'years.sh' then echo shar: will not over-write existing file "'years.sh'" else cat << \SHAR_EOF > 'years.sh' : '@(#)years.sh 2.1' : years lo hi type case $# in 3) ;; *) echo "$0: usage is $0 lo hi type" 1>&2 exit 1 ;; esac lo="$1" hi="$2" type="$3" case $type in uspres) check='(y % 4) == 0' ;; nonpres) check='(y % 4) != 0' ;; *) echo "$0: wild year type ($type)" 1>&2 exit 1 ;; esac exec awk " BEGIN { for (y = $lo; y <= $hi; ++y) if ($check) print y; exit } " SHAR_EOF if test 382 -ne "`wc -c < 'years.sh'`" then echo shar: error transmitting "'years.sh'" '(should have been 382 characters)' fi fi echo shar: extracting "'asia'" '(71 characters)' if test -f 'asia' then echo shar: will not over-write existing file "'asia'" else cat << \SHAR_EOF > 'asia' # @(#)asia 2.1 # Zone NAME GMTOFF RULES FORMAT Zone Japan 9:00 - JST SHAR_EOF if test 71 -ne "`wc -c < 'asia'`" then echo shar: error transmitting "'asia'" '(should have been 71 characters)' fi fi echo shar: extracting "'australasia'" '(942 characters)' if test -f 'australasia' then echo shar: will not over-write existing file "'australasia'" else cat << \SHAR_EOF > 'australasia' # @(#)australasia 2.1 # Australian Data (for states with DST), standard rules # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Aus 1971 2037 - Oct lastSun 2:00 1:00 - Rule Aus 1972 only - Feb 27 3:00 0 - Rule Aus 1973 2037 - Mar Sun>=1 3:00 0 - #Australian Data, Vic (and NSW except for a variation in 83? that I[kre] forget) Rule Aus-Vic 1971 2037 - Oct lastSun 2:00 1:00 - Rule Aus-Vic 1972 only - Feb 27 3:00 0 - Rule Aus-Vic 1973 1985 - Mar Sun>=1 3:00 0 - # is this really forever, or just 86?? Rule Aus-Vic 1986 2037 - Mar Sun<=21 3:00 0 - # Australia - something of a turmoil here # Zone NAME GMTOFF RULES FORMAT Zone EST 10:00 Aus-Vic EST # rule change, 1986 Zone Tasmania 10:00 Aus EST # still the standard rules? Zone Queensland 10:00 - EST # Queensland - no DST Zone CST 9:30 Aus CST # still the standard rules? Zone North 9:30 - CST # Northern Territory - no DST Zone WST 8:00 - WST # No DST ever, this is simple... SHAR_EOF if test 942 -ne "`wc -c < 'australasia'`" then echo shar: error transmitting "'australasia'" '(should have been 942 characters)' fi fi echo shar: extracting "'europe'" '(930 characters)' if test -f 'europe' then echo shar: will not over-write existing file "'europe'" else cat << \SHAR_EOF > 'europe' # @(#)europe 2.1 # European data is hearsay... # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule W-Eur 1969 2037 - Mar lastSun 2:00 1:00 " DST" Rule W-Eur 1969 1982 - Oct lastSun 2:00 0 - Rule W-Eur 1983 only - Oct 23 2:00 0 - Rule W-Eur 1984 2037 - Oct lastSun 2:00 0 - Rule M-Eur 1969 2037 - Mar lastSun 2:00 1:00 " DST" Rule M-Eur 1969 1982 - Sep lastSun 2:00 0 - Rule M-Eur 1983 only - Oct 23 2:00 0 - Rule M-Eur 1984 2037 - Sep lastSun 2:00 0 - # Zone NAME GMTOFF RULES FORMAT Zone WET 0 W-Eur WET%s Zone MET 1 M-Eur MET%s # One source shows that Bulgaria, Cyprus, Finland, and Greece observe DST from # the last Sunday in March to the last Sunday in September in 1986. # Did any/all of them have Middle Europe's 1983 wobble? # # The source shows Romania changing a day later than everybody else; # since I don't think they're allowed to have UNIX(tm) systems, we'll skip # them for now. Zone EET 2 M-Eur EET%s SHAR_EOF if test 930 -ne "`wc -c < 'europe'`" then echo shar: error transmitting "'europe'" '(should have been 930 characters)' fi fi echo shar: extracting "'etcetera'" '(1208 characters)' if test -f 'etcetera' then echo shar: will not over-write existing file "'etcetera'" else cat << \SHAR_EOF > 'etcetera' # @(#)etcetera 2.1 # # A settz("") uses the code's built-in GMT without going to disk to get # the information. Still, we want things to work if somebody does a # settz("GMT"), so. . . # Zone GMT 0 - GMT # # Names for zones that might exist, just so people can set a timezone # that's right for their area, even if it doesn't have a name or dst rules # (half hour zones are too much to bother with -- when someone asks!) # Zone GMT-12 -12 - GMT-1200 Zone GMT-11 -11 - GMT-1100 Zone GMT-10 -10 - GMT-1000 Zone GMT-9 -9 - GMT-0900 Zone GMT-8 -8 - GMT-0800 Zone GMT-7 -7 - GMT-0700 Zone GMT-6 -6 - GMT-0600 Zone GMT-5 -5 - GMT-0500 Zone GMT-4 -4 - GMT-0400 Zone GMT-3 -3 - GMT-0300 Zone GMT-2 -2 - GMT-0200 Zone GMT-1 -1 - GMT-0100 Zone GMT+1 1 - GMT+0100 Zone GMT+2 2 - GMT+0200 Zone GMT+3 3 - GMT+0300 Zone GMT+4 4 - GMT+0400 Zone GMT+5 5 - GMT+0500 Zone GMT+6 6 - GMT+0600 Zone GMT+7 7 - GMT+0700 Zone GMT+8 8 - GMT+0800 Zone GMT+9 9 - GMT+0900 Zone GMT+10 10 - GMT+1000 Zone GMT+11 11 - GMT+1100 Zone GMT+12 12 - GMT+1200 Zone GMT+13 13 - GMT+1300 # GMT+12 with DST Link GMT UTC # one of these two is right, but which? Link GMT UCT Link GMT Universal Link GMT Greenwich SHAR_EOF if test 1208 -ne "`wc -c < 'etcetera'`" then echo shar: error transmitting "'etcetera'" '(should have been 1208 characters)' fi fi echo shar: extracting "'northamerica'" '(3139 characters)' if test -f 'northamerica' then echo shar: will not over-write existing file "'northamerica'" else cat << \SHAR_EOF > 'northamerica' # @(#)northamerica 2.1 # Bob Devine has written that ". . .your table is wrong for MostNA in 1974. # The correct ending date is 10/27 not 11/24." Yet on a 4.1bsd VAX/11-750 # system, compiling and executing the program # # #include "time.h" # # long l = 152592000; # # main() { # struct tm * tmp; # # tmp = localtime(&l); # printf("%s", asctime(tmp)); # printf("isdst: %d\n", tmp->tm_isdst); # } # # results in the output # # Fri Nov 1 22:40:00 1974 # isdst: 1 # # For now we'll stay with 4.1bsd's version. # # Note also this from munnari!kre: # "I recall also being told by someone once that Canada didn't have # the DST variations in 74/75 that the US did, but I am not nearly # sure enough of this to add anything." # Before the Uniform Time Act of 1966 took effect in 1967, observance of # Daylight Saving Time in the US was by local option, except during wartime. # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule MostNA 1918 1919 - Mar lastSun 2:00 1:00 D Rule MostNA 1918 1919 - Oct lastSun 2:00 0 S Rule MostNA 1942 only - Feb 9 2:00 1:00 W # War Rule MostNA 1945 only - Sep 30 2:00 0 S Rule MostNA 1967 1973 - Apr lastSun 2:00 1:00 D Rule MostNA 1967 1973 - Oct lastSun 2:00 0 S Rule MostNA 1974 only - Jan 6 2:00 1:00 D Rule MostNA 1974 only - Nov 24 2:00 0 S Rule MostNA 1975 only - Feb 23 2:00 1:00 D Rule MostNA 1975 only - Oct 26 2:00 0 S Rule MostNA 1976 2037 - Apr lastSun 2:00 1:00 D Rule MostNA 1976 2037 - Oct lastSun 2:00 0 S ############################################################################### # New names # Zone NAME GMTOFF RULES FORMAT Zone Atlantic -4:00 MostNA A%sT Zone Eastern -5:00 MostNA E%sT Zone Central -6:00 MostNA C%sT Zone Mountain -7:00 MostNA M%sT Zone Pacific -8:00 MostNA P%sT Zone Yukon -9:00 MostNA Y%sT Zone Aleutian -10:00 MostNA A%sT Zone Newfoundland -3:30 - NST # Is DST now observed here? # If so, when did it start? # Old names # Link LINK-FROM LINK-TO Link Eastern EST5EDT Link Central CST6CDT Link Mountain MST7MDT Link Pacific PST8PDT # Nonstandard mainland areas: Rule SomeUS 1918 1919 - Mar lastSun 2:00 1:00 D Rule SomeUS 1918 1919 - Oct lastSun 2:00 0 S Rule SomeUS 1942 only - Feb 9 2:00 1:00 W # War Rule SomeUS 1945 only - Sep 30 2:00 0 S Zone East-Indiana -5:00 SomeUS E%sT # Usually standard near South Bend Zone Arizona -7:00 SomeUS M%sT # Usually standard in Arizona # And then there's Hawaii. # DST was observed for one day in 1933. # Standard time was change by half an hour in 1947; this accounts for # the half-hour offsets before then, and the peculiar first rule. # (An alternative is to have "Zone Hawaiian -10:30..." with a # current standard offset of 0:30; this seems a bit more obscure.) # # Things have been calm since 1947. Rule Hawaii 1901 only - Dec 14 0:00 -0:30 S Rule Hawaii 1918 1919 - Mar lastSun 2:00 0:30 D Rule Hawaii 1918 1919 - Oct lastSun 2:00 -0:30 S Rule Hawaii 1933 only - Apr 30 2:00 0:30 D Rule Hawaii 1933 only - May 1 2:00 -0:30 S Rule Hawaii 1942 only - Feb 9 2:00 0:30 W # War Rule Hawaii 1945 only - Sep 30 2:00 -0:30 S Rule Hawaii 1947 only - Jun 8 2:00 0 S Zone Hawaiian -10:00 Hawaii H%sT SHAR_EOF if test 3139 -ne "`wc -c < 'northamerica'`" then echo shar: error transmitting "'northamerica'" '(should have been 3139 characters)' fi fi echo shar: extracting "'pacificnew'" '(922 characters)' if test -f 'pacificnew' then echo shar: will not over-write existing file "'pacificnew'" else cat << \SHAR_EOF > 'pacificnew' # @(#)pacificnew 2.1 # "Pacific Presidential Election Time" is being contemplated by the US Congress # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Twilite 1918 1919 - Mar lastSun 2:00 1:00 D Rule Twilite 1918 1919 - Oct lastSun 2:00 0 S Rule Twilite 1942 only - Feb 9 2:00 1:00 W # War Rule Twilite 1945 only - Sep 30 2:00 0 S Rule Twilite 1967 1973 - Apr lastSun 2:00 1:00 D Rule Twilite 1967 1973 - Oct lastSun 2:00 0 S Rule Twilite 1974 only - Jan 6 2:00 1:00 D Rule Twilite 1974 only - Nov 24 2:00 0 S Rule Twilite 1975 only - Feb 23 2:00 1:00 D Rule Twilite 1975 only - Oct 26 2:00 0 S Rule Twilite 1976 2037 - Apr lastSun 2:00 1:00 D Rule Twilite 1976 1987 - Oct lastSun 2:00 0 S Rule Twilite 1988 2037 uspres Oct lastSun 2:00 1:00 PE Rule Twilite 1988 2037 uspres Nov Sun>=7 2:00 0 S Rule Twilite 1988 2037 nonpres Oct lastSun 2:00 0 S # Zone NAME GMTOFF RULES FORMAT Zone Pacific-New -8:00 Twilite P%sT SHAR_EOF if test 922 -ne "`wc -c < 'pacificnew'`" then echo shar: error transmitting "'pacificnew'" '(should have been 922 characters)' fi fi exit 0 # End of shell archive