time conversion / time zone system
sources-request at panda.UUCP
sources-request at panda.UUCP
Sat Mar 8 08:43:08 AEST 1986
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
More information about the Mod.sources
mailing list