/*
 * 			C A L E N D A R
 *
 * Usage:
 *	calend	MM		If small, it's a month, if large, a year.
 * or
 *	calend	YYYY MM		year/month
 * or
 *	calend	MM YYYY	
 */

/*)BUILD
*/

#include <stdio.h>
#include <time.h>
#ifdef	decus
int	$$narg = 1;			/* Don't prompt			*/
#endif

#define	EOS	0

#define	MWIDTH	(7*3 + 3)		/* Bytes to print a month	*/
#define	WPERMO	6			/* Max. weeks per month		*/
#define	LWIDTH	72			/* Line width			*/
#define	MPERLIN	(LWIDTH / MWIDTH)	/* Three months per line	*/
#define	WWIDTH	(LWIDTH / MPERLIN)	/* Bytes in one week		*/
#define	BUFSIZ	(WPERMO * LWIDTH)	/* Buffer dimension		*/

char	*weekday = " S  M Tu  W Th  F  S";
char	*monthname[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

char	buffer[BUFSIZ];

main(argc, argv)
int		argc;
char		*argv[];
{
	register int	month;
	register int	year;

	register int	arg1val;
	int		arg1len;
	int		arg2val;
	int		tvec[2];
	struct tm	*tm;

	time(&tvec[0]);
	tm = localtime(&tvec[0]);
	year = tm->tm_year + 1900;
	month = tm->tm_mon + 1;

	if (argc <= 1)
	    /*
	     * No arguments mean do last, this, and next month
	     */
	    do3months(year, month);
	else {
	    arg1val = atoi(argv[1]);
	    arg1len = strlen(argv[1]);
	    if (argc == 2) {
		/*
		 * Only one argument, if small, it's a month.  If
		 * large, it's a year.  Note:
		 *	calend	0082	Year '82
		 *	calend	82	Year 1982
		 */
		if (arg1len <= 2 && arg1val <= 12)
		    do3months(year, arg1val);
		else {
		    if (arg1len <= 2 && arg1val > 0 && arg1val <= 99)
			arg1val += 1900;
		    doyear(arg1val);
		}
	    }
	    else {
		/*
		 * Two arguments, allow 1980 12 or 12 1980
		 */
		arg2val = atoi(argv[2]);
		if (arg1len > 2)
		    do3months(arg1val, arg2val);
		else
		    do3months(arg2val, arg1val);
	    }
	}
}

doyear(year)
int		year;
/*
 * Print a calendar for an entire year.
 */
{
	register int	month;
	register int	windex;		/* Week index			*/

	if (year < 1 || year > 9999)
		usage("year: %d\n", year);
	printf("\n\n\n%35d\n\n", year);
	for (month = 1; month <= 12; month += MPERLIN) {
		zero(buffer, sizeof buffer);
		printf("%12s%23s%23s\n", monthname[month-1],
				monthname[month], monthname[month+1]);
		printf("%s   %s   %s\n", weekday, weekday, weekday);
		calendar(year, month+0, &buffer[(MWIDTH-1)*0], LWIDTH);
		calendar(year, month+1, &buffer[(MWIDTH-1)*1], LWIDTH);
		calendar(year, month+2, &buffer[(MWIDTH-1)*2], LWIDTH);
		for (windex = 0; windex < (WPERMO * LWIDTH); windex += LWIDTH)
			out(&buffer[windex], LWIDTH);
	}
	printf("\n\n\n");
}

domonth(year, month)
int		year;
int		month;
/*
 * Do one specific month -- note: no longer used
 */
{
	register int	windex;

	if (year < 1 || year > 9999)
		usage("Bad year: %d\n", year);
	if (month <= 0 || month > 12)
		usage("month: %d\n", month);
	printf("%9s%5d\n\n%s\n", monthname[month-1], year, weekday);
	calendar(year, month, buffer, WWIDTH);
	for (windex = 0; windex < (WPERMO * WWIDTH); windex += WWIDTH)
		out(&buffer[windex], WWIDTH);
	printf("\n\n");
}

do3months(thisyear, thismonth)
int		thisyear;
register int	thismonth;
/*
 * Do last month, this month, and next months.  The parameters
 * are guaranteed accurate. (and year will not be less than 2 nor
 * greater than 9998).
 */
{
	register int	windex;		/* Week index			*/
	int		lastmonth;
	int		lastyear;
	int		nextmonth;
	int		nextyear;

	lastyear = nextyear = thisyear;
	if ((lastmonth = thismonth - 1) == 0) {
		lastmonth = 12;
		lastyear--;
	}
	if ((nextmonth = thismonth + 1) == 13) {
		nextmonth = 1;
		nextyear++;
	}
	printf("%9s%5d%18s%5d%18s%5d\n",
		monthname[lastmonth - 1], lastyear,
		monthname[thismonth - 1], thisyear,
		monthname[nextmonth - 1], nextyear);
	printf("%s   %s   %s\n", weekday, weekday, weekday);
	calendar(lastyear, lastmonth, &buffer[(MWIDTH-1)*0], LWIDTH);
	calendar(thisyear, thismonth, &buffer[(MWIDTH-1)*1], LWIDTH);
	calendar(nextyear, nextmonth, &buffer[(MWIDTH-1)*2], LWIDTH);
	for (windex = 0; windex < (WPERMO * LWIDTH); windex += LWIDTH)
		out(&buffer[windex], LWIDTH);
	printf("\n\n\n");
}
	
out(text, len)
char		*text;
int		len;
/*
 * Clean up and output this line
 */
{
	register char	*firstp;
	register char	*lastp;

	lastp = &text[len];

	for (firstp = text; firstp < lastp; firstp++) {
		if (*firstp == EOS)
			*firstp = ' ';
	}
	while (lastp > text && *--lastp == ' ');
	lastp[1] = EOS;
	printf("%s\n", text);
}

usage(format, args)
char		*format;
int		args;
/*
 * Fatal parameter error
 */
{
	fprintf(stderr, "Calendar parameter error: ");
#ifdef	decus
	$$prnt(format, &args, stderr);
#else
	_doprnt(format, &args, stderr);
#endif
	fprintf(stderr, "Usage: \"calend month\" or \"calend year month\"\n");
	fprintf(stderr, "Year and month are integers.\n");
	exit(1);
}


/*
 * Actually generate a calendar
 */
static char monthdays[] = {
 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};					/* Thirty days hath september	*/
					/* Except for 1752		*/

calender(year, month, outbuf, offset)
int		year;
int		month;
char		*outbuf;		/* Output goes here		*/
int		offset;			/* "Distance" to next month	*/
/*
 * Build the calendar for this year/month
 */
{
	register char	*outp;
	register int	dow;		/* Day of week			*/
	register int	day;		/* Day in month			*/

	outp = outbuf;
	dow = jan1(year);
	monthdays[2] = 29;		/* Assume leap year		*/
	monthdays[9] = 30;		/* Assume no magic		*/
	switch((jan1(year + 1) + 7 - dow) % 7) {

	case 2:
		/*
		 * Leap year if jan 1 of next year is two days after
		 * this year's jan 1
		 */
		break;

	case 1:
		/*
		 * Not a leap year if jan 1 of next year is the next day
		 * after jan 1 of this year.
		 */
		monthdays[2] = 28;
		break;

	default:
		/*
		 * Magic year of 1752 when September lost 11 days.
		 */
		monthdays[9] = 19;
		break;
	}
	for (day = 1; day < month; day++)
		dow += monthdays[day];
	dow %= 7;			/* Weekday of first of month	*/
	outp += (dow * 3);		/* Where to start in calendar	*/
	for (day = 1; day <= monthdays[month]; day++) {
		if (day == 3 && monthdays[month] == 19) {
			/*
			 * It's magic (September 3 became September 14)
			 * And September 1752 really did have 30 days.
			 */
			day += 11;
			monthdays[month] += 11;
		}
		if (day > 9)
			*outp = (day / 10) + '0';
		outp++;
		*outp++ = (day % 10) + '0';
		outp++;
		if (++dow >= 7) {
			/*
			 * Sunday, rotate to the next line
			 */
			dow = 0;
			outp = &outbuf[offset];
			outbuf = outp;
		}
	}
}


jan1(year)
register int	year;
/*
 * Return day that jan 1 falls on for this year.
 */
{
	register int	day;

	/*
	 * Gregorian calendar:  one extra day per four years.
	 */
	day = year + 4 + (year + 3) / 4;
	/*
	 * Julian calendar:  Gregorian less three days per 400
	 */
	if (year > 1800) {
		day -= ((year - 1701) / 100);
		day += ((year - 1601) / 400);
	}
	/*
	 * Calendar changeover year (valid in America only).
	 */
	if (year > 1752)
		day += 3;
	return (day % 7);
}

