ncal.c revision 32215
1198157Srrs/*-
2198157Srrs * Copyright (c) 1997 Wolfgang Helbig
3198157Srrs * All rights reserved.
4198157Srrs *
5198157Srrs * Redistribution and use in source and binary forms, with or without
6198157Srrs * modification, are permitted provided that the following conditions
7198157Srrs * are met:
8198157Srrs * 1. Redistributions of source code must retain the above copyright
9198157Srrs *    notice, this list of conditions and the following disclaimer.
10198157Srrs * 2. Redistributions in binary form must reproduce the above copyright
11198157Srrs *    notice, this list of conditions and the following disclaimer in the
12198157Srrs *    documentation and/or other materials provided with the distribution.
13198157Srrs *
14198157Srrs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15198157Srrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16198157Srrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17198157Srrs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18198157Srrs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19198157Srrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20198157Srrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21198157Srrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22198157Srrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23198157Srrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24198157Srrs * SUCH DAMAGE.
25198157Srrs *
26198157Srrs *	$Id: ncal.c,v 1.2 1997/12/31 15:55:08 helbig Exp $
27198157Srrs */
28198157Srrs#include <calendar.h>
29202066Simp#include <err.h>
30202066Simp#include <locale.h>
31198157Srrs#include <stdio.h>
32202066Simp#include <stdlib.h>
33202066Simp#include <string.h>
34202066Simp#include <sysexits.h>
35198157Srrs#include <time.h>
36198157Srrs#include <unistd.h>
37198157Srrs
38198157Srrs/* Width of one month with backward compatibility */
39198157Srrs#define MONTH_WIDTH_B_J 27
40198157Srrs#define MONTH_WIDTH_B 20
41198157Srrs
42198157Srrs#define MONTH_WIDTH_J 24
43198157Srrs#define MONTH_WIDTH 18
44198157Srrs
45198157Srrs#define MAX_WIDTH 28
46198157Srrs
47198157Srrstypedef struct date date;
48198157Srrs
49198157Srrsstruct monthlines {
50198157Srrs	char name[MAX_WIDTH + 1];
51198157Srrs	char lines[7][MAX_WIDTH + 1];
52198157Srrs	char weeks[MAX_WIDTH + 1];
53198157Srrs};
54198157Srrs
55198157Srrsstruct weekdays {
56198157Srrs	char names[7][4];
57208165Srrs};
58211994Sjchandra
59198157Srrs/* The switches from Julian to Gregorian in some countries */
60198157Srrsstatic struct djswitch {
61198157Srrs	char *cc;	/* Country code according to ISO 3166 */
62198157Srrs	char *nm;	/* Name of country */
63198157Srrs	date dt;	/* Last day of Julian calendar */
64198157Srrs	char *efmt;	/* strftime format for printing date of easter */
65198157Srrs} switches[] = {
66198157Srrs	{"AL", "Albania",	{1912, 11, 30}, "%e %B %Y"},
67198157Srrs	{"AT", "Austria",	{1582, 10,  4}, "%e. %B %Y"},
68198157Srrs	{"AU", "Australia",	{1752,  9,  2}, "%B %e, %Y"},
69198157Srrs	{"BG", "Bulgaria",	{1916,  3, 18}, "%e %B %Y"},
70198157Srrs	{"CA", "Canada",	{1752,  9,  2}, "%B %e, %Y"},
71198157Srrs	{"CH", "Switzerland",	{1655,  2, 28}, "%e. %B %Y"},
72198157Srrs	{"CN", "China",		{1911, 12, 18}, "%e %B %Y"},
73198157Srrs	{"CZ", "Czech Republic",{1584,  1,  6}, "%e %B %Y"},
74198157Srrs	{"DE", "Germany",	{1700,  2, 18}, "%e. %B %Y"},
75198157Srrs	{"DK", "Denmark",	{1700,  2, 18}, "%e. %B %Y"},
76198157Srrs	{"ES", "Spain",		{1582, 10,  4}, "%e de %B de %Y"},
77198157Srrs	{"FR", "France",	{1582, 12,  9}, "%e. %B %Y"},
78198157Srrs	{"GB", "United Kingdom",{1752,  9,  2}, "%e %B %Y"},
79198157Srrs	{"GR", "Greece",	{1924,  3,  9}, "%e %B %Y"},
80198157Srrs	{"HU", "Hungary",	{1587, 10, 21}, "%e %B %Y"},
81198789Srrs	{"IS", "Iceland",	{1700, 11, 16}, "%e %B %Y"},
82198157Srrs	{"IT", "Italy",		{1582, 10,  4}, "%e %B %Y"},
83198157Srrs	{"JP", "Japan",		{1918, 12, 18}, "%Y\x94N %B%e"},
84198157Srrs	{"LV", "Latvia",	{1918,  2,  1}, "%e %B %Y"},
85198157Srrs	{"LI", "Lithuania",	{1918,  2,  1}, "%e %B %Y"},
86198157Srrs	{"NL", "Netherlands",	{1701,  4, 30}, "%e %B %Y"},
87208165Srrs	{"NO", "Norway",	{1700,  2, 18}, "%e %B %Y"},
88198626Srrs	{"PL", "Poland",	{1582, 10,  4}, "%e %B %Y"},
89198157Srrs	{"PT", "Portugal",	{1582, 10,  4}, "%e %B %Y"},
90211994Sjchandra	{"RO", "Romania",	{1920,  3,  4}, "%e %B %Y"},
91211994Sjchandra	{"RU", "Russia",	{1920,  3,  4}, "%e %B %Y"},
92211994Sjchandra	{"SI", "Slovenia",	{1919,  3,  4}, "%e %B %Y"},
93211994Sjchandra	{"SU", "USSR",		{1920,  3,  4}, "%e %B %Y"},
94211994Sjchandra	{"SW", "Sweden",	{1753,  2, 17}, "%e %B %Y"},
95198608Srrs	{"TR", "Turkey",	{1926, 12, 18}, "%e %B %Y"},
96198608Srrs	{"US", "United States",	{1752,  9,  2}, "%B %e, %Y"},
97198608Srrs	{"YU", "Yugoslavia",	{1919,  3,  4}, "%e %B %Y"}
98198608Srrs};
99211994Sjchandra
100211994Sjchandrastruct djswitch *dftswitch =
101198608Srrs    switches + sizeof(switches) / sizeof(struct djswitch) - 2;
102211996Sjchandra    /* default switch (should be "US") */
103211996Sjchandra
104202066Simp/* Table used to print day of month and week numbers */
105202066Simpchar daystr[] = "     1  2  3  4  5  6  7  8  9 10 11 12 13 14 15"
106202092Simp		" 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31"
107198157Srrs		" 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47"
108198608Srrs		" 48 49 50 51 52 53";
109198608Srrs
110198157Srrs/* Table used to print day of year and week numbers */
111198157Srrschar jdaystr[] = "       1   2   3   4   5   6   7   8   9"
112198157Srrs		 "  10  11  12  13  14  15  16  17  18  19"
113198157Srrs		 "  20  21  22  23  24  25  26  27  28  29"
114198157Srrs		 "  30  31  32  33  34  35  36  37  38  39"
115198157Srrs		 "  40  41  42  43  44  45  46  47  48  49"
116198157Srrs		 "  50  51  52  53  54  55  56  57  58  59"
117198157Srrs		 "  60  61  62  63  64  65  66  67  68  69"
118198157Srrs		 "  70  71  72  73  74  75  76  77  78  79"
119198157Srrs		 "  80  81  82  83  84  85  86  87  88  89"
120198626Srrs		 "  90  91  92  93  94  95  96  97  98  99"
121198626Srrs		 " 100 101 102 103 104 105 106 107 108 109"
122198157Srrs		 " 110 111 112 113 114 115 116 117 118 119"
123198157Srrs		 " 120 121 122 123 124 125 126 127 128 129"
124198157Srrs		 " 130 131 132 133 134 135 136 137 138 139"
125198157Srrs		 " 140 141 142 143 144 145 146 147 148 149"
126208369Sjchandra		 " 150 151 152 153 154 155 156 157 158 159"
127198157Srrs		 " 160 161 162 163 164 165 166 167 168 169"
128198157Srrs		 " 170 171 172 173 174 175 176 177 178 179"
129198157Srrs		 " 180 181 182 183 184 185 186 187 188 189"
130198157Srrs		 " 190 191 192 193 194 195 196 197 198 199"
131198157Srrs		 " 200 201 202 203 204 205 206 207 208 209"
132198157Srrs		 " 210 211 212 213 214 215 216 217 218 219"
133198157Srrs		 " 220 221 222 223 224 225 226 227 228 229"
134198626Srrs		 " 230 231 232 233 234 235 236 237 238 239"
135198626Srrs		 " 240 241 242 243 244 245 246 247 248 249"
136198157Srrs		 " 250 251 252 253 254 255 256 257 258 259"
137198157Srrs		 " 260 261 262 263 264 265 266 267 268 269"
138198157Srrs		 " 270 271 272 273 274 275 276 277 278 279"
139198157Srrs		 " 280 281 282 283 284 285 286 287 288 289"
140198157Srrs		 " 290 291 292 293 294 295 296 297 298 299"
141198157Srrs		 " 300 301 302 303 304 305 306 307 308 309"
142198157Srrs		 " 310 311 312 313 314 315 316 317 318 319"
143198157Srrs		 " 320 321 322 323 324 325 326 327 328 329"
144198157Srrs		 " 330 331 332 333 334 335 336 337 338 339"
145198157Srrs		 " 340 341 342 343 344 345 346 347 348 349"
146198157Srrs		 " 350 351 352 353 354 355 356 357 358 359"
147198157Srrs		 " 360 361 362 363 364 365 366";
148198157Srrs
149198157Srrsint     flag_weeks;		/* user wants number of week */
150198157Srrsint     nswitch;		/* user defined switch date */
151198157Srrsint	nswitchb;		/* switch date for backward compatibility */
152198157Srrschar   *efmt;			/* strftime format string for printeaster() */
153198157Srrs
154198157Srrschar   *center(char *s, char *t, int w);
155198157Srrsvoid	mkmonth(int year, int month, int jd_flag, struct monthlines * monthl);
156198157Srrsvoid    mkmonthb(int year, int month, int jd_flag, struct monthlines * monthl);
157198157Srrsvoid    mkweekdays(struct weekdays * wds);
158198157Srrsvoid    printcc(void);
159198157Srrsvoid    printeaster(int year, int julian, int orthodox);
160198157Srrsvoid    printmonth(int year, int month, int jd_flag);
161198157Srrsvoid    printmonthb(int year, int month, int jd_flag);
162198157Srrsvoid    printyear(int year, int jd_flag);
163198157Srrsvoid    printyearb(int year, int jd_flag);
164198157Srrsdate   *sdate(int ndays, struct date * d);
165198157Srrsdate   *sdateb(int ndays, struct date * d);
166198157Srrsint     sndays(struct date * d);
167198157Srrsint     sndaysb(struct date * d);
168198157Srrsvoid    usage(void);
169198157Srrsint     weekdayb(int nd);
170198626Srrs
171198626Srrsint
172198157Srrsmain(int argc, char *argv[])
173198157Srrs{
174198157Srrs	struct  djswitch *p, *q;	/* to search user defined switch date */
175198626Srrs	date	never = {10000, 1, 1};	/* outside valid range of dates */
176198626Srrs	date	ukswitch = {1752, 9, 2};/* switch date for Great Britain */
177198157Srrs	int     ch;			/* holds the option character */
178198157Srrs	int     m = 0;			/* month */
179198157Srrs	int	y = 0;			/* year */
180198157Srrs	int     flag_backward = 0;	/* user called cal--backward compat. */
181198157Srrs	int     flag_hole_year = 0;	/* user wants the whole year */
182198157Srrs	int	flag_julian_cal = 0;	/* user wants Julian Calendar */
183198157Srrs	int     flag_julian_day = 0;	/* user wants the Julian day
184198157Srrs					 * numbers */
185198157Srrs	int	flag_orthodox = 0;	/* use wants Orthodox easter */
186198157Srrs	int	flag_easter = 0;	/* use wants easter date */
187198626Srrs	char	*cp;			/* character pointer */
188198157Srrs	char    *locale;		/* locale to get country code */
189198157Srrs
190198626Srrs	/*
191198626Srrs	 * Use locale to determine the country code,
192198626Srrs	 * and use the country code to determine the default
193198626Srrs	 * switchdate and date format from the switches table.
194198626Srrs	 */
195198626Srrs	if ((locale = setlocale(LC_TIME, "")) == NULL)
196198626Srrs		warn("setlocale");
197198626Srrs	if (locale == NULL || locale == "C")
198198626Srrs		locale = "_US";
199198626Srrs	q = switches + sizeof(switches) / sizeof(struct djswitch);
200198626Srrs	for (p = switches; p != q; p++)
201198626Srrs		if ((cp = strstr(locale, p->cc)) != NULL && *(cp - 1) == '_')
202198157Srrs			break;
203198157Srrs	if (p == q) {
204198157Srrs		nswitch = ndaysj(&dftswitch->dt);
205208165Srrs		efmt = dftswitch->efmt;
206208165Srrs	} else {
207208165Srrs		nswitch = ndaysj(&p->dt);
208208165Srrs		efmt = p->efmt;
209208165Srrs		dftswitch = p;
210208165Srrs	}
211208165Srrs
212208165Srrs
213208165Srrs	/*
214198157Srrs	 * Get the filename portion of argv[0] and set flag_backward if
215198157Srrs	 * this program is called "cal".
216198157Srrs	 */
217198157Srrs	for (cp = argv[0]; *cp; cp++)
218198157Srrs		;
219198157Srrs	while (cp >= argv[0] && *cp != '/')
220198157Srrs		cp--;
221198157Srrs	if (strcmp("cal", ++cp) == 0)
222198157Srrs		flag_backward = 1;
223198157Srrs
224198157Srrs	/* Set the switch date to United Kingdom if backwards compatible */
225198157Srrs	if (flag_backward)
226198157Srrs		nswitchb = ndaysj(&ukswitch);
227198626Srrs
228198157Srrs	while ((ch = getopt(argc, argv, "Jejops:wy")) != -1)
229198626Srrs		switch (ch) {
230198157Srrs		case 'J':
231198626Srrs			if (flag_backward)
232198157Srrs				usage();
233198157Srrs			nswitch = ndaysj(&never);
234198157Srrs			flag_julian_cal = 1;
235198626Srrs			break;
236198626Srrs		case 'e':
237198157Srrs			if (flag_backward)
238198626Srrs				usage();
239198157Srrs			flag_easter = 1;
240198157Srrs			break;
241198626Srrs		case 'j':
242198626Srrs			flag_julian_day = 1;
243198626Srrs			break;
244198626Srrs		case 'o':
245198626Srrs			if (flag_backward)
246198626Srrs				usage();
247198626Srrs			flag_orthodox = 1;
248198626Srrs			flag_easter = 1;
249198626Srrs			break;
250198626Srrs		case 'p':
251198626Srrs			if (flag_backward)
252198626Srrs				usage();
253198626Srrs			printcc();
254198626Srrs			return (0);
255198626Srrs			break;
256198157Srrs		case 's':
257198157Srrs			if (flag_backward)
258198626Srrs				usage();
259198626Srrs			q = switches +
260198157Srrs			    sizeof(switches) / sizeof(struct djswitch);
261198626Srrs			for (p = switches;
262198626Srrs			     p != q && strcmp(p->cc, optarg) != 0; p++)
263198626Srrs				;
264198626Srrs			if (p == q)
265198626Srrs				errx(EX_USAGE,
266198626Srrs				    "%s: invalid country code", optarg);
267198626Srrs			nswitch = ndaysj(&(p->dt));
268198626Srrs			break;
269198626Srrs		case 'w':
270198626Srrs			if (flag_backward)
271198626Srrs				usage();
272198626Srrs			flag_weeks = 1;
273198626Srrs			break;
274198157Srrs		case 'y':
275198626Srrs			flag_hole_year = 1;
276198626Srrs			break;
277198626Srrs		default:
278198626Srrs			usage();
279198626Srrs		}
280198626Srrs
281198157Srrs	argc -= optind;
282198626Srrs	argv += optind;
283198626Srrs
284198157Srrs	if (argc == 0) {
285198626Srrs		time_t t;
286198626Srrs		struct tm *tm;
287198626Srrs
288198626Srrs		t = time(NULL);
289198626Srrs		tm = localtime(&t);
290198626Srrs		y = tm->tm_year + 1900;
291198157Srrs		m = tm->tm_mon + 1;
292198626Srrs	}
293198157Srrs
294198157Srrs	switch (argc) {
295198157Srrs	case 2:
296198157Srrs		if (flag_easter)
297198157Srrs			usage();
298198626Srrs		m = atoi(*argv++);
299198157Srrs		if (m < 1 || m > 12)
300198157Srrs			errx(EX_USAGE, "month %d not in range 1..12", m);
301198626Srrs		/* FALLTHROUGH */
302198157Srrs	case 1:
303198626Srrs		y = atoi(*argv++);
304198157Srrs		if (y < 1 || y > 9999)
305198626Srrs			errx(EX_USAGE, "year %d not in range 1..9999", y);
306198157Srrs		break;
307198157Srrs	case 0:
308198157Srrs		break;
309198157Srrs	default:
310198157Srrs		usage();
311198157Srrs	}
312198157Srrs
313198157Srrs	if (flag_easter)
314198157Srrs		printeaster(y, flag_julian_cal, flag_orthodox);
315198157Srrs	else if (argc == 1 || flag_hole_year)
316198157Srrs		if (flag_backward)
317198157Srrs			printyearb(y, flag_julian_day);
318198608Srrs		else
319198157Srrs			printyear(y, flag_julian_day);
320198157Srrs	else
321198157Srrs		if (flag_backward)
322198157Srrs			printmonthb(y, m, flag_julian_day);
323198626Srrs		else
324198157Srrs			printmonth(y, m, flag_julian_day);
325198157Srrs
326198157Srrs	return (0);
327198157Srrs}
328198157Srrs
329198157Srrsvoid
330198157Srrsusage(void)
331198157Srrs{
332198157Srrs
333198157Srrs	fprintf(stderr,
334198157Srrs	    "usage: cal [-jy] [month[year]]\n"
335198157Srrs	    "usage: ncal [-Jjpwy] [-s country_code] [[month] year]\n"
336198157Srrs	    "usage: ncal [-Jeo] [year]\n");
337198157Srrs	exit(EX_USAGE);
338198157Srrs}
339198157Srrs
340198157Srrs/* print the assumed switches for all countries */
341198626Srrsvoid
342198157Srrsprintcc(void)
343198157Srrs{
344198157Srrs	struct djswitch *p;
345198626Srrs	int n;	/* number of lines to print */
346198157Srrs	int m;	/* offset from left to right table entry on the same line */
347198626Srrs
348198157Srrs#define FSTR "%c%s %-15s%4d-%02d-%02d"
349198157Srrs#define DFLT(p) ((p) == dftswitch ? '*' : ' ')
350198157Srrs#define FSTRARG(p) DFLT(p), (p)->cc, (p)->nm, (p)->dt.y, (p)->dt.m, (p)->dt.d
351198157Srrs
352198157Srrs	n = sizeof(switches) / sizeof(struct djswitch);
353198157Srrs	m = (n + 1) / 2;
354198157Srrs	n /= 2;
355198157Srrs	for (p = switches; p != switches + n; p++)
356198157Srrs		printf(FSTR"     "FSTR"\n", FSTRARG(p), FSTRARG(p+m));
357198626Srrs	if (m != n)
358198157Srrs		printf(FSTR"\n", FSTRARG(p));
359198626Srrs}
360198626Srrs
361198626Srrs/* print the date of easter sunday */
362198626Srrsvoid
363198626Srrsprinteaster(int y, int julian, int orthodox)
364198626Srrs{
365198626Srrs	date    dt;
366198626Srrs	struct tm tm;
367198626Srrs	char    buf[80];
368198626Srrs
369198157Srrs	/* force orthodox easter for years before 1583 */
370198157Srrs	if (y < 1583)
371199139Srrs		orthodox = 1;
372198626Srrs
373198626Srrs	if (orthodox)
374198626Srrs		if (julian)
375198626Srrs			easteroj(y, &dt);
376198626Srrs		else
377198626Srrs			easterog(y, &dt);
378198626Srrs	else
379198626Srrs		easterg(y, &dt);
380198626Srrs
381198626Srrs	memset(&tm, 0, sizeof(tm));
382198626Srrs	tm.tm_year = dt.y - 1900;
383198157Srrs	tm.tm_mon  = dt.m - 1;
384198157Srrs	tm.tm_mday = dt.d;
385198157Srrs	strftime(buf, sizeof(buf), efmt,  &tm);
386198157Srrs	printf("%s\n", buf);
387198626Srrs}
388198626Srrs
389198157Srrsvoid
390198157Srrsprintmonth(int y, int m, int jd_flag)
391198157Srrs{
392198157Srrs	struct monthlines month;
393198157Srrs	struct weekdays wds;
394198157Srrs	int i;
395198157Srrs
396198157Srrs	mkmonth(y, m - 1, jd_flag, &month);
397198157Srrs	mkweekdays(&wds);
398198157Srrs	printf("    %s %d\n", month.name, y);
399198626Srrs	for (i = 0; i != 7; i++)
400198626Srrs		printf("%.2s%s\n", wds.names[i], month.lines[i]);
401198157Srrs	if (flag_weeks)
402198157Srrs		printf("  %s\n", month.weeks);
403198157Srrs}
404208369Sjchandra
405198157Srrsvoid
406198157Srrsprintmonthb(int y, int m, int jd_flag)
407198157Srrs{
408208165Srrs	struct monthlines month;
409198626Srrs	struct weekdays wds;
410198626Srrs	char s[MAX_WIDTH], t[MAX_WIDTH];
411198157Srrs	int i;
412198157Srrs	int mw;
413198157Srrs
414198157Srrs	mkmonthb(y, m - 1, jd_flag, &month);
415198626Srrs	mkweekdays(&wds);
416198626Srrs
417198626Srrs	mw = jd_flag ? MONTH_WIDTH_B_J : MONTH_WIDTH_B;
418198157Srrs
419198626Srrs	sprintf(s, "%s %d", month.name, y);
420198157Srrs	printf("%s\n", center(t, s, mw));
421198157Srrs
422198157Srrs	if (jd_flag)
423198157Srrs		printf(" %s %s %s %s %s %s %.2s\n", wds.names[6], wds.names[0],
424198157Srrs			wds.names[1], wds.names[2], wds.names[3],
425198157Srrs			wds.names[4], wds.names[5]);
426198626Srrs	else
427198626Srrs		printf("%s%s%s%s%s%s%.2s\n", wds.names[6], wds.names[0],
428198157Srrs			wds.names[1], wds.names[2], wds.names[3],
429198157Srrs			wds.names[4], wds.names[5]);
430198626Srrs
431198626Srrs	for (i = 0; i != 6; i++)
432198157Srrs		printf("%s\n", month.lines[i]+1);
433198626Srrs}
434198157Srrs
435198157Srrsvoid
436198157Srrsprintyear(int y, int jd_flag)
437198157Srrs{
438198157Srrs	struct monthlines year[12];
439198626Srrs	struct weekdays wds;
440198626Srrs	char    s[80], t[80];
441198157Srrs	int     i, j;
442198157Srrs	int     mpl;
443198157Srrs	int     mw;
444198157Srrs
445198626Srrs	for (i = 0; i != 12; i++)
446198157Srrs		mkmonth(y, i, jd_flag, year + i);
447198157Srrs	mkweekdays(&wds);
448198157Srrs	mpl = jd_flag ? 3 : 4;
449198157Srrs	mw = jd_flag ? MONTH_WIDTH_J : MONTH_WIDTH;
450198157Srrs
451198157Srrs	sprintf(s, "%d", y);
452198157Srrs	printf("%s\n", center(t, s, mpl * mw));
453198157Srrs
454198157Srrs	for (j = 0; j != 12; j += mpl) {
455198157Srrs		printf("    %-*s%-*s",
456198157Srrs		    mw, year[j].name,
457198626Srrs		    mw, year[j + 1].name);
458198157Srrs		if (mpl == 3)
459198157Srrs			printf("%s\n", year[j + 2].name);
460198157Srrs		else
461198157Srrs			printf("%-*s%s\n",
462198157Srrs		    	    mw, year[j + 2].name,
463198157Srrs		    	    year[j + 3].name);
464198157Srrs		for (i = 0; i != 7; i++) {
465198157Srrs			printf("%.2s%-*s%-*s",
466208165Srrs			    wds.names[i],
467198157Srrs			    mw, year[j].lines[i],
468198157Srrs			    mw, year[j + 1].lines[i]);
469198157Srrs			if (mpl == 3)
470198157Srrs				printf("%s\n", year[j + 2].lines[i]);
471198157Srrs			else
472198157Srrs				printf("%-*s%s\n",
473198157Srrs			    	    mw, year[j + 2].lines[i],
474198157Srrs			    	    year[j + 3].lines[i]);
475198626Srrs		}
476198626Srrs		if (flag_weeks)
477198157Srrs			if (mpl == 3)
478198157Srrs				printf("  %-*s%-*s%-s\n",
479198157Srrs				    mw, year[j].weeks,
480198157Srrs				    mw, year[j + 1].weeks,
481198157Srrs				    year[j + 2].weeks);
482198626Srrs			else
483198157Srrs				printf("  %-*s%-*s%-*s%-s\n",
484198157Srrs				    mw, year[j].weeks,
485198157Srrs				    mw, year[j + 1].weeks,
486208165Srrs				    mw, year[j + 2].weeks,
487198157Srrs				    year[j + 3].weeks);
488198157Srrs	}
489198157Srrs}
490198157Srrs
491198157Srrsvoid
492198157Srrsprintyearb(int y, int jd_flag)
493198157Srrs{
494198157Srrs	struct monthlines year[12];
495198157Srrs	struct weekdays wds;
496198157Srrs	char	s[80], t[80];
497198157Srrs	int     i, j;
498198157Srrs	int     mpl;
499198157Srrs	int     mw;
500198626Srrs
501198157Srrs	for (i = 0; i != 12; i++)
502198157Srrs		mkmonthb(y, i, jd_flag, year + i);
503198626Srrs	mkweekdays(&wds);
504198626Srrs	mpl = jd_flag ? 2 : 3;
505198626Srrs	mw = jd_flag ? MONTH_WIDTH_B_J : MONTH_WIDTH_B;
506198626Srrs
507198626Srrs	sprintf(s, "%d", y);
508198626Srrs	printf("%s\n\n", center(t, s, mw * mpl + mpl));
509198626Srrs
510198626Srrs	for (j = 0; j != 12; j += mpl) {
511198626Srrs		printf("%-*s  ", mw, center(s, year[j].name, mw));
512208165Srrs		if (mpl == 2)
513198157Srrs			printf("%s\n", center(s, year[j + 1].name, mw));
514198157Srrs		else
515198157Srrs			printf("%-*s  %s\n", mw,
516198157Srrs			    center(s, year[j + 1].name, mw),
517198157Srrs			    center(t, year[j + 2].name, mw));
518198157Srrs
519198157Srrs		if (mpl == 2)
520198157Srrs			printf(" %s %s %s %s %s %s %s "
521198157Srrs			       " %s %s %s %s %s %s %.2s\n",
522198157Srrs				wds.names[6], wds.names[0], wds.names[1],
523198157Srrs				wds.names[2], wds.names[3], wds.names[4],
524198157Srrs				wds.names[5],
525198157Srrs				wds.names[6], wds.names[0], wds.names[1],
526198626Srrs				wds.names[2], wds.names[3], wds.names[4],
527198157Srrs				wds.names[5]);
528198157Srrs		else
529198626Srrs			printf("%s%s%s%s%s%s%s "
530198157Srrs				"%s%s%s%s%s%s%s "
531198626Srrs				"%s%s%s%s%s%s%.2s\n",
532198157Srrs				wds.names[6], wds.names[0], wds.names[1],
533198626Srrs				wds.names[2], wds.names[3], wds.names[4],
534198157Srrs				wds.names[5],
535198157Srrs				wds.names[6], wds.names[0], wds.names[1],
536203754Srrs				wds.names[2], wds.names[3], wds.names[4],
537203754Srrs				wds.names[5],
538198157Srrs				wds.names[6], wds.names[0], wds.names[1],
539198157Srrs				wds.names[2], wds.names[3], wds.names[4],
540198157Srrs				wds.names[5]);
541198157Srrs		for (i = 0; i != 6; i++) {
542198626Srrs			if (mpl == 2)
543198157Srrs				printf("%-*s  %s\n",
544198157Srrs			    mw, year[j].lines[i]+1,
545198157Srrs			    year[j + 1].lines[i]+1);
546198157Srrs			else
547198157Srrs				printf("%-*s  %-*s  %s\n",
548198608Srrs			    mw, year[j].lines[i]+1,
549198157Srrs			    mw, year[j + 1].lines[i]+1,
550198626Srrs			    year[j + 2].lines[i]+1);
551198157Srrs
552198157Srrs		}
553198157Srrs	}
554198157Srrs}
555198157Srrs
556198157Srrsvoid
557198626Srrsmkmonth(int y, int m, int jd_flag, struct monthlines *mlines)
558198157Srrs{
559198157Srrs
560198157Srrs	struct tm tm;		/* for strftime printing local names of
561198157Srrs				 * months */
562198157Srrs	date    dt;		/* handy date */
563198157Srrs	int     dw;		/* width of numbers */
564198626Srrs	int     first;		/* first day of month */
565198157Srrs	int     firstm;		/* first day of first week of month */
566198157Srrs	int     i, j, k;	/* just indices */
567210630Sjchandra	int     last;		/* the first day of next month */
568210630Sjchandra	int     jan1 = 0;	/* the first day of this year */
569198157Srrs	char   *ds;		/* pointer to day strings (daystr or
570198157Srrs				 * jdaystr) */
571198157Srrs
572198626Srrs	/* Set name of month. */
573198157Srrs	memset(&tm, 0, sizeof(tm));
574198157Srrs	tm.tm_mon = m;
575198157Srrs	strftime(mlines->name, sizeof(mlines->name), "%B", &tm);
576198626Srrs
577198157Srrs	/*
578198157Srrs	 * Set first and last to the day number of the first day of this
579199139Srrs	 * month and the first day of next month respectively. Set jan1 to
580198626Srrs	 * the day number of Jan 1st of this year.
581198157Srrs	 */
582199139Srrs	dt.y = y;
583209808Sjchandra	dt.m = m + 1;
584199139Srrs	dt.d = 1;
585199139Srrs	first = sndays(&dt);
586199139Srrs	if (m == 11) {
587199139Srrs		dt.y = y + 1;
588199139Srrs		dt.m = 1;
589198157Srrs		dt.d = 1;
590209808Sjchandra	} else {
591198157Srrs		dt.y = y;
592198157Srrs		dt.m = m + 2;
593198157Srrs		dt.d = 1;
594198157Srrs	}
595198157Srrs	last = sndays(&dt);
596198157Srrs
597198626Srrs	if (jd_flag) {
598198157Srrs		dt.y = y;
599198157Srrs		dt.m = 1;
600198626Srrs		dt.d = 1;
601198157Srrs		jan1 = sndays(&dt);
602198157Srrs	}
603198157Srrs
604198157Srrs	/*
605198157Srrs	 * Set firstm to the day number of monday of the first week of
606198157Srrs	 * this month. (This might be in the last month)
607198157Srrs	 */
608198157Srrs	firstm = first - weekday(first);
609198157Srrs
610198626Srrs	/* Set ds (daystring) and dw (daywidth) according to the jd_flag */
611198157Srrs	if (jd_flag) {
612198157Srrs		ds = jdaystr;
613198626Srrs		dw = 4;
614210630Sjchandra	} else {
615210630Sjchandra		ds = daystr;
616210630Sjchandra		dw = 3;
617198157Srrs	}
618210630Sjchandra
619210630Sjchandra	/*
620210630Sjchandra	 * Fill the lines with day of month or day of year (julian day)
621210630Sjchandra	 * line index: i, each line is one weekday. column index: j, each
622210630Sjchandra	 * column is one day number. print column index: k.
623210630Sjchandra	 */
624208165Srrs	for (i = 0; i != 7; i++) {
625210630Sjchandra		for (j = firstm + i, k = 0; j < last; j += 7, k += dw)
626210630Sjchandra			if (j >= first) {
627208165Srrs				if (jd_flag)
628210630Sjchandra					dt.d = j - jan1 + 1;
629208165Srrs				else
630199139Srrs					sdate(j, &dt);
631208165Srrs				memcpy(mlines->lines[i] + k,
632208165Srrs				       ds + dt.d * dw, dw);
633199139Srrs			} else
634199139Srrs				memcpy(mlines->lines[i] + k, "    ", dw);
635198157Srrs		mlines->lines[i][k] = '\0';
636198157Srrs
637198157Srrs	}
638198157Srrs
639198157Srrs	/* fill the weeknumbers */
640198157Srrs	if (flag_weeks) {
641198157Srrs		for (j = firstm, k = 0; j < last;  k += dw, j += 7)
642198626Srrs			if (j <= nswitch)
643210630Sjchandra				memset(mlines->weeks + k, ' ', dw);
644198157Srrs			else
645198626Srrs				memcpy(mlines->weeks + k,
646198157Srrs				    ds + week(j, &i)*dw, dw);
647198157Srrs		mlines->weeks[k] = '\0';
648198157Srrs	}
649198157Srrs}
650198157Srrs
651198157Srrsvoid
652209808Sjchandramkmonthb(int y, int m, int jd_flag, struct monthlines *mlines)
653210630Sjchandra{
654209808Sjchandra
655198157Srrs	struct tm tm;		/* for strftime printing local names of
656198157Srrs				 * months */
657198157Srrs	date    dt;		/* handy date */
658198157Srrs	int     dw;		/* width of numbers */
659198626Srrs	int     first;		/* first day of month */
660198626Srrs	int     firsts;		/* sunday of first week of month */
661198157Srrs	int     i, j, k;	/* just indices */
662198157Srrs	int     jan1 = 0;	/* the first day of this year */
663198157Srrs	int     last;		/* the first day of next month */
664198157Srrs	char   *ds;		/* pointer to day strings (daystr or
665198157Srrs				 * jdaystr) */
666198157Srrs
667198157Srrs	/* Set ds (daystring) and dw (daywidth) according to the jd_flag */
668198157Srrs	if (jd_flag) {
669198626Srrs		ds = jdaystr;
670198157Srrs		dw = 4;
671198157Srrs	} else {
672198626Srrs		ds = daystr;
673198626Srrs		dw = 3;
674198157Srrs	}
675198157Srrs
676198157Srrs	/* Set name of month centered */
677198157Srrs	memset(&tm, 0, sizeof(tm));
678198626Srrs	tm.tm_mon = m;
679198157Srrs	strftime(mlines->name, sizeof(mlines->name), "%B", &tm);
680198157Srrs
681198157Srrs	/*
682198157Srrs	 * Set first and last to the day number of the first day of this
683198157Srrs	 * month and the first day of next month respectively. Set jan1 to
684198157Srrs	 * the day number of Jan 1st of this year.
685198157Srrs	 */
686198157Srrs	dt.y = y;
687198157Srrs	dt.m = m + 1;
688198157Srrs	dt.d = 1;
689198157Srrs	first = sndaysb(&dt);
690198157Srrs	if (m == 11) {
691198157Srrs		dt.y = y + 1;
692198157Srrs		dt.m = 1;
693198157Srrs		dt.d = 1;
694198626Srrs	} else {
695198157Srrs		dt.y = y;
696198157Srrs		dt.m = m + 2;
697198157Srrs		dt.d = 1;
698198157Srrs	}
699198157Srrs	last = sndaysb(&dt);
700198157Srrs
701198157Srrs	if (jd_flag) {
702198157Srrs		dt.y = y;
703198157Srrs		dt.m = 1;
704198157Srrs		dt.d = 1;
705198157Srrs		jan1 = sndaysb(&dt);
706198157Srrs	}
707198157Srrs
708198157Srrs	/*
709198157Srrs	 * Set firsts to the day number of sunday of the first week of
710198626Srrs	 * this month. (This might be in the last month)
711198157Srrs	 */
712198626Srrs	firsts = first - (weekday(first)+1) % 7;
713198157Srrs
714212321Sjchandra	/*
715198626Srrs	 * Fill the lines with day of month or day of year (Julian day)
716212409Sjchandra	 * line index: i, each line is one week. column index: j, each
717212321Sjchandra	 * column is one day number. print column index: k.
718212409Sjchandra	 */
719212409Sjchandra	for (i = 0; i != 6; i++) {
720212409Sjchandra		for (j = firsts + 7 * i, k = 0; j < last && k != dw * 7;
721198157Srrs		     j++, k += dw)
722198626Srrs			if (j >= first) {
723198157Srrs				if (jd_flag)
724198157Srrs					dt.d = j - jan1 + 1;
725208165Srrs				else
726208165Srrs					sdateb(j, &dt);
727212321Sjchandra				memcpy(mlines->lines[i] + k,
728212321Sjchandra				       ds + dt.d * dw, dw);
729212321Sjchandra			} else
730212321Sjchandra				memcpy(mlines->lines[i] + k, "    ", dw);
731198157Srrs		if (k == 0)
732212321Sjchandra			mlines->lines[i][1] = '\0';
733212321Sjchandra		else
734212321Sjchandra			mlines->lines[i][k] = '\0';
735212321Sjchandra	}
736212321Sjchandra}
737212321Sjchandra
738212321Sjchandra/* Put the local names of weekdays into the wds */
739198157Srrsvoid
740198157Srrsmkweekdays(struct weekdays *wds)
741198157Srrs{
742198157Srrs	int i;
743198157Srrs	struct tm tm;
744198626Srrs
745198157Srrs	memset(&tm, 0, sizeof(tm));
746198157Srrs
747198626Srrs	for (i = 0; i != 7; i++) {
748198626Srrs		tm.tm_wday = (i+1) % 7;
749198626Srrs		strftime(wds->names[i], 4, "%a", &tm);
750198157Srrs		wds->names[i][2] = ' ';
751198157Srrs	}
752198157Srrs}
753198157Srrs
754198157Srrs/*
755198157Srrs * Compute the number of days from date, obey the local switch from
756198157Srrs * Julian to Gregorian if specified by the user.
757198157Srrs */
758198157Srrsint
759198157Srrssndays(struct date *d)
760198157Srrs{
761198157Srrs
762198157Srrs	if (nswitch != 0)
763198157Srrs		if (nswitch < ndaysj(d))
764198157Srrs			return (ndaysg(d));
765198626Srrs		else
766198157Srrs			return (ndaysj(d));
767198157Srrs	else
768198626Srrs		return ndaysg(d);
769198157Srrs}
770198157Srrs
771198157Srrs/*
772198157Srrs * Compute the number of days from date, obey the switch from
773198157Srrs * Julian to Gregorian as used by UK and her colonies.
774198157Srrs */
775198157Srrsint
776198157Srrssndaysb(struct date *d)
777198157Srrs{
778198157Srrs
779198157Srrs	if (nswitchb < ndaysj(d))
780198157Srrs		return (ndaysg(d));
781198157Srrs	else
782198157Srrs		return (ndaysj(d));
783198157Srrs}
784198157Srrs
785198157Srrs/* Inverse of sndays */
786198626Srrsstruct date *
787198626Srrssdate(int nd, struct date *d)
788198157Srrs{
789198626Srrs
790198157Srrs	if (nswitch < nd)
791198157Srrs		return (gdate(nd, d));
792198157Srrs	else
793198626Srrs		return (jdate(nd, d));
794198157Srrs}
795198157Srrs
796198626Srrs/* Inverse of sndaysb */
797198157Srrsstruct date *
798198157Srrssdateb(int nd, struct date *d)
799198157Srrs{
800198157Srrs
801198157Srrs	if (nswitchb < nd)
802198157Srrs		return (gdate(nd, d));
803198157Srrs	else
804198157Srrs		return (jdate(nd, d));
805198157Srrs}
806198157Srrs
807198157Srrs/* Center string t in string s of length w by putting enough leading blanks */
808198157Srrschar *
809198157Srrscenter(char *s, char *t, int w)
810198157Srrs{
811198157Srrs	char blanks[80];
812198157Srrs
813198157Srrs	memset(blanks, ' ', sizeof(blanks));
814198626Srrs	sprintf(s, "%.*s%s", (int)(w - strlen(t)) / 2, blanks, t);
815198608Srrs	return (s);
816198157Srrs}
817198157Srrs