[net.sources] cute verbose "date"

fnf@unisoft.UUCP (Fred Fish) (01/25/85)

[...........]

Another cute little program from an old DECUS C distribution.
This one puts out something of the form:

"Today is Thursday, the twenty-fourth day of January, nineteen
eighty-five. The time is twelve minutes and forty seconds after seven
PM, Daylight Savings Time. The moon is new."

--Fred

#--------CUT---------CUT---------CUT---------CUT--------#
#########################################################
#                                                       #
# This is a shell archive file.  To extract files:      #
#                                                       #
#    1)	Make a directory for the files.                 #
#    2) Write a file, such as "file.shar", containing   #
#       this archive file into the directory.           #
#    3) Type "sh file.shar".  Do not use csh.           #
#                                                       #
#########################################################
#
#
echo Extracting Makefile:
sed 's/^Z//' >Makefile <<\STUNKYFLUFF
Z#
Z#	Makefile for very verbose date command (today).
Z
ZOBJ =	datetx.o moontx.o nbrtxt.o timetx.o today.o
Z
Ztoday:	$(OBJ)
Z	$(CC) -o $@ $(OBJ)
Z
STUNKYFLUFF
set `sum Makefile`
if test 62527 != $1
then
echo Makefile: Checksum error. Is: $1, should be: 62527.
fi
#
#
echo Extracting datetx.c:
sed 's/^Z//' >datetx.c <<\STUNKYFLUFF
Z#
Z/*
Z *              Convert Date to Readable Format.
Z *
Z * Synopsis:
Z *
Z *      char    *datetxt(buffer, year, month, day);
Z *      char    *buffer;        -- Output string goes here
Z *      int     year;           -- Year,        1979 = 1979
Z *      int     month;          -- Month,       January = 1
Z *      int     day;            -- Day,         1 = 1
Z *
Z * The readable date will be written into the outpub buffer, terminated by
Z * a null byte.  datetxt returns a pointer to the null byte.
Z *
Z * External routines called:
Z *
Z *      nbrtxt          (Number to ascii conversion)
Z *      copyst          (String copy routine)
Z */
Z
Zextern  char    *nbrtxt();
Zextern  char    *copyst();
Zextern  char    *datetxt();
Z
Zstatic char *daynames[] = {
Z	"Sunday",                       /* Sunday is day zero           */
Z	"Monday",
Z	"Tuesday",
Z	"Wednesday",
Z	"Thursday",
Z	"Friday",
Z	"Saturday",
Z};
Z
Zstatic char *monthnames[] = {
Z	"?Nomember?",                   /* Illegal month                */
Z	"January",
Z	"February",
Z	"March",
Z	"April",
Z	"May",
Z	"June",
Z	"July",
Z	"August",
Z	"September",
Z	"October",
Z	"November",
Z	"December",
Z};
Z
Zchar *datetxt(buffer, year, month, day)
Zchar    *buffer;                        /* Output goes here             */
Zint     year;                           /* Year, 1979 = 1979            */
Zint     month;                          /* Month of year, Jan = 1       */
Zint     day;                            /* Day in the month 1 = 1       */
Z/*
Z * Output the date in readable format:
Z *      Tuesday, the third of October
Z */
Z{
Z	register char   *op;                    /* Output pointer       */
Z
Z	op = buffer;                            /* Setup output pointer */
Z	op = copyst(op, daynames[dayofweek(year, month, day)]);
Z	op = copyst(op, ", the ");
Z	op = nbrtxt(op, day, 1);
Z	op = copyst(op, " day of ");
Z	op = copyst(op, monthnames[(month < 0 || month > 12) ? 0 : month]);
Z	op = copyst(op, ", ");
Z	if (year < 1000 || year >= 2000)
Z		return(nbrtxt(op, year, 0));
Z	else {
Z		op = nbrtxt(op, year/100, 0);
Z		op = copyst(op, " ");
Z		if ((year = year % 100) == 0)
Z			return(copyst(op, "hundred"));
Z		else
Z			return(nbrtxt(op, year, 0));
Z	}
Z}
Z
Zdayofweek(year, month, day)
Zint     year;                                   /* Year, 1978 = 1978    */
Zint     month;                                  /* Month, January = 1   */
Zint     day;                                    /* Day of month, 1 = 1  */
Z/*
Z * Return the day of the week on which this date falls: Sunday = 0.
Z * Note, this routine is valid only for the Gregorian calender.
Z */
Z{
Z	register int yearfactor;
Z
Z	yearfactor = year + (month - 14)/12;
Z	return (( (13 * (month + 10 - (month + 10)/13*12) - 1)/5
Z		+ day + 77 + 5 * (yearfactor % 100)/4
Z		+ yearfactor / 400
Z		- yearfactor / 100 * 2) % 7);
Z}
STUNKYFLUFF
set `sum datetx.c`
if test 65180 != $1
then
echo datetx.c: Checksum error. Is: $1, should be: 65180.
fi
#
#
echo Extracting moontx.c:
sed 's/^Z//' >moontx.c <<\STUNKYFLUFF
Z#
Z/*
Z *              Phase of the Moon Conversion.
Z *
Z * Synopsis
Z *
Z *      char    *moontxt(buffer, year, month, day)
Z *      char    *buffer;        -- Output goes here.
Z *      int     year;           -- Year         1979 = 1979
Z *      int     month;          -- Month        Jan. = 1
Z *      int     day;            -- Day          1    = 1
Z *
Z * The phase of the moon (in readable ascii) is written into the buffer,
Z * followed by a null.  The routine returns a pointer to the trailing null.
Z *
Z * External routines called:
Z *
Z *      copyst		(String copy routine)
Z */
Z
Zextern char		*copyst();	/* String output routine        */
Z
Zstatic char *phasetxt[] = {
Z	"new",
Z	"waxing crescent",
Z	"in its first quarter",
Z	"waxing gibbous",
Z	"full",
Z	"waning gibbous",
Z	"in its last quarter",
Z	"waning crescent"
Z};
Z
Zstatic int day_year[] = {		/* Days in year for each month	*/
Z	-1, -1, 30, 58, 89, 119, 150, 180, 211, 241, 272, 303, 333
Z};					/* Note: Jan. 1 will equal zero	*/
Z
Zchar *moontxt(buffer, year, month, day)
Zchar    *buffer;                        /* Where to put the text        */
Zint     year;                           /* Year, 1978 = 1978            */
Zint     month;                          /* Month, Jan = 1               */
Zint     day;                            /* Day, 1 = 1                   */
Z/*
Z * Output the phase of the moon for the given year, month, day.
Z * The routine calculates the year's epact (the age of the moon on Jan 1.),
Z * adds this to the number of days in the year, and calculates the phase
Z * of the moon for this date.
Z *
Z * In the algorithm:
Z *
Z *	diy	Is the day of the year - 1 (i.e., Jan 1 is day 0).
Z *
Z *	golden	Is the number of the year in the Mentonic cycle, used to
Z *		determine the position of the calender moon.
Z *
Z *	epact	Is the age of the calender moon (in days) at the beginning
Z *		of the year.  To calculate epact, two century-based
Z *		corrections are applied:
Z *		Gregorian:	(3 * cent)/4 - 12
Z *			is the number of years such as 1700, 1800 when
Z *			leap year was not held.
Z *		Clavian:	(((8 * cent) + 5) / 25) - 5
Z *			is a correction to the Mentonic cycle of about
Z *			8 days evry 2500 years.  Note that this will
Z *			overflow 16 bits in the year 409600.  Beware.
Z *
Z * The algorithm is accurate for the Gregorian calender only.
Z *	
Z * The magic numbers used in the phase calculation are as follows:
Z *	 29.5		The moon's period in days.
Z *	177		29.5 scaled by 6
Z *	 22		(29.5 / 8) scaled by 6 (this gets the phase)
Z *	 11		((29.5 / 8) / 2) scaled by 6
Z *
Z * Theoretically, this should yield a number in the range 0 .. 7.  However,
Z * two days per year, things don't work out too well.
Z *
Z * Epact is calculated by the algorithm given in Knuth vol. 1 (calculation
Z * of Easter).  See also the article on Calenders in the Encyclopaedia
Z * Britannica and Knuth's algorithm in CACM April 1962, page 209.
Z */
Z{
Z	int		phase;		/* Moon phase			*/
Z	register int	cent;		/* Century number (1979 = 20)	*/
Z	register int	epact;		/* Age of the moon on Jan. 1	*/
Z	register int	diy;		/* Day in the year		*/
Z	int		golden;		/* Moon's golden number		*/
Z
Z	if (month < 0 || month > 12) month = 0;	/* Just in case		*/
Z	diy = day + day_year[month];		/* Day in the year	*/
Z	if ((month > 2) && ((year % 4 == 0) && 
Z			((year % 400 == 0) || (year % 100 != 0))))
Z		diy++;				/* Leapyear fixup	*/
Z	cent = (year / 100) + 1;		/* Century number	*/
Z	golden = (year % 19) + 1;		/* Golden number	*/
Z	epact = ((11 * golden) + 20		/* Golden number	*/
Z		+ (((8 * cent) + 5) / 25) - 5	/* 400 year cycle	*/
Z		- (((3 * cent) / 4) - 12)) % 30;/* Leap year correction	*/
Z	if (epact <= 0)
Z		epact += 30;			/* Age range is 1 .. 30	*/
Z	if ((epact == 25 && golden > 11) || epact == 24)
Z		epact++;
Z	/*
Z	 * Calculate the phase, using the magic numbers defined above.
Z	 * Note that (phase and 7) is equivalent to (phase mod 8) and
Z	 * is needed on two days per year (when the algorithm yields 8).
Z	 */
Z	phase = (((((diy + epact) * 6) + 11) % 177) / 22) & 7;
Z	return(copyst(buffer, phasetxt[phase]));
Z}
STUNKYFLUFF
set `sum moontx.c`
if test 12622 != $1
then
echo moontx.c: Checksum error. Is: $1, should be: 12622.
fi
#
#
echo Extracting nbrtxt.c:
sed 's/^Z//' >nbrtxt.c <<\STUNKYFLUFF
Z#
Z/*
Z *              Integer to Readable ASCII Conversion Routine.
Z *
Z * Synopsis:
Z *
Z *      char *nbrtxt(buffer, value, ordinal)
Z *      char    *buffer;        -- The output buffer
Z *      int     value;          -- The number to output
Z *      int     ordinal;        -- Non-zero for ordinal number
Z *
Z *
Z * The value is converted to a readable number and put in the output
Z * buffer (null-terminated).  A pointer to the first free location
Z * in the buffer (i.e., the null) is returned.  The ordinal
Z * flag distinguishes between cardinal and ordinal numbers:
Z *
Z *      nbrtxt(buffer, 1, 0) = "one"
Z *      nbrtxt(buffer, 1, 1) = "first"
Z *
Z * The longest output string is:
Z *
Z *      Twenty-seven thousand, three hundred and seventy-seventh.
Z *
Z *
Z *
Z *              Copy a String
Z *
Z * Synopsis
Z *
Z *      char *copyst(out, in)
Z *      char    *out;           -- The output string
Z *      char    *in;            -- The input string
Z *
Z * The input string is copied into the output string.  Copyst returns
Z * a pointer to the null trailer.
Z *
Z */
Z
Zextern char     *nbrtxt();
Zextern char     *copyst();
Z
Zstatic char *cardinal[] = {
Z	"zero",
Z	"one",
Z	"two",
Z	"three",
Z	"four",
Z	"five",
Z	"six",
Z	"seven",
Z	"eight",
Z	"nine",
Z	"ten",
Z	"eleven",
Z	"twelve",
Z	"thirteen",
Z	"fourteen",
Z	"fifteen",
Z	"sixteen",
Z	"seventeen",
Z	"eighteen",
Z	"nineteen"
Z};
Z
Zstatic char *ordinal[] = {
Z	"zeroth",
Z	"first",
Z	"second",
Z	"third",
Z	"fourth",
Z	"fifth",
Z	"sixth",
Z	"seventh",
Z	"eighth",
Z	"ninth",
Z	"tenth",
Z	"eleventh",
Z	"twelfth"
Z};
Z
Zstatic char *twenties[] = {
Z	"twen",
Z	"thir",
Z	"for",
Z	"fif",
Z	"six",
Z	"seven",
Z	"eigh",
Z	"nine"
Z};
Z
Zchar *nbrtxt(buffer, datum, ordflag)
Zchar    *buffer;                        /* Output string buffer         */
Zint     datum;                          /* what to translate            */
Zint     ordflag;                        /* 0 if cardinal, 1 if ordinal  */
Z/*
Z * Translate a number to a readable text string, punctuation and all.
Z * If ordflag is non-zero, ordinal numbers ("first, second") will
Z * be generated, rather than cardinal ("one, two").
Z * Note: nbrtxt() is recursive.
Z */
Z{
Z
Z	register int value;
Z	register char   *op;
Z
Z	op = buffer;
Z	value = datum;
Z	if (value < 0) {
Z		op = copyst(op, "minus ");
Z		value = (-value);
Z		if (value < 0) {                /* Hack -32768          */
Z			op = copyst(op, twenties[1]);
Z			value = 2768;
Z		}
Z	}
Z	if (value >= 1000) {
Z		op = nbrtxt(op, value/1000, 0);
Z		op = copyst(op, " thousand");
Z		value = value % 1000;
Z		if (value == 0) goto exit;
Z		op = copyst(op, (value >= 100) ? ", " : " and ");
Z	}
Z	if (value >= 100) {
Z		op = copyst(op, cardinal[value/100]);
Z		op = copyst(op, " hundred");
Z		value = value % 100;
Z		if (value == 0) goto exit;
Z		op = copyst(op, " ");
Z	}
Z	if (value >= 20) {
Z		if (value == 90 && ordflag)
Z			return(copyst(op, "nintieth"));
Z		op = copyst(op, twenties[(value-20) / 10]);
Z		value = value % 10;
Z		if (value == 0) {
Z			return(copyst(op, (ordflag) ? "tieth" : "ty"));
Z		}
Z		op = copyst(op, "ty-");
Z	}
Z	if (value <= 12) {
Z		return(copyst(op,
Z			(ordflag) ? ordinal[value] : cardinal[value]));
Z	}
Z	op = copyst(op, cardinal[value]);       /* fourteen, fourteenth */
Z	/*
Z	 * Here on 100, 14000, etc.
Z	 */
Zexit:   if (ordflag) op = copyst(op, "th");
Z	return(op);
Z}
Z
Zchar *
Zcopyst(buffer, string)
Zchar    *buffer;
Zchar    *string;
Z/*
Z * Copy a string into buffer.  Return the free pointer.
Z */
Z{
Z	register char   *ip;
Z	register char   *op;
Z
Z	ip = string;
Z	op = buffer;
Z
Z	while ((*op = *ip++)) op++;
Z	return (op);
Z}
STUNKYFLUFF
set `sum nbrtxt.c`
if test 30886 != $1
then
echo nbrtxt.c: Checksum error. Is: $1, should be: 30886.
fi
#
#
echo Extracting timetx.c:
sed 's/^Z//' >timetx.c <<\STUNKYFLUFF
Z#
Z/*
Z *              Convert Time to a Readable Format.
Z *
Z * Synopsis:
Z *
Z *      char    *timetxt(buffer, hour, minute, second, daylight);
Z *      char    *buffer;        -- Where output goes
Z *      int     hour;           -- Hour,        range is 0 to 24
Z *      int     minute;         -- Minute,      range is -1 to 59
Z *      int     second;         -- Seconds,     range is -1 to 59
Z *      int     daylight;       -- Daylight savings time if non-zero.
Z *
Z * Note: if minute or second is less than zero, the value is not calculated.
Z * This distinguishes between "unknown seconds" and "exactly no seconds."
Z * If hour is less than zero, a null string is returned.
Z * Timetxt converts the time to a null-trailed string.  It returns a pointer
Z * to the first free byte (i.e. the null);
Z *
Z * The output follows the syntax of Robert J. Lurtsema, and includes:
Z *
Z *      In twenty-five seconds, the time will be ten minutes before noon.
Z *
Z *
Z * External routines called:
Z *
Z *      nbrtxt          (Number to ascii conversion)
Z *      copyst          (String copy routine)
Z */
Z
Zextern char     *nbrtxt();
Zextern char     *copyst();
Z
Zchar *timetxt(buffer, hour, minute, second, daylight)
Zchar    *buffer;                        /* Output buffer                */
Zint     hour;                           /* Hours 00 - 23                */
Zint     minute;                         /* Minutes                      */
Zint     second;                         /* Seconds                      */
Zint     daylight;                       /* Non-zero if savings time     */
Z/*
Z * Output time of day.
Z */
Z{
Z	char            *op;            /* Output pointer               */
Z	register int    late;           /* after hour or afternoon      */
Z	register int	sec;		/* Seconds temp			*/
Z	char		*stuff();	/* Buffer stuffer		*/
Z
Z	op = buffer;                    /* Setup buffer pointer         */
Z	if (hour < 0) {			/* If it's a dummy call,	*/
Z		*op = 0;		/* Return a null string		*/
Z		return(op);
Z	}
Z	if (daylight == 0101010) {      /* Secret flag                  */
Z		op = copyst(op, "The big hand is on the ");
Z		op = nbrtxt(op, (((minute + 2 + second/30)/5 + 11)%12)+1, 0);
Z		op = copyst(op," and the little hand is on the ");
Z		op = nbrtxt(op, ((hour + 11) % 12) + 1, 0);
Z		return(copyst(op, ".  "));
Z	}
Z	/*
Z	 * Check if the time is more than 30 minutes past the hour.
Z	 * If so, output the time before the next hour.
Z	 */
Z	if (minute < 0) second = (-2);  /* No minutes means no seconds  */
Z	else if ((late = (minute > 30 || (minute == 30 && second > 0)))) {
Z		if (second > 0) {       /* Before next hour             */
Z			second = 60 - second;
Z			minute += 1;    /* Diddle the minute, too       */
Z		}
Z		minute = 60 - minute;   /* Minutes before next hour     */
Z		hour += 1;              /* Frobozz next hour getter     */
Z	}
Z	/*
Z	 * Decisions, decisions:
Z	 *	Minutes	Seconds =>
Z	 *	  00	  00	Exactly Noon
Z	 *	  00	  01	One second after noon
Z	 *	  01	  00	Exactly one minute after noon
Z	 *	  30	  00	Exactly half past noon
Z	 *	  59	  00	Exactly one minute before noon
Z	 *	  59	  59	In one second, the time will be noon
Z	 */
Z	if (late && second > 0) {       /* Respectfully dedicated to    */
Z					/* Robert J. Lurtsema           */
Z		op = stuff(op, second, 1, "In ", " second");
Z		op = copyst(op, ", the time will be ");
Z		sec = -2;		/* We've done seconds already	*/
Z	}
Z	else {
Z		op = copyst(op, "The time is ");
Z		sec = second;		/* Seconds still to be done	*/
Z	}
Z	if (sec == 0) {
Z		op = copyst(op, "exactly ");
Z		if (minute == 30)
Z			op = copyst(op, "half past ");
Z		else	op = stuff(op, minute, 1, " ", " minute");
Z	}
Z	else {				/* Non exact or missing seconds	*/
Z		op = stuff(op, minute, 0, " ",     " minute");
Z	        op = stuff(op, sec, (minute > 0),  " and ", " second");
Z	}
Z	op = copyst(op, (minute < 0 || (minute == 0 && late)
Z			|| (second == 0
Z				&& ((minute == 0 && late == 0)
Z					|| minute == 30))) ? " "
Z		: (late) ? " before " : " after ");
Z	/*
Z	 * Hours are not quite so bad
Z	 */
Z	if (hour == 0 || hour == 24)
Z		op = copyst(op, "midnight");
Z	else if (hour == 12)
Z		op = copyst(op, "noon");
Z	else {
Z		if (late = (hour > 12))
Z			hour = hour - 12;
Z		op = nbrtxt(op, hour, 0);
Z		op = copyst(op, (late) ? " PM" : " AM");
Z	}
Z	return(copyst(op, (daylight)
Z		? ", Daylight Savings Time.  "
Z		: ", Digital Standard Time.  "));
Z}
Z
Zstatic char *
Zstuff(buffer, value, flag, leading, trailing)
Zchar    *buffer;                        /* Output goes here             */
Zint     value;                          /* The value to print if > 0    */
Zint     flag;                           /* flag is set to print leading */
Zchar    *leading;                       /* preceeded by ...             */
Zchar    *trailing;                      /* and followed by ...          */
Z/*
Z * If value <= zero, output nothing. Else, output "leading" value "trailing".
Z * Note: leading is output only if flag is set.
Z * If value is not one, output an "s", too.
Z */
Z{
Z	register char   *op;            /* Output pointer               */
Z
Z	op = buffer;                    /* Setup buffer pointer         */
Z	if (value > 0) {
Z		if (flag)
Z			op = copyst(op, leading);
Z		op = nbrtxt(op, value, 0);
Z		op = copyst(op, trailing);
Z		if (value != 1)
Z			op = copyst(op, "s");
Z	}
Z	return(op);
Z}
STUNKYFLUFF
set `sum timetx.c`
if test 32974 != $1
then
echo timetx.c: Checksum error. Is: $1, should be: 32974.
fi
#
#
echo Extracting today.c:
sed 's/^Z//' >today.c <<\STUNKYFLUFF
Z#
Z/*
Z *			T O D A Y
Z *
Z * time of day
Z *
Z * Define UNIX for "native" Unix
Z */
Z
Z/*)BUILD	$(PROGRAM)	= today
Z		$(FILES)	= { today datetx timetx nbrtxt moontx }
Z		$(TKBOPTIONS)	= {
Z			TASK	= ...TOD
Z		}
Z*/
Z
Z#ifdef	DOCUMENTATION
Z
Ztitle	today	Date and Time in English
Zindex		Date and Time in English
Z
Zsynopsis
Z
Z	today [-] [x] | [date]
Z
Zdescription
Z
Z	Today prints the date, time, and phase of the moon in English.
Z	The following options are available:
Z	.lm +8
Z	.s.i -8;- or x	Read date strings from the standard input file.
Z	.s.i -8;date	Print information for the indicated date.
Z	.s.lm -8
Z	Date and time information is given in ISO numeric notation.  For
Z	example, November 6, 1980 would be represented as "801106".  If
Z	a time is needed, it would be appended to the date, using 24-hour
Z	notation: "801106110402" would be a time which is exact to the
Z	second.  To specify the century, the two-digit century number
Z	may be preceeded by '+' as in "+18801106".
Z	.s
Z	Non-numeric separators between the various fields are permitted:
Z	"+1776.07.04-11:15:21".  Note that the full two digit entry must be
Z	given.
Z	.s
Z	If no parameter is given, today outputs the current date and time.
Z
Zdiagnostics
Z
Z	.lm +8
Z	.s.i -8;Bad parameters or date out of range in ...
Z	.s
Z	An input date or time is incorrect.
Z	.lm -8
Z
Zauthor
Z
Z	Martin Minow
Z
Zbugs
Z
Z	The algorithm is only valid for the Gregorian calender.
Z
Z#endif
Z
Z#define	APRIL_FOOLS
Z
Zint	__narg	=	1;		/* No prompt if no args		*/
Z#define LINEWIDTH       72              /* Width of line                */
Z
Z#include <stdio.h>
Z#include <time.h>
Z#ifdef unix
Z#include <ctype.h>
Z#undef NULL
Z#endif
Z
Z#define	NULL		0
Z#define	EOS		0
Z#define	FALSE		0
Z#define	TRUE		1
Z
Zint day_month[] = {			/* Needed for dotexttime()      */
Z	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
Z};
Zint     ccpos;                          /* Current line position        */
Zchar    lastbyte;                       /* Memory for output()          */
Zchar    line[100];                      /* Data line for input function */
Zchar    *valptr;                        /* Needed for number converter  */
Zchar    wordbuffer[LINEWIDTH];          /* Buffer for output function   */
Zchar    *wordptr = wordbuffer;          /* Free byte in wordbuffer      */
Zchar	linebuffer[LINEWIDTH+2];	/* Output text buffer		*/
Zchar	*lineptr = linebuffer;		/* Free byte in linebuffer	*/
Zint     polish;                         /* Funny mode flag              */
Z
Zextern  char    *datetxt();             /* Date getter                  */
Zextern  char    *timetxt();             /* Time of day getter           */
Zextern  char    *moontxt();             /* Phase of the moon getter     */
Z
Zmain(argc, argv)
Zint     argc;
Zchar    *argv[];
Z/*
Z * Driver for time routines.  Usage:
Z *
Z *      today                   Prints current time of day in readable form,
Z *                              followed by a cookie.
Z *
Z *      today {+cc}yymmddhhmmss Prints indicated time of day.
Z *                              Note, hh, mm, ss may be omitted.
Z *                              For example:
Z *                                today 401106     = Nov. 6, 1940
Z *                                today +19401106  = Nov. 6, 1940
Z *                                today 4011061015 = Nov. 6, 1940 10:15 AM
Z *
Z *      today -                 Data is read from the standard input and
Z *      today x                 output as needed.  The format of each
Z *                              line is identical to the command format.
Z *				("today x" is needed for vms.)
Z */
Z
Z{
Z	ccpos = 0;                      /* New line now                 */
Z	wordptr = wordbuffer;           /* Nothing buffered             */
Z	lineptr = linebuffer;		/* Nothing in output buffer too	*/
Z	polish = 0;			/* Normal mode			*/
Z	if (argc > 1 && tolower(argv[1][0]) == 'p') {
Z		polish = 1;
Z		argc--;
Z		argv++;
Z	}
Z	if (argc == 2 && ((argv[1][0] == '-') || (argv[1][0] | 040) == 'x')) {
Z		while (!getline()) {	/* Read and print times */
Z			dotexttime(line);
Z		}
Z		return;
Z	}
Z	else if (argc > 1) {
Z		if (dotexttime(argv[1]) == 0)
Z			return;
Z	}
Z	/*
Z	 * Here if no parameters or an error in the parameter field.
Z	 */
Z	dotime();			/* Print the time.              */
Z	output("\n");           	/* Space before cookie          */
Z#ifdef	UNIX
Z	execl(COOKIEPROGRAM, "cookie", 0);
Z#endif
Z}
Z
Zdotime()
Z/*
Z * Print the time of day for Unix or VMS native mode.
Z */
Z{
Z	int     tvec[2];                /* Buffer for time function     */
Z	struct  tm *localtime();	/* Unix time decompile function */
Z	struct  tm *p;			/* Local pointer to time of day */
Z	int     year;
Z	int     month;
Z 
Z	time(tvec);                     /* Get the time of day          */
Z	p = localtime(tvec);            /* Make it more understandable  */
Z	year = p->tm_year + 1900;
Z	month = p->tm_mon + 1;
Z#ifdef	APRIL_FOOLS
Z	if (month == 4 && p->tm_mday == 1)
Z		polish = !polish;
Z#endif
Z	process(year, month, p->tm_mday, p->tm_hour,
Z			p->tm_min, p->tm_sec, p->tm_wday);
Z}
Z
Zdotexttime(text)
Zchar    *text;                          /* Time text                    */
Z/*
Z * Create the time values and print them, return 1 on error.
Z */
Z
Z{
Z	int     epoch;                  /* Which century                */
Z	int     year;
Z	int     month;
Z	int     day;
Z	int     hour;
Z	int     minute;
Z	int     second;
Z	int     leapyear;
Z
Z	valptr = text;                          /* Setup for getval()   */
Z	while (*valptr == ' ') valptr++;        /* Leading blanks skip  */
Z	if (*valptr != '+')
Z		epoch = 1900;                   /* Default for now      */
Z	else {
Z		valptr++;
Z		if ((epoch = getval(-1, 00, 99)) < 0) goto bad;
Z		epoch *= 100;                   /* Make it a real epoch */
Z	}
Z
Z	if ((year = getval(-1, 00, 99)) < 0) goto bad;
Z	year += epoch;
Z	leapyear = ((year%4) == 0) && (((year%400) == 0) || (year%100 != 0));
Z	if ((month = getval(-1, 1, 12)) < 0) goto bad;
Z	if ((day = getval(-1, 1,
Z		(month == 2 && leapyear) ? 29 : day_month[month])) < 0)
Z			goto bad;
Z	if ((hour = getval(-2, 0, 23)) == -1) goto bad;
Z	if ((minute = getval(-2, 0, 59)) == -1) goto bad;
Z	if ((second = getval(-2, 0, 59)) == -1) goto bad;
Z	process(year, month, day, hour, minute, second, 0);
Z	return(0);				/* Normal exit		*/
Z
Zbad:    output("Bad parameters or date out of range in \"");
Z	output(text);
Z	output("\" after scanning \"");
Z	*valptr = '\0';
Z	output(text);
Z	output("\".\n");
Z	return(1);				/* Error exit		*/
Z}
Z
Zstatic	char    outline[500];		/* Output buffer                */
Z
Zprocess(year, month, day, hour, minute, second, daylight)
Zint     year;                           /* Year		1900 = 1900	*/
Zint     month;                          /* Month	January = 1	*/
Zint     day;                            /* Day		1 = 1		*/
Zint	hour;				/* Hour		0 .. 23		*/
Zint	minute;				/* Minute	0 .. 59		*/
Zint	second;				/* Second	0 .. 59		*/
Zint	daylight;			/* Daylight savings time if 1	*/
Z/*
Z * Output the information.  Note that the parameters are within range.
Z */
Z{
Z
Z	output("Today is ");
Z	datetxt(outline, year, month, day);
Z	output(outline);
Z	output(".  ");
Z	timetxt(outline, hour, minute, second,
Z			(polish) ? 0101010 : daylight);
Z	output(outline);
Z	output("The moon is ");
Z	moontxt(outline, year, month, day);
Z	output(outline);
Z	output(".  \n");
Z}
Z
Z
Zoutput(text)
Zchar    *text;                                  /* What to print        */
Z/*
Z * Output routine.  Text is output using put() so that lines are
Z * not more than LINEWIDTH bytes long.  Current position is in global ccpos.
Z * (put is equivalent to putchar() except that it is locally buffered.)
Z */
Z{
Z	register char	*in;                    /* Current pos. in scan */
Z	register char	c;                      /* Current character    */
Z	register char	*wp;			/* Word pointer		*/
Z
Z	in = text;
Z	while (c = *in++) {
Z		switch (c) {
Z		case '\n':                      /* Force new line       */
Z		case ' ':                       /* or a space seen      */
Z			if ((wordptr-wordbuffer) + ccpos >= LINEWIDTH) {
Z				put('\n');  /* Current word         */
Z				ccpos = 0;      /* won't fit, dump it.  */
Z			}
Z			if (wordptr > wordbuffer) {
Z				if (ccpos) {	/* Leading space needed */
Z					put(' ');
Z					ccpos++;
Z				}
Z				for (wp = wordbuffer; wp < wordptr;) {
Z					put(*wp++);
Z				}
Z				ccpos += (wordptr - wordbuffer);
Z				wordptr = wordbuffer;	/* Empty buffer	*/
Z			}
Z			if (c == '\n') {
Z				put('\n');	/* Print a newline	*/
Z				ccpos = 0;	/* and reset the cursor	*/
Z			}
Z			break;
Z
Z		default:
Z			*wordptr++ = c;         /* Save piece of word   */
Z		}
Z	}
Z}
Z
Zput(c)
Zregister char	c;
Z/*
Z * Actual output routine
Z */
Z{
Z	if (c == '\n' || (lineptr - linebuffer) >= LINEWIDTH) {
Z		*lineptr = EOS;
Z		puts(linebuffer);
Z		lineptr = linebuffer;
Z		if (c == '\n')
Z			return;
Z	}
Z	*lineptr++ = c;
Z} 
Z
Zgetline()
Z/*
Z * Read text to global line[].  Return 1 on end of file, zero on ok.
Z */
Z{
Z	register char *t;
Z
Z	return (gets(line) == NULL);
Z}
Z
Zgetval(flag, low, high)
Zint     flag;
Zint     low;
Zint     high;
Z/*
Z * Global valptr points to a 2-digit positive decimal integer.
Z * Skip over leading non-numbers and return the value.
Z * Return flag if text[0] == '\0'. Return -1 if the text is bad,
Z * or if the value is out of the low:high range.
Z */
Z{
Z	register int value;
Z	register int i;
Z	register int temp;
Z
Z	if (*valptr == '\0') return(flag);        /* Default?             */
Z	while (*valptr && (*valptr < '0' || *valptr > '9')) *valptr++;
Z				/* The above allows for 78.04.22 format */
Z	for (value = i = 0; i < 2; i++) {
Z		temp = *valptr++ - '0';
Z		if (temp < 0 || temp > 9) return(-1);
Z		value = (value*10) + temp;
Z	}
Z	return((value >= low && value <= high) ? value : -1);
Z}
Z
STUNKYFLUFF
set `sum today.c`
if test 43354 != $1
then
echo today.c: Checksum error. Is: $1, should be: 43354.
fi
echo ALL DONE BUNKY!
exit 0

avolio@grendel.UUCP (Frederick M. Avolio) (01/30/85)

Yes, it is "cute."  I believe there is one mistake.  The pointer to the
day of the week is passed in today to process when the daylight savings
flag is needed. (Sorry if this has already been reported.)

new vs. old:
171c171
< 			p->tm_min, p->tm_sec, p->tm_isdst);
---
> 			p->tm_min, p->tm_sec, p->tm_wday);
-- 
Fred Avolio      {seismo,decvax}!grendel!avolio      301/731-4100 x4227