Home | History | Annotate | Line # | Download | only in cal
cal.c revision 1.15
      1  1.15    atatat /*	$NetBSD: cal.c,v 1.15 2003/06/05 00:21:20 atatat Exp $	*/
      2   1.6     glass 
      3   1.1       cgd /*
      4   1.6     glass  * Copyright (c) 1989, 1993, 1994
      5   1.6     glass  *	The Regents of the University of California.  All rights reserved.
      6   1.1       cgd  *
      7   1.1       cgd  * This code is derived from software contributed to Berkeley by
      8   1.1       cgd  * Kim Letkeman.
      9   1.1       cgd  *
     10   1.1       cgd  * Redistribution and use in source and binary forms, with or without
     11   1.1       cgd  * modification, are permitted provided that the following conditions
     12   1.1       cgd  * are met:
     13   1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     14   1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     15   1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     16   1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     17   1.1       cgd  *    documentation and/or other materials provided with the distribution.
     18   1.1       cgd  * 3. All advertising materials mentioning features or use of this software
     19   1.1       cgd  *    must display the following acknowledgement:
     20   1.1       cgd  *	This product includes software developed by the University of
     21   1.1       cgd  *	California, Berkeley and its contributors.
     22   1.1       cgd  * 4. Neither the name of the University nor the names of its contributors
     23   1.1       cgd  *    may be used to endorse or promote products derived from this software
     24   1.1       cgd  *    without specific prior written permission.
     25   1.1       cgd  *
     26   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27   1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28   1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29   1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30   1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31   1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32   1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33   1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34   1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35   1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36   1.1       cgd  * SUCH DAMAGE.
     37   1.1       cgd  */
     38   1.1       cgd 
     39   1.7     lukem #include <sys/cdefs.h>
     40   1.1       cgd #ifndef lint
     41   1.7     lukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\
     42   1.7     lukem 	The Regents of the University of California.  All rights reserved.\n");
     43   1.1       cgd #endif /* not lint */
     44   1.1       cgd 
     45   1.1       cgd #ifndef lint
     46   1.6     glass #if 0
     47   1.6     glass static char sccsid[] = "@(#)cal.c	8.4 (Berkeley) 4/2/94";
     48   1.6     glass #else
     49  1.15    atatat __RCSID("$NetBSD: cal.c,v 1.15 2003/06/05 00:21:20 atatat Exp $");
     50   1.6     glass #endif
     51   1.1       cgd #endif /* not lint */
     52   1.1       cgd 
     53   1.1       cgd #include <sys/types.h>
     54   1.6     glass 
     55   1.6     glass #include <ctype.h>
     56   1.6     glass #include <err.h>
     57  1.14      yamt #include <errno.h>
     58  1.14      yamt #include <limits.h>
     59   1.1       cgd #include <stdio.h>
     60   1.6     glass #include <stdlib.h>
     61   1.4       cgd #include <string.h>
     62  1.15    atatat #include <termcap.h>
     63   1.6     glass #include <time.h>
     64   1.8  christos #include <tzfile.h>
     65   1.6     glass #include <unistd.h>
     66   1.1       cgd 
     67   1.1       cgd #define	THURSDAY		4		/* for reformation */
     68   1.1       cgd #define	SATURDAY 		6		/* 1 Jan 1 was a Saturday */
     69   1.1       cgd 
     70   1.5        ws #define	FIRST_MISSING_DAY 	639799		/* 3 Sep 1752 */
     71   1.1       cgd #define	NUMBER_MISSING_DAYS 	11		/* 11 day correction */
     72   1.1       cgd 
     73   1.1       cgd #define	MAXDAYS			42		/* max slots in a month array */
     74   1.1       cgd #define	SPACE			-1		/* used in day array */
     75   1.1       cgd 
     76   1.1       cgd static int days_in_month[2][13] = {
     77   1.1       cgd 	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
     78   1.1       cgd 	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
     79   1.1       cgd };
     80   1.1       cgd 
     81   1.1       cgd int sep1752[MAXDAYS] = {
     82   1.1       cgd 	SPACE,	SPACE,	1,	2,	14,	15,	16,
     83   1.1       cgd 	17,	18,	19,	20,	21,	22,	23,
     84   1.1       cgd 	24,	25,	26,	27,	28,	29,	30,
     85   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     86   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     87   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     88   1.1       cgd }, j_sep1752[MAXDAYS] = {
     89   1.1       cgd 	SPACE,	SPACE,	245,	246,	258,	259,	260,
     90   1.1       cgd 	261,	262,	263,	264,	265,	266,	267,
     91   1.1       cgd 	268,	269,	270,	271,	272,	273,	274,
     92   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     93   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     94   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     95   1.1       cgd }, empty[MAXDAYS] = {
     96   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     97   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     98   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
     99   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
    100   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
    101   1.1       cgd 	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,	SPACE,
    102   1.1       cgd };
    103   1.1       cgd 
    104   1.1       cgd char *month_names[12] = {
    105   1.1       cgd 	"January", "February", "March", "April", "May", "June",
    106   1.1       cgd 	"July", "August", "September", "October", "November", "December",
    107   1.1       cgd };
    108   1.1       cgd 
    109   1.1       cgd char *day_headings = " S  M Tu  W Th  F  S";
    110   1.1       cgd char *j_day_headings = "  S   M  Tu   W  Th   F   S";
    111   1.1       cgd 
    112   1.1       cgd /* leap year -- account for gregorian reformation in 1752 */
    113   1.1       cgd #define	leap_year(yr) \
    114   1.1       cgd 	((yr) <= 1752 ? !((yr) % 4) : \
    115   1.9        ws 	(!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
    116   1.1       cgd 
    117   1.1       cgd /* number of centuries since 1700, not inclusive */
    118   1.1       cgd #define	centuries_since_1700(yr) \
    119   1.1       cgd 	((yr) > 1700 ? (yr) / 100 - 17 : 0)
    120   1.1       cgd 
    121   1.1       cgd /* number of centuries since 1700 whose modulo of 400 is 0 */
    122   1.1       cgd #define	quad_centuries_since_1700(yr) \
    123   1.1       cgd 	((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
    124   1.1       cgd 
    125   1.1       cgd /* number of leap years between year 1 and this year, not inclusive */
    126   1.1       cgd #define	leap_years_since_year_1(yr) \
    127   1.1       cgd 	((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
    128   1.1       cgd 
    129   1.1       cgd int julian;
    130  1.15    atatat int hilite;
    131  1.15    atatat char *md, *me;
    132   1.1       cgd 
    133  1.15    atatat void	init_hilite(void);
    134  1.14      yamt int	getnum(const char *);
    135  1.15    atatat int	ascii_day(char *, int);
    136  1.12     perry void	center(char *, int, int);
    137  1.12     perry void	day_array(int, int, int *);
    138  1.12     perry int	day_in_week(int, int, int);
    139  1.12     perry int	day_in_year(int, int, int);
    140  1.14      yamt void	monthrange(int, int, int, int, int);
    141  1.12     perry int	main(int, char **);
    142  1.12     perry void	trim_trailing_spaces(char *);
    143  1.12     perry void	usage(void);
    144   1.6     glass 
    145   1.6     glass int
    146  1.12     perry main(int argc, char **argv)
    147   1.1       cgd {
    148   1.1       cgd 	struct tm *local_time;
    149   1.6     glass 	time_t now;
    150  1.14      yamt 	int ch, month, year, yflag;
    151  1.14      yamt 	int before, after;
    152  1.14      yamt 	int yearly = 0;
    153   1.1       cgd 
    154  1.14      yamt 	before = after = 0;
    155   1.7     lukem 	yflag = year = 0;
    156  1.15    atatat 	while ((ch = getopt(argc, argv, "A:B:hjy3")) != -1) {
    157  1.12     perry 		switch (ch) {
    158  1.14      yamt 		case 'A':
    159  1.14      yamt 			after = getnum(optarg);
    160  1.14      yamt 			break;
    161  1.14      yamt 		case 'B':
    162  1.14      yamt 			before = getnum(optarg);
    163  1.14      yamt 			break;
    164  1.15    atatat 		case 'h':
    165  1.15    atatat 			init_hilite();
    166  1.15    atatat 			break;
    167   1.1       cgd 		case 'j':
    168   1.1       cgd 			julian = 1;
    169   1.1       cgd 			break;
    170   1.1       cgd 		case 'y':
    171   1.1       cgd 			yflag = 1;
    172   1.1       cgd 			break;
    173  1.13     perry 		case '3':
    174  1.14      yamt 			before = after = 1;
    175  1.13     perry 			break;
    176   1.1       cgd 		case '?':
    177   1.1       cgd 		default:
    178   1.1       cgd 			usage();
    179  1.12     perry 			/* NOTREACHED */
    180   1.1       cgd 		}
    181  1.12     perry 	}
    182  1.13     perry 
    183   1.1       cgd 	argc -= optind;
    184   1.1       cgd 	argv += optind;
    185   1.1       cgd 
    186   1.1       cgd 	month = 0;
    187  1.12     perry 	switch (argc) {
    188   1.1       cgd 	case 2:
    189   1.6     glass 		if ((month = atoi(*argv++)) < 1 || month > 12)
    190   1.6     glass 			errx(1, "illegal month value: use 1-12");
    191   1.1       cgd 		/* FALLTHROUGH */
    192   1.1       cgd 	case 1:
    193   1.6     glass 		if ((year = atoi(*argv)) < 1 || year > 9999)
    194   1.6     glass 			errx(1, "illegal year value: use 1-9999");
    195   1.1       cgd 		break;
    196   1.1       cgd 	case 0:
    197   1.1       cgd 		(void)time(&now);
    198   1.1       cgd 		local_time = localtime(&now);
    199   1.8  christos 		year = local_time->tm_year + TM_YEAR_BASE;
    200   1.1       cgd 		if (!yflag)
    201   1.1       cgd 			month = local_time->tm_mon + 1;
    202   1.1       cgd 		break;
    203   1.1       cgd 	default:
    204   1.1       cgd 		usage();
    205   1.1       cgd 	}
    206   1.1       cgd 
    207  1.14      yamt 	if (!month) {
    208  1.14      yamt 		/* yearly */
    209  1.14      yamt 		month = 1;
    210  1.14      yamt 		before = 0;
    211  1.14      yamt 		after = 11;
    212  1.14      yamt 		yearly = 1;
    213  1.14      yamt 	}
    214  1.14      yamt 
    215  1.14      yamt 	monthrange(month, year, before, after, yearly);
    216  1.13     perry 
    217   1.1       cgd 	exit(0);
    218   1.1       cgd }
    219   1.1       cgd 
    220   1.1       cgd #define	DAY_LEN		3		/* 3 spaces per day */
    221   1.1       cgd #define	J_DAY_LEN	4		/* 4 spaces per day */
    222   1.1       cgd #define	WEEK_LEN	20		/* 7 * 3 - one space at the end */
    223   1.1       cgd #define	J_WEEK_LEN	27		/* 7 * 4 - one space at the end */
    224   1.1       cgd #define	HEAD_SEP	2		/* spaces between day headings */
    225   1.1       cgd #define	J_HEAD_SEP	2
    226  1.14      yamt #define	MONTH_PER_ROW	3		/* how many monthes in a row */
    227  1.14      yamt #define	J_MONTH_PER_ROW	2
    228   1.1       cgd 
    229   1.6     glass void
    230  1.14      yamt monthrange(int month, int year, int before, int after, int yearly)
    231   1.1       cgd {
    232  1.14      yamt 	int startmonth, startyear;
    233  1.14      yamt 	int endmonth, endyear;
    234  1.14      yamt 	int i, row;
    235  1.14      yamt 	int days[3][MAXDAYS];
    236  1.15    atatat 	char lineout[256];
    237  1.14      yamt 	int inayear;
    238  1.14      yamt 	int newyear;
    239  1.14      yamt 	int day_len, week_len, head_sep;
    240  1.14      yamt 	int month_per_row;
    241  1.15    atatat 	int skip, r_off, w_off;
    242  1.14      yamt 
    243  1.14      yamt 	if (julian) {
    244  1.14      yamt 		day_len = J_DAY_LEN;
    245  1.14      yamt 		week_len = J_WEEK_LEN;
    246  1.14      yamt 		head_sep = J_HEAD_SEP;
    247  1.14      yamt 		month_per_row = J_MONTH_PER_ROW;
    248  1.14      yamt 	}
    249  1.14      yamt 	else {
    250  1.14      yamt 		day_len = DAY_LEN;
    251  1.14      yamt 		week_len = WEEK_LEN;
    252  1.14      yamt 		head_sep = HEAD_SEP;
    253  1.14      yamt 		month_per_row = MONTH_PER_ROW;
    254  1.14      yamt 	}
    255  1.14      yamt 
    256  1.14      yamt 	month--;
    257  1.14      yamt 
    258  1.14      yamt 	startyear = year - (before + 12 - 1 - month) / 12;
    259  1.14      yamt 	startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12);
    260  1.14      yamt 	endyear = year + (month + after) / 12;
    261  1.14      yamt 	endmonth = (month + after) % 12;
    262   1.1       cgd 
    263  1.14      yamt 	if (startyear < 0 || endyear > 9999) {
    264  1.14      yamt 		errx(1, "year should be in 1-9999\n");
    265   1.1       cgd 	}
    266   1.1       cgd 
    267  1.14      yamt 	year = startyear;
    268  1.14      yamt 	month = startmonth;
    269  1.14      yamt 	inayear = newyear = (year != endyear || yearly);
    270  1.14      yamt 	if (inayear) {
    271  1.14      yamt 		skip = month % month_per_row;
    272  1.14      yamt 		month -= skip;
    273  1.14      yamt 	}
    274  1.14      yamt 	else {
    275  1.14      yamt 		skip = 0;
    276  1.14      yamt 	}
    277  1.14      yamt 
    278  1.14      yamt 	do {
    279  1.14      yamt 		if (newyear) {
    280  1.14      yamt 			(void)snprintf(lineout, sizeof(lineout), "%d", year);
    281  1.14      yamt 			center(lineout, week_len * month_per_row +
    282  1.14      yamt 			    head_sep * (month_per_row - 1), 0);
    283  1.14      yamt 			(void)printf("\n\n");
    284  1.14      yamt 			newyear = 0;
    285  1.14      yamt 		}
    286  1.14      yamt 
    287  1.14      yamt 		for (i = 0; i < skip; i++)
    288  1.14      yamt 			center("", week_len, head_sep);
    289  1.14      yamt 
    290  1.14      yamt 		for (; i < month_per_row; i++) {
    291  1.14      yamt 			int sep;
    292  1.14      yamt 
    293  1.14      yamt 			if (year == endyear && month + i > endmonth)
    294  1.14      yamt 				break;
    295  1.14      yamt 
    296  1.14      yamt 			sep = (i == month_per_row - 1) ? 0 : head_sep;
    297  1.14      yamt 			day_array(month + i + 1, year, days[i]);
    298  1.14      yamt 			if (inayear) {
    299  1.14      yamt 				center(month_names[month + i], week_len, sep);
    300  1.14      yamt 			}
    301  1.14      yamt 			else {
    302  1.14      yamt 				snprintf(lineout, sizeof(lineout), "%s %d",
    303  1.14      yamt 				    month_names[month + i], year);
    304  1.14      yamt 				center(lineout, week_len, sep);
    305   1.1       cgd 			}
    306   1.1       cgd 		}
    307  1.14      yamt 		printf("\n");
    308  1.14      yamt 
    309  1.14      yamt 		for (i = 0; i < skip; i++)
    310  1.14      yamt 			center("", week_len, head_sep);
    311  1.14      yamt 
    312  1.14      yamt 		for (; i < month_per_row; i++) {
    313  1.14      yamt 			int sep;
    314  1.14      yamt 
    315  1.14      yamt 			if (year == endyear && month + i > endmonth)
    316  1.14      yamt 				break;
    317  1.14      yamt 
    318  1.14      yamt 			sep = (i == month_per_row - 1) ? 0 : head_sep;
    319  1.14      yamt 			printf("%s%*s",
    320  1.14      yamt 			    (julian) ? j_day_headings : day_headings, sep, "");
    321  1.14      yamt 		}
    322  1.14      yamt 		printf("\n");
    323   1.1       cgd 
    324  1.14      yamt 		memset(lineout, ' ', sizeof(lineout));
    325   1.1       cgd 		for (row = 0; row < 6; row++) {
    326  1.14      yamt 			char *p;
    327  1.14      yamt 			for (i = 0; i < skip; i++) {
    328  1.15    atatat 				p = lineout + i * (week_len + 2) + w_off;
    329  1.14      yamt 				memset(p, ' ', week_len);
    330  1.14      yamt 			}
    331  1.15    atatat 			w_off = 0;
    332  1.14      yamt 			for (; i < month_per_row; i++) {
    333  1.14      yamt 				int col, *dp;
    334  1.14      yamt 
    335  1.14      yamt 				if (year == endyear && month + i > endmonth)
    336  1.14      yamt 					break;
    337  1.14      yamt 
    338  1.15    atatat 				p = lineout + i * (week_len + 2) + w_off;
    339  1.14      yamt 				dp = &days[i][row * 7];
    340  1.15    atatat 				for (col = 0; col < 7;
    341  1.15    atatat 				     col++, p += day_len + r_off) {
    342  1.15    atatat 					r_off = ascii_day(p, *dp++);
    343  1.15    atatat 					w_off += r_off;
    344  1.15    atatat 				}
    345   1.1       cgd 			}
    346   1.3       cgd 			*p = '\0';
    347   1.1       cgd 			trim_trailing_spaces(lineout);
    348   1.1       cgd 			(void)printf("%s\n", lineout);
    349   1.1       cgd 		}
    350  1.13     perry 
    351  1.14      yamt 		skip = 0;
    352  1.14      yamt 		month += month_per_row;
    353  1.14      yamt 		if (month >= 12) {
    354  1.14      yamt 			month -= 12;
    355  1.14      yamt 			year++;
    356  1.14      yamt 			newyear = 1;
    357  1.13     perry 		}
    358  1.14      yamt 	} while (year < endyear || (year == endyear && month <= endmonth));
    359  1.13     perry }
    360  1.13     perry 
    361   1.1       cgd /*
    362   1.1       cgd  * day_array --
    363   1.1       cgd  *	Fill in an array of 42 integers with a calendar.  Assume for a moment
    364   1.1       cgd  *	that you took the (maximum) 6 rows in a calendar and stretched them
    365   1.1       cgd  *	out end to end.  You would have 42 numbers or spaces.  This routine
    366   1.1       cgd  *	builds that array for any month from Jan. 1 through Dec. 9999.
    367   1.1       cgd  */
    368   1.6     glass void
    369  1.12     perry day_array(int month, int year, int *days)
    370   1.1       cgd {
    371   1.6     glass 	int day, dw, dm;
    372  1.15    atatat 	time_t t;
    373  1.15    atatat 	struct tm *tm;
    374  1.15    atatat 
    375  1.15    atatat 	t = time(NULL);
    376  1.15    atatat 	tm = localtime(&t);
    377  1.15    atatat 	tm->tm_year += TM_YEAR_BASE;
    378  1.15    atatat 	tm->tm_mon++;
    379  1.15    atatat 	tm->tm_yday++; /* jan 1 is 1 for us, not 0 */
    380   1.1       cgd 
    381   1.1       cgd 	if (month == 9 && year == 1752) {
    382   1.6     glass 		memmove(days,
    383   1.6     glass 			julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
    384   1.1       cgd 		return;
    385   1.1       cgd 	}
    386   1.6     glass 	memmove(days, empty, MAXDAYS * sizeof(int));
    387   1.1       cgd 	dm = days_in_month[leap_year(year)][month];
    388   1.1       cgd 	dw = day_in_week(1, month, year);
    389   1.1       cgd 	day = julian ? day_in_year(1, month, year) : 1;
    390  1.15    atatat 	while (dm--) {
    391  1.15    atatat 		if (hilite && year == tm->tm_year &&
    392  1.15    atatat 		    (julian ? (day == tm->tm_yday) :
    393  1.15    atatat 		     (month == tm->tm_mon && day == tm->tm_mday)))
    394  1.15    atatat 			days[dw++] = -1 - day++;
    395  1.15    atatat 		else
    396  1.15    atatat 			days[dw++] = day++;
    397  1.15    atatat 	}
    398   1.1       cgd }
    399   1.1       cgd 
    400   1.1       cgd /*
    401   1.1       cgd  * day_in_year --
    402   1.1       cgd  *	return the 1 based day number within the year
    403   1.1       cgd  */
    404   1.6     glass int
    405  1.12     perry day_in_year(int day, int month, int year)
    406   1.1       cgd {
    407   1.6     glass 	int i, leap;
    408   1.1       cgd 
    409   1.1       cgd 	leap = leap_year(year);
    410   1.1       cgd 	for (i = 1; i < month; i++)
    411   1.1       cgd 		day += days_in_month[leap][i];
    412   1.6     glass 	return (day);
    413   1.1       cgd }
    414   1.1       cgd 
    415   1.1       cgd /*
    416   1.1       cgd  * day_in_week
    417   1.1       cgd  *	return the 0 based day number for any date from 1 Jan. 1 to
    418   1.1       cgd  *	31 Dec. 9999.  Assumes the Gregorian reformation eliminates
    419   1.1       cgd  *	3 Sep. 1752 through 13 Sep. 1752.  Returns Thursday for all
    420   1.1       cgd  *	missing days.
    421   1.1       cgd  */
    422   1.6     glass int
    423  1.12     perry day_in_week(int day, int month, int year)
    424   1.1       cgd {
    425   1.1       cgd 	long temp;
    426   1.1       cgd 
    427   1.1       cgd 	temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
    428   1.1       cgd 	    + day_in_year(day, month, year);
    429   1.1       cgd 	if (temp < FIRST_MISSING_DAY)
    430   1.6     glass 		return ((temp - 1 + SATURDAY) % 7);
    431   1.1       cgd 	if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
    432   1.6     glass 		return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
    433   1.6     glass 	return (THURSDAY);
    434   1.1       cgd }
    435   1.1       cgd 
    436  1.15    atatat int
    437  1.12     perry ascii_day(char *p, int day)
    438   1.1       cgd {
    439  1.15    atatat 	int display, val, rc;
    440  1.15    atatat 	char *b;
    441   1.1       cgd 	static char *aday[] = {
    442   1.1       cgd 		"",
    443   1.1       cgd 		" 1", " 2", " 3", " 4", " 5", " 6", " 7",
    444   1.1       cgd 		" 8", " 9", "10", "11", "12", "13", "14",
    445   1.1       cgd 		"15", "16", "17", "18", "19", "20", "21",
    446   1.1       cgd 		"22", "23", "24", "25", "26", "27", "28",
    447   1.1       cgd 		"29", "30", "31",
    448   1.1       cgd 	};
    449   1.1       cgd 
    450   1.1       cgd 	if (day == SPACE) {
    451   1.1       cgd 		memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
    452  1.15    atatat 		return (0);
    453   1.1       cgd 	}
    454  1.15    atatat 	if (day < 0) {
    455  1.15    atatat 		b = p;
    456  1.15    atatat 		day = -1 - day;
    457  1.15    atatat 	} else
    458  1.15    atatat 		b = NULL;
    459   1.1       cgd 	if (julian) {
    460   1.7     lukem 		if ((val = day / 100) != 0) {
    461   1.1       cgd 			day %= 100;
    462   1.1       cgd 			*p++ = val + '0';
    463   1.1       cgd 			display = 1;
    464   1.1       cgd 		} else {
    465   1.1       cgd 			*p++ = ' ';
    466   1.1       cgd 			display = 0;
    467   1.1       cgd 		}
    468   1.1       cgd 		val = day / 10;
    469   1.1       cgd 		if (val || display)
    470   1.1       cgd 			*p++ = val + '0';
    471   1.1       cgd 		else
    472   1.1       cgd 			*p++ = ' ';
    473   1.1       cgd 		*p++ = day % 10 + '0';
    474   1.1       cgd 	} else {
    475   1.1       cgd 		*p++ = aday[day][0];
    476   1.1       cgd 		*p++ = aday[day][1];
    477   1.1       cgd 	}
    478  1.15    atatat 
    479  1.15    atatat 	rc = 0;
    480  1.15    atatat 	if (b != NULL) {
    481  1.15    atatat 		char *t, h[64];
    482  1.15    atatat 		int l;
    483  1.15    atatat 
    484  1.15    atatat 		l = p - b;
    485  1.15    atatat 		memcpy(h, b, l);
    486  1.15    atatat 		p = b;
    487  1.15    atatat 
    488  1.15    atatat 		if (md != NULL) {
    489  1.15    atatat 			for (t = md; *t; rc++)
    490  1.15    atatat 				*p++ = *t++;
    491  1.15    atatat 			memcpy(p, h, l);
    492  1.15    atatat 			p += l;
    493  1.15    atatat 			for (t = me; *t; rc++)
    494  1.15    atatat 				*p++ = *t++;
    495  1.15    atatat 		} else {
    496  1.15    atatat 			for (t = &h[0]; l--; t++) {
    497  1.15    atatat 				*p++ = *t;
    498  1.15    atatat 				rc++;
    499  1.15    atatat 				*p++ = '\b';
    500  1.15    atatat 				rc++;
    501  1.15    atatat 				*p++ = *t;
    502  1.15    atatat 			}
    503  1.15    atatat 		}
    504  1.15    atatat 
    505  1.15    atatat 	}
    506  1.15    atatat 
    507   1.1       cgd 	*p = ' ';
    508  1.15    atatat 	return (rc);
    509   1.1       cgd }
    510   1.1       cgd 
    511   1.6     glass void
    512  1.12     perry trim_trailing_spaces(char *s)
    513   1.1       cgd {
    514   1.6     glass 	char *p;
    515   1.1       cgd 
    516   1.6     glass 	for (p = s; *p; ++p)
    517   1.6     glass 		continue;
    518  1.11  christos 	while (p > s && isspace((unsigned char)*--p))
    519   1.6     glass 		continue;
    520   1.1       cgd 	if (p > s)
    521   1.1       cgd 		++p;
    522   1.1       cgd 	*p = '\0';
    523   1.1       cgd }
    524   1.1       cgd 
    525   1.6     glass void
    526  1.12     perry center(char *str, int len, int separate)
    527   1.1       cgd {
    528   1.6     glass 
    529   1.1       cgd 	len -= strlen(str);
    530   1.1       cgd 	(void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
    531   1.1       cgd 	if (separate)
    532   1.1       cgd 		(void)printf("%*s", separate, "");
    533   1.1       cgd }
    534   1.1       cgd 
    535  1.14      yamt int
    536  1.14      yamt getnum(const char *p)
    537  1.14      yamt {
    538  1.14      yamt 	long result;
    539  1.14      yamt 	char *ep;
    540  1.14      yamt 
    541  1.14      yamt 	errno = 0;
    542  1.14      yamt 	result = strtoul(p, &ep, 10);
    543  1.14      yamt 	if (p[0] == '\0' || *ep != '\0')
    544  1.14      yamt 		goto error;
    545  1.14      yamt 	if (errno == ERANGE && result == ULONG_MAX)
    546  1.14      yamt 		goto error;
    547  1.14      yamt 	if (result > INT_MAX)
    548  1.14      yamt 		goto error;
    549  1.14      yamt 
    550  1.14      yamt 	return (int)result;
    551  1.14      yamt 
    552  1.14      yamt error:
    553  1.14      yamt 	errx(1, "bad number: %s", p);
    554  1.14      yamt 	/*NOTREACHED*/
    555  1.14      yamt }
    556  1.14      yamt 
    557   1.6     glass void
    558  1.15    atatat init_hilite(void)
    559  1.15    atatat {
    560  1.15    atatat 	static char control[128];
    561  1.15    atatat 	char cap[1024];
    562  1.15    atatat 	char *tc;
    563  1.15    atatat 
    564  1.15    atatat 	hilite++;
    565  1.15    atatat 
    566  1.15    atatat 	if (!isatty(fileno(stdout)))
    567  1.15    atatat 		return;
    568  1.15    atatat 
    569  1.15    atatat 	tc = getenv("TERM");
    570  1.15    atatat 	if (tc == NULL)
    571  1.15    atatat 		tc = "dumb";
    572  1.15    atatat 	if (tgetent(&cap[0], tc) != 1)
    573  1.15    atatat 		return;
    574  1.15    atatat 
    575  1.15    atatat 	tc = &control[0];
    576  1.15    atatat 	if ((md = tgetstr(hilite > 1 ? "mr" : "md", &tc)))
    577  1.15    atatat 		*tc++ = '\0';
    578  1.15    atatat 	if ((me = tgetstr("me", &tc)))
    579  1.15    atatat 		*tc++ = '\0';
    580  1.15    atatat 	if (me == NULL || md == NULL)
    581  1.15    atatat 		md = me = NULL;
    582  1.15    atatat }
    583  1.15    atatat 
    584  1.15    atatat void
    585  1.12     perry usage(void)
    586   1.1       cgd {
    587   1.6     glass 
    588  1.14      yamt 	(void)fprintf(stderr,
    589  1.15    atatat 	    "usage: cal [-hjy3] [-B before] [-A after] [[month] year]\n");
    590   1.1       cgd 	exit(1);
    591   1.1       cgd }
    592