cal.c revision 1.18 1 1.18 agc /* $NetBSD: cal.c,v 1.18 2003/08/07 11:13:13 agc 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.18 agc * 3. Neither the name of the University nor the names of its contributors
19 1.1 cgd * may be used to endorse or promote products derived from this software
20 1.1 cgd * without specific prior written permission.
21 1.1 cgd *
22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 1.1 cgd * SUCH DAMAGE.
33 1.1 cgd */
34 1.1 cgd
35 1.7 lukem #include <sys/cdefs.h>
36 1.1 cgd #ifndef lint
37 1.7 lukem __COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\
38 1.7 lukem The Regents of the University of California. All rights reserved.\n");
39 1.1 cgd #endif /* not lint */
40 1.1 cgd
41 1.1 cgd #ifndef lint
42 1.6 glass #if 0
43 1.6 glass static char sccsid[] = "@(#)cal.c 8.4 (Berkeley) 4/2/94";
44 1.6 glass #else
45 1.18 agc __RCSID("$NetBSD: cal.c,v 1.18 2003/08/07 11:13:13 agc Exp $");
46 1.6 glass #endif
47 1.1 cgd #endif /* not lint */
48 1.1 cgd
49 1.1 cgd #include <sys/types.h>
50 1.6 glass
51 1.6 glass #include <ctype.h>
52 1.6 glass #include <err.h>
53 1.14 yamt #include <errno.h>
54 1.14 yamt #include <limits.h>
55 1.1 cgd #include <stdio.h>
56 1.6 glass #include <stdlib.h>
57 1.4 cgd #include <string.h>
58 1.15 atatat #include <termcap.h>
59 1.6 glass #include <time.h>
60 1.8 christos #include <tzfile.h>
61 1.6 glass #include <unistd.h>
62 1.1 cgd
63 1.1 cgd #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
64 1.1 cgd
65 1.17 atatat #define FIRST_MISSING_DAY reform->first_missing_day
66 1.17 atatat #define NUMBER_MISSING_DAYS reform->missing_days
67 1.1 cgd
68 1.1 cgd #define MAXDAYS 42 /* max slots in a month array */
69 1.1 cgd #define SPACE -1 /* used in day array */
70 1.1 cgd
71 1.1 cgd static int days_in_month[2][13] = {
72 1.1 cgd {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
73 1.1 cgd {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
74 1.1 cgd };
75 1.1 cgd
76 1.17 atatat int empty[MAXDAYS] = {
77 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
78 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
79 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
80 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
81 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
82 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
83 1.1 cgd };
84 1.17 atatat int shift_days[2][4][MAXDAYS + 1];
85 1.1 cgd
86 1.1 cgd char *month_names[12] = {
87 1.1 cgd "January", "February", "March", "April", "May", "June",
88 1.1 cgd "July", "August", "September", "October", "November", "December",
89 1.1 cgd };
90 1.1 cgd
91 1.1 cgd char *day_headings = " S M Tu W Th F S";
92 1.1 cgd char *j_day_headings = " S M Tu W Th F S";
93 1.1 cgd
94 1.17 atatat /* leap years according to the julian calendar */
95 1.17 atatat #define j_leap_year(y, m, d) \
96 1.17 atatat (((m) > 2) && \
97 1.17 atatat !((y) % 4))
98 1.17 atatat
99 1.17 atatat /* leap years according to the gregorian calendar */
100 1.17 atatat #define g_leap_year(y, m, d) \
101 1.17 atatat (((m) > 2) && \
102 1.17 atatat ((!((y) % 4) && ((y) % 100)) || \
103 1.17 atatat !((y) % 400)))
104 1.17 atatat
105 1.17 atatat /* leap year -- account for gregorian reformation at some point */
106 1.1 cgd #define leap_year(yr) \
107 1.17 atatat ((yr) <= reform->year ? j_leap_year((yr), 3, 1) : \
108 1.17 atatat g_leap_year((yr), 3, 1))
109 1.1 cgd
110 1.17 atatat /* number of julian leap days that have passed by a given date */
111 1.17 atatat #define j_leap_days(y, m, d) \
112 1.17 atatat ((((y) - 1) / 4) + j_leap_year(y, m, d))
113 1.17 atatat
114 1.17 atatat /* number of gregorian leap days that have passed by a given date */
115 1.17 atatat #define g_leap_days(y, m, d) \
116 1.17 atatat ((((y) - 1) / 4) - (((y) - 1) / 100) + (((y) - 1) / 400) + \
117 1.17 atatat g_leap_year(y, m, d))
118 1.17 atatat
119 1.17 atatat /*
120 1.17 atatat * Subtracting the gregorian leap day count (for a given date) from
121 1.17 atatat * the julian leap day count (for the same date) describes the number
122 1.17 atatat * of days from the date before the shift to the next date that
123 1.17 atatat * appears in the calendar. Since we want to know the number of
124 1.17 atatat * *missing* days, not the number of days that the shift spans, we
125 1.17 atatat * subtract 2.
126 1.17 atatat *
127 1.17 atatat * Alternately...
128 1.17 atatat *
129 1.17 atatat * There's a reason they call the Dark ages the Dark Ages. Part of it
130 1.17 atatat * is that we don't have that many records of that period of time.
131 1.17 atatat * One of the reasons for this is that a lot of the Dark Ages never
132 1.17 atatat * actually took place. At some point in the first millenium A.D., a
133 1.17 atatat * ruler of some power decided that he wanted the number of the year
134 1.17 atatat * to be different than what it was, so he changed it to coincide
135 1.17 atatat * nicely with some event (a birthday or anniversary, perhaps a
136 1.17 atatat * wedding, or maybe a centennial for a largish city). One of the
137 1.17 atatat * side effects of this upon the Gregorian reform is that two Julian
138 1.17 atatat * leap years (leap days celebrated during centennial years that are
139 1.17 atatat * not quatro-centennial years) were skipped.
140 1.17 atatat */
141 1.17 atatat #define GREGORIAN_MAGIC 2
142 1.17 atatat
143 1.17 atatat /* number of centuries since the reform, not inclusive */
144 1.17 atatat #define centuries_since_reform(yr) \
145 1.17 atatat ((yr) > reform->year ? ((yr) / 100) - (reform->year / 100) : 0)
146 1.17 atatat
147 1.17 atatat /* number of centuries since the reform whose modulo of 400 is 0 */
148 1.17 atatat #define quad_centuries_since_reform(yr) \
149 1.17 atatat ((yr) > reform->year ? ((yr) / 400) - (reform->year / 400) : 0)
150 1.1 cgd
151 1.1 cgd /* number of leap years between year 1 and this year, not inclusive */
152 1.1 cgd #define leap_years_since_year_1(yr) \
153 1.17 atatat ((yr) / 4 - centuries_since_reform(yr) + quad_centuries_since_reform(yr))
154 1.17 atatat
155 1.17 atatat struct reform {
156 1.17 atatat const char *country;
157 1.17 atatat int ambiguity, year, month, date;
158 1.17 atatat long first_missing_day;
159 1.17 atatat int missing_days;
160 1.17 atatat /*
161 1.17 atatat * That's 2 for standard/julian display, 4 for months possibly
162 1.17 atatat * affected by the Gregorian shift, and MAXDAYS + 1 for the
163 1.17 atatat * days that get displayed, plus a crib slot.
164 1.17 atatat */
165 1.17 atatat } *reform, reforms[] = {
166 1.17 atatat { "DEFAULT", 0, 1752, 9, 3 },
167 1.17 atatat { "Italy", 1, 1582, 10, 5 },
168 1.17 atatat { "Spain", 1, 1582, 10, 5 },
169 1.17 atatat { "Portugal", 1, 1582, 10, 5 },
170 1.17 atatat { "Poland", 1, 1582, 10, 5 },
171 1.17 atatat { "France", 2, 1582, 12, 10 },
172 1.17 atatat { "Luxembourg", 2, 1582, 12, 22 },
173 1.17 atatat { "Netherlands", 2, 1582, 12, 22 },
174 1.17 atatat { "Bavaria", 0, 1583, 10, 6 },
175 1.17 atatat { "Austria", 2, 1584, 1, 7 },
176 1.17 atatat { "Switzerland", 2, 1584, 1, 12 },
177 1.17 atatat { "Hungary", 0, 1587, 10, 22 },
178 1.17 atatat { "Germany", 0, 1700, 2, 19 },
179 1.17 atatat { "Norway", 0, 1700, 2, 19 },
180 1.17 atatat { "Denmark", 0, 1700, 2, 19 },
181 1.17 atatat { "Great Britain", 0, 1752, 9, 3 },
182 1.17 atatat { "England", 0, 1752, 9, 3 },
183 1.17 atatat { "America", 0, 1752, 9, 3 },
184 1.17 atatat { "Sweden", 0, 1753, 2, 18 },
185 1.17 atatat { "Finland", 0, 1753, 2, 18 },
186 1.17 atatat { "Japan", 0, 1872, 12, 20 },
187 1.17 atatat { "China", 0, 1911, 11, 7 },
188 1.17 atatat { "Bulgaria", 0, 1916, 4, 1 },
189 1.17 atatat { "U.S.S.R.", 0, 1918, 2, 1 },
190 1.17 atatat { "Serbia", 0, 1919, 1, 19 },
191 1.17 atatat { "Romania", 0, 1919, 1, 19 },
192 1.17 atatat { "Greece", 0, 1924, 3, 10 },
193 1.17 atatat { "Turkey", 0, 1925, 12, 19 },
194 1.17 atatat { "Egypt", 0, 1928, 9, 18 },
195 1.17 atatat { NULL, 0, 0, 0, 0 },
196 1.17 atatat };
197 1.1 cgd
198 1.1 cgd int julian;
199 1.17 atatat int dow;
200 1.15 atatat int hilite;
201 1.15 atatat char *md, *me;
202 1.1 cgd
203 1.15 atatat void init_hilite(void);
204 1.14 yamt int getnum(const char *);
205 1.17 atatat void gregorian_reform(const char *);
206 1.17 atatat void reform_day_array(int, int, int *, int *, int *,int *,int *,int *);
207 1.15 atatat int ascii_day(char *, int);
208 1.12 perry void center(char *, int, int);
209 1.12 perry void day_array(int, int, int *);
210 1.12 perry int day_in_week(int, int, int);
211 1.12 perry int day_in_year(int, int, int);
212 1.14 yamt void monthrange(int, int, int, int, int);
213 1.12 perry int main(int, char **);
214 1.12 perry void trim_trailing_spaces(char *);
215 1.12 perry void usage(void);
216 1.6 glass
217 1.6 glass int
218 1.12 perry main(int argc, char **argv)
219 1.1 cgd {
220 1.1 cgd struct tm *local_time;
221 1.6 glass time_t now;
222 1.14 yamt int ch, month, year, yflag;
223 1.17 atatat int before, after, use_reform;
224 1.14 yamt int yearly = 0;
225 1.17 atatat char *when;
226 1.1 cgd
227 1.14 yamt before = after = 0;
228 1.17 atatat use_reform = yflag = year = 0;
229 1.17 atatat when = NULL;
230 1.17 atatat while ((ch = getopt(argc, argv, "A:B:d:hjR:ry3")) != -1) {
231 1.12 perry switch (ch) {
232 1.14 yamt case 'A':
233 1.14 yamt after = getnum(optarg);
234 1.14 yamt break;
235 1.14 yamt case 'B':
236 1.14 yamt before = getnum(optarg);
237 1.14 yamt break;
238 1.17 atatat case 'd':
239 1.17 atatat dow = getnum(optarg);
240 1.17 atatat if (dow < 0 || dow > 6)
241 1.17 atatat errx(1, "illegal day of week value: use 0-6");
242 1.17 atatat break;
243 1.15 atatat case 'h':
244 1.15 atatat init_hilite();
245 1.15 atatat break;
246 1.1 cgd case 'j':
247 1.1 cgd julian = 1;
248 1.1 cgd break;
249 1.17 atatat case 'R':
250 1.17 atatat when = optarg;
251 1.17 atatat break;
252 1.17 atatat case 'r':
253 1.17 atatat use_reform = 1;
254 1.17 atatat break;
255 1.1 cgd case 'y':
256 1.1 cgd yflag = 1;
257 1.1 cgd break;
258 1.13 perry case '3':
259 1.14 yamt before = after = 1;
260 1.13 perry break;
261 1.1 cgd case '?':
262 1.1 cgd default:
263 1.1 cgd usage();
264 1.12 perry /* NOTREACHED */
265 1.1 cgd }
266 1.12 perry }
267 1.13 perry
268 1.1 cgd argc -= optind;
269 1.1 cgd argv += optind;
270 1.1 cgd
271 1.17 atatat if (when != NULL)
272 1.17 atatat gregorian_reform(when);
273 1.17 atatat if (reform == NULL)
274 1.17 atatat gregorian_reform("DEFAULT");
275 1.17 atatat
276 1.1 cgd month = 0;
277 1.12 perry switch (argc) {
278 1.1 cgd case 2:
279 1.6 glass if ((month = atoi(*argv++)) < 1 || month > 12)
280 1.6 glass errx(1, "illegal month value: use 1-12");
281 1.1 cgd /* FALLTHROUGH */
282 1.1 cgd case 1:
283 1.6 glass if ((year = atoi(*argv)) < 1 || year > 9999)
284 1.6 glass errx(1, "illegal year value: use 1-9999");
285 1.1 cgd break;
286 1.1 cgd case 0:
287 1.1 cgd (void)time(&now);
288 1.1 cgd local_time = localtime(&now);
289 1.17 atatat if (use_reform)
290 1.17 atatat year = reform->year;
291 1.17 atatat else
292 1.17 atatat year = local_time->tm_year + TM_YEAR_BASE;
293 1.17 atatat if (!yflag) {
294 1.17 atatat if (use_reform)
295 1.17 atatat month = reform->month;
296 1.17 atatat else
297 1.17 atatat month = local_time->tm_mon + 1;
298 1.17 atatat }
299 1.1 cgd break;
300 1.1 cgd default:
301 1.1 cgd usage();
302 1.1 cgd }
303 1.1 cgd
304 1.14 yamt if (!month) {
305 1.14 yamt /* yearly */
306 1.14 yamt month = 1;
307 1.14 yamt before = 0;
308 1.14 yamt after = 11;
309 1.14 yamt yearly = 1;
310 1.14 yamt }
311 1.14 yamt
312 1.14 yamt monthrange(month, year, before, after, yearly);
313 1.13 perry
314 1.1 cgd exit(0);
315 1.1 cgd }
316 1.1 cgd
317 1.1 cgd #define DAY_LEN 3 /* 3 spaces per day */
318 1.1 cgd #define J_DAY_LEN 4 /* 4 spaces per day */
319 1.1 cgd #define WEEK_LEN 20 /* 7 * 3 - one space at the end */
320 1.1 cgd #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */
321 1.1 cgd #define HEAD_SEP 2 /* spaces between day headings */
322 1.1 cgd #define J_HEAD_SEP 2
323 1.14 yamt #define MONTH_PER_ROW 3 /* how many monthes in a row */
324 1.14 yamt #define J_MONTH_PER_ROW 2
325 1.1 cgd
326 1.6 glass void
327 1.14 yamt monthrange(int month, int year, int before, int after, int yearly)
328 1.1 cgd {
329 1.14 yamt int startmonth, startyear;
330 1.14 yamt int endmonth, endyear;
331 1.14 yamt int i, row;
332 1.14 yamt int days[3][MAXDAYS];
333 1.15 atatat char lineout[256];
334 1.14 yamt int inayear;
335 1.14 yamt int newyear;
336 1.14 yamt int day_len, week_len, head_sep;
337 1.14 yamt int month_per_row;
338 1.15 atatat int skip, r_off, w_off;
339 1.14 yamt
340 1.14 yamt if (julian) {
341 1.14 yamt day_len = J_DAY_LEN;
342 1.14 yamt week_len = J_WEEK_LEN;
343 1.14 yamt head_sep = J_HEAD_SEP;
344 1.14 yamt month_per_row = J_MONTH_PER_ROW;
345 1.14 yamt }
346 1.14 yamt else {
347 1.14 yamt day_len = DAY_LEN;
348 1.14 yamt week_len = WEEK_LEN;
349 1.14 yamt head_sep = HEAD_SEP;
350 1.14 yamt month_per_row = MONTH_PER_ROW;
351 1.14 yamt }
352 1.14 yamt
353 1.14 yamt month--;
354 1.14 yamt
355 1.14 yamt startyear = year - (before + 12 - 1 - month) / 12;
356 1.14 yamt startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12);
357 1.14 yamt endyear = year + (month + after) / 12;
358 1.14 yamt endmonth = (month + after) % 12;
359 1.1 cgd
360 1.14 yamt if (startyear < 0 || endyear > 9999) {
361 1.14 yamt errx(1, "year should be in 1-9999\n");
362 1.1 cgd }
363 1.1 cgd
364 1.14 yamt year = startyear;
365 1.14 yamt month = startmonth;
366 1.14 yamt inayear = newyear = (year != endyear || yearly);
367 1.14 yamt if (inayear) {
368 1.14 yamt skip = month % month_per_row;
369 1.14 yamt month -= skip;
370 1.14 yamt }
371 1.14 yamt else {
372 1.14 yamt skip = 0;
373 1.14 yamt }
374 1.14 yamt
375 1.14 yamt do {
376 1.14 yamt if (newyear) {
377 1.14 yamt (void)snprintf(lineout, sizeof(lineout), "%d", year);
378 1.14 yamt center(lineout, week_len * month_per_row +
379 1.14 yamt head_sep * (month_per_row - 1), 0);
380 1.14 yamt (void)printf("\n\n");
381 1.14 yamt newyear = 0;
382 1.14 yamt }
383 1.14 yamt
384 1.14 yamt for (i = 0; i < skip; i++)
385 1.14 yamt center("", week_len, head_sep);
386 1.14 yamt
387 1.14 yamt for (; i < month_per_row; i++) {
388 1.14 yamt int sep;
389 1.14 yamt
390 1.14 yamt if (year == endyear && month + i > endmonth)
391 1.14 yamt break;
392 1.14 yamt
393 1.14 yamt sep = (i == month_per_row - 1) ? 0 : head_sep;
394 1.14 yamt day_array(month + i + 1, year, days[i]);
395 1.14 yamt if (inayear) {
396 1.14 yamt center(month_names[month + i], week_len, sep);
397 1.14 yamt }
398 1.14 yamt else {
399 1.14 yamt snprintf(lineout, sizeof(lineout), "%s %d",
400 1.14 yamt month_names[month + i], year);
401 1.14 yamt center(lineout, week_len, sep);
402 1.1 cgd }
403 1.1 cgd }
404 1.14 yamt printf("\n");
405 1.14 yamt
406 1.14 yamt for (i = 0; i < skip; i++)
407 1.14 yamt center("", week_len, head_sep);
408 1.14 yamt
409 1.14 yamt for (; i < month_per_row; i++) {
410 1.14 yamt int sep;
411 1.14 yamt
412 1.14 yamt if (year == endyear && month + i > endmonth)
413 1.14 yamt break;
414 1.14 yamt
415 1.14 yamt sep = (i == month_per_row - 1) ? 0 : head_sep;
416 1.17 atatat if (dow) {
417 1.17 atatat printf("%s ", (julian) ?
418 1.17 atatat j_day_headings + 4 * dow :
419 1.17 atatat day_headings + 3 * dow);
420 1.17 atatat printf("%.*s", dow * (julian ? 4 : 3) - 1,
421 1.17 atatat (julian) ? j_day_headings : day_headings);
422 1.17 atatat } else
423 1.17 atatat printf("%s", (julian) ? j_day_headings : day_headings);
424 1.17 atatat printf("%*s", sep, "");
425 1.14 yamt }
426 1.14 yamt printf("\n");
427 1.1 cgd
428 1.1 cgd for (row = 0; row < 6; row++) {
429 1.14 yamt char *p;
430 1.16 yamt
431 1.16 yamt memset(lineout, ' ', sizeof(lineout));
432 1.14 yamt for (i = 0; i < skip; i++) {
433 1.16 yamt /* nothing */
434 1.14 yamt }
435 1.15 atatat w_off = 0;
436 1.14 yamt for (; i < month_per_row; i++) {
437 1.14 yamt int col, *dp;
438 1.14 yamt
439 1.14 yamt if (year == endyear && month + i > endmonth)
440 1.14 yamt break;
441 1.14 yamt
442 1.15 atatat p = lineout + i * (week_len + 2) + w_off;
443 1.14 yamt dp = &days[i][row * 7];
444 1.15 atatat for (col = 0; col < 7;
445 1.15 atatat col++, p += day_len + r_off) {
446 1.15 atatat r_off = ascii_day(p, *dp++);
447 1.15 atatat w_off += r_off;
448 1.15 atatat }
449 1.1 cgd }
450 1.3 cgd *p = '\0';
451 1.1 cgd trim_trailing_spaces(lineout);
452 1.1 cgd (void)printf("%s\n", lineout);
453 1.1 cgd }
454 1.13 perry
455 1.14 yamt skip = 0;
456 1.14 yamt month += month_per_row;
457 1.14 yamt if (month >= 12) {
458 1.14 yamt month -= 12;
459 1.14 yamt year++;
460 1.14 yamt newyear = 1;
461 1.13 perry }
462 1.14 yamt } while (year < endyear || (year == endyear && month <= endmonth));
463 1.13 perry }
464 1.13 perry
465 1.1 cgd /*
466 1.1 cgd * day_array --
467 1.1 cgd * Fill in an array of 42 integers with a calendar. Assume for a moment
468 1.1 cgd * that you took the (maximum) 6 rows in a calendar and stretched them
469 1.1 cgd * out end to end. You would have 42 numbers or spaces. This routine
470 1.1 cgd * builds that array for any month from Jan. 1 through Dec. 9999.
471 1.1 cgd */
472 1.6 glass void
473 1.12 perry day_array(int month, int year, int *days)
474 1.1 cgd {
475 1.6 glass int day, dw, dm;
476 1.15 atatat time_t t;
477 1.15 atatat struct tm *tm;
478 1.15 atatat
479 1.15 atatat t = time(NULL);
480 1.15 atatat tm = localtime(&t);
481 1.15 atatat tm->tm_year += TM_YEAR_BASE;
482 1.15 atatat tm->tm_mon++;
483 1.15 atatat tm->tm_yday++; /* jan 1 is 1 for us, not 0 */
484 1.1 cgd
485 1.17 atatat for (dm = month + year * 12, dw = 0; dw < 4; dw++) {
486 1.17 atatat if (dm == shift_days[julian][dw][MAXDAYS]) {
487 1.17 atatat memmove(days, shift_days[julian][dw],
488 1.17 atatat MAXDAYS * sizeof(int));
489 1.17 atatat return;
490 1.17 atatat }
491 1.1 cgd }
492 1.17 atatat
493 1.6 glass memmove(days, empty, MAXDAYS * sizeof(int));
494 1.1 cgd dm = days_in_month[leap_year(year)][month];
495 1.1 cgd dw = day_in_week(1, month, year);
496 1.1 cgd day = julian ? day_in_year(1, month, year) : 1;
497 1.15 atatat while (dm--) {
498 1.15 atatat if (hilite && year == tm->tm_year &&
499 1.15 atatat (julian ? (day == tm->tm_yday) :
500 1.15 atatat (month == tm->tm_mon && day == tm->tm_mday)))
501 1.17 atatat days[dw++] = SPACE - day++;
502 1.15 atatat else
503 1.15 atatat days[dw++] = day++;
504 1.15 atatat }
505 1.1 cgd }
506 1.1 cgd
507 1.1 cgd /*
508 1.1 cgd * day_in_year --
509 1.1 cgd * return the 1 based day number within the year
510 1.1 cgd */
511 1.6 glass int
512 1.12 perry day_in_year(int day, int month, int year)
513 1.1 cgd {
514 1.6 glass int i, leap;
515 1.1 cgd
516 1.1 cgd leap = leap_year(year);
517 1.1 cgd for (i = 1; i < month; i++)
518 1.1 cgd day += days_in_month[leap][i];
519 1.6 glass return (day);
520 1.1 cgd }
521 1.1 cgd
522 1.1 cgd /*
523 1.1 cgd * day_in_week
524 1.1 cgd * return the 0 based day number for any date from 1 Jan. 1 to
525 1.17 atatat * 31 Dec. 9999. Returns the day of the week of the first
526 1.17 atatat * missing day for any given Gregorian shift.
527 1.1 cgd */
528 1.6 glass int
529 1.12 perry day_in_week(int day, int month, int year)
530 1.1 cgd {
531 1.1 cgd long temp;
532 1.1 cgd
533 1.1 cgd temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
534 1.1 cgd + day_in_year(day, month, year);
535 1.1 cgd if (temp < FIRST_MISSING_DAY)
536 1.17 atatat return ((temp - dow + 6 + SATURDAY) % 7);
537 1.1 cgd if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
538 1.17 atatat return (((temp - dow + 6 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
539 1.17 atatat return ((FIRST_MISSING_DAY - dow + 6 + SATURDAY) % 7);
540 1.1 cgd }
541 1.1 cgd
542 1.15 atatat int
543 1.12 perry ascii_day(char *p, int day)
544 1.1 cgd {
545 1.15 atatat int display, val, rc;
546 1.15 atatat char *b;
547 1.1 cgd static char *aday[] = {
548 1.1 cgd "",
549 1.1 cgd " 1", " 2", " 3", " 4", " 5", " 6", " 7",
550 1.1 cgd " 8", " 9", "10", "11", "12", "13", "14",
551 1.1 cgd "15", "16", "17", "18", "19", "20", "21",
552 1.1 cgd "22", "23", "24", "25", "26", "27", "28",
553 1.1 cgd "29", "30", "31",
554 1.1 cgd };
555 1.1 cgd
556 1.1 cgd if (day == SPACE) {
557 1.1 cgd memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
558 1.15 atatat return (0);
559 1.1 cgd }
560 1.17 atatat if (day < SPACE) {
561 1.15 atatat b = p;
562 1.17 atatat day = SPACE - day;
563 1.15 atatat } else
564 1.15 atatat b = NULL;
565 1.1 cgd if (julian) {
566 1.7 lukem if ((val = day / 100) != 0) {
567 1.1 cgd day %= 100;
568 1.1 cgd *p++ = val + '0';
569 1.1 cgd display = 1;
570 1.1 cgd } else {
571 1.1 cgd *p++ = ' ';
572 1.1 cgd display = 0;
573 1.1 cgd }
574 1.1 cgd val = day / 10;
575 1.1 cgd if (val || display)
576 1.1 cgd *p++ = val + '0';
577 1.1 cgd else
578 1.1 cgd *p++ = ' ';
579 1.1 cgd *p++ = day % 10 + '0';
580 1.1 cgd } else {
581 1.1 cgd *p++ = aday[day][0];
582 1.1 cgd *p++ = aday[day][1];
583 1.1 cgd }
584 1.15 atatat
585 1.15 atatat rc = 0;
586 1.15 atatat if (b != NULL) {
587 1.15 atatat char *t, h[64];
588 1.15 atatat int l;
589 1.15 atatat
590 1.15 atatat l = p - b;
591 1.15 atatat memcpy(h, b, l);
592 1.15 atatat p = b;
593 1.15 atatat
594 1.15 atatat if (md != NULL) {
595 1.15 atatat for (t = md; *t; rc++)
596 1.15 atatat *p++ = *t++;
597 1.15 atatat memcpy(p, h, l);
598 1.15 atatat p += l;
599 1.15 atatat for (t = me; *t; rc++)
600 1.15 atatat *p++ = *t++;
601 1.15 atatat } else {
602 1.15 atatat for (t = &h[0]; l--; t++) {
603 1.15 atatat *p++ = *t;
604 1.15 atatat rc++;
605 1.15 atatat *p++ = '\b';
606 1.15 atatat rc++;
607 1.15 atatat *p++ = *t;
608 1.15 atatat }
609 1.15 atatat }
610 1.15 atatat }
611 1.15 atatat
612 1.1 cgd *p = ' ';
613 1.15 atatat return (rc);
614 1.1 cgd }
615 1.1 cgd
616 1.6 glass void
617 1.12 perry trim_trailing_spaces(char *s)
618 1.1 cgd {
619 1.6 glass char *p;
620 1.1 cgd
621 1.6 glass for (p = s; *p; ++p)
622 1.6 glass continue;
623 1.11 christos while (p > s && isspace((unsigned char)*--p))
624 1.6 glass continue;
625 1.1 cgd if (p > s)
626 1.1 cgd ++p;
627 1.1 cgd *p = '\0';
628 1.1 cgd }
629 1.1 cgd
630 1.6 glass void
631 1.12 perry center(char *str, int len, int separate)
632 1.1 cgd {
633 1.6 glass
634 1.1 cgd len -= strlen(str);
635 1.1 cgd (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
636 1.1 cgd if (separate)
637 1.1 cgd (void)printf("%*s", separate, "");
638 1.1 cgd }
639 1.1 cgd
640 1.17 atatat /*
641 1.17 atatat * gregorian_reform --
642 1.17 atatat * Given a description of date on which the Gregorian Reform was
643 1.17 atatat * applied. The argument can be any of the "country" names
644 1.17 atatat * listed in the reforms array (case insensitive) or a date of
645 1.17 atatat * the form YYYY/MM/DD. The date and month can be omitted if
646 1.17 atatat * doing so would not select more than one different built-in
647 1.17 atatat * reform point.
648 1.17 atatat */
649 1.17 atatat void
650 1.17 atatat gregorian_reform(const char *p)
651 1.17 atatat {
652 1.17 atatat int year, month, date;
653 1.17 atatat int i, days, diw, diy;
654 1.17 atatat char c;
655 1.17 atatat
656 1.17 atatat i = sscanf(p, "%d%*[/,-]%d%*[/,-]%d%c", &year, &month, &date, &c);
657 1.17 atatat switch (i) {
658 1.17 atatat case 4:
659 1.17 atatat /*
660 1.17 atatat * If the character was sscanf()ed, then there's more
661 1.17 atatat * stuff than we need.
662 1.17 atatat */
663 1.17 atatat errx(1, "date specifier %s invalid", p);
664 1.17 atatat case 0:
665 1.17 atatat /*
666 1.17 atatat * Not a form we can sscanf(), so void these, and we
667 1.17 atatat * can try matching "country" names later.
668 1.17 atatat */
669 1.17 atatat year = month = date = -1;
670 1.17 atatat break;
671 1.17 atatat case 1:
672 1.17 atatat month = 0;
673 1.17 atatat /*FALLTHROUGH*/
674 1.17 atatat case 2:
675 1.17 atatat date = 0;
676 1.17 atatat /*FALLTHROUGH*/
677 1.17 atatat case 3:
678 1.17 atatat /*
679 1.17 atatat * At last, some sanity checking on the values we were
680 1.17 atatat * given.
681 1.17 atatat */
682 1.17 atatat if (year < 1 || year > 9999)
683 1.17 atatat errx(1, "%d: illegal year value: use 1-9999", year);
684 1.17 atatat if (i > 1 && (month < 1 || month > 12))
685 1.17 atatat errx(1, "%d: illegal month value: use 1-12", month);
686 1.17 atatat if ((i == 3 && date < 1) || date < 0 ||
687 1.17 atatat date > days_in_month[1][month])
688 1.17 atatat /*
689 1.17 atatat * What about someone specifying a leap day in
690 1.17 atatat * a non-leap year? Well...that's a tricky
691 1.17 atatat * one. We can't yet *say* whether the year
692 1.17 atatat * in question is a leap year. What if the
693 1.17 atatat * date given was, for example, 1700/2/29? is
694 1.17 atatat * that a valid leap day?
695 1.17 atatat *
696 1.17 atatat * So...we punt, and hope that saying 29 in
697 1.17 atatat * the case of February isn't too bad an idea.
698 1.17 atatat */
699 1.17 atatat errx(1, "%d: illegal date value: use 1-%d", date,
700 1.17 atatat days_in_month[1][month]);
701 1.17 atatat break;
702 1.17 atatat }
703 1.17 atatat
704 1.17 atatat /*
705 1.17 atatat * A complete date was specified, so use the other pope.
706 1.17 atatat */
707 1.17 atatat if (date > 0) {
708 1.17 atatat static struct reform Goestheveezl;
709 1.17 atatat
710 1.17 atatat reform = &Goestheveezl;
711 1.17 atatat reform->country = "Bompzidaize";
712 1.17 atatat reform->year = year;
713 1.17 atatat reform->month = month;
714 1.17 atatat reform->date = date;
715 1.17 atatat }
716 1.17 atatat
717 1.17 atatat /*
718 1.17 atatat * No date information was specified, so let's try to match on
719 1.17 atatat * country name.
720 1.17 atatat */
721 1.17 atatat else if (year == -1) {
722 1.17 atatat for (reform = &reforms[0]; reform->year; reform++) {
723 1.17 atatat if (strcasecmp(p, reform->country) == 0)
724 1.17 atatat break;
725 1.17 atatat }
726 1.17 atatat }
727 1.17 atatat
728 1.17 atatat /*
729 1.17 atatat * We have *some* date information, but not a complete date.
730 1.17 atatat * Let's see if we have enough to pick a single entry from the
731 1.17 atatat * list that's not ambiguous.
732 1.17 atatat */
733 1.17 atatat else {
734 1.17 atatat for (reform = &reforms[0]; reform->year; reform++) {
735 1.17 atatat if ((year == 0 || year == reform->year) &&
736 1.17 atatat (month == 0 || month == reform->month) &&
737 1.17 atatat (date == 0 || month == reform->date))
738 1.17 atatat break;
739 1.17 atatat }
740 1.17 atatat
741 1.17 atatat if (i <= reform->ambiguity)
742 1.17 atatat errx(1, "%s: ambiguous short reform date specification", p);
743 1.17 atatat }
744 1.17 atatat
745 1.17 atatat /*
746 1.17 atatat * Oops...we reached the end of the list.
747 1.17 atatat */
748 1.17 atatat if (reform->year == 0)
749 1.17 atatat errx(1, "reform name %s invalid", p);
750 1.17 atatat
751 1.17 atatat /*
752 1.17 atatat *
753 1.17 atatat */
754 1.17 atatat reform->missing_days =
755 1.17 atatat j_leap_days(reform->year, reform->month, reform->date) -
756 1.17 atatat g_leap_days(reform->year, reform->month, reform->date) -
757 1.17 atatat GREGORIAN_MAGIC;
758 1.17 atatat
759 1.17 atatat reform->first_missing_day =
760 1.17 atatat (reform->year - 1) * 365 +
761 1.17 atatat day_in_year(reform->date, reform->month, reform->year) +
762 1.17 atatat date +
763 1.17 atatat j_leap_days(reform->year, reform->month, reform->date);
764 1.17 atatat
765 1.17 atatat /*
766 1.17 atatat * Once we know the day of the week of the first missing day,
767 1.17 atatat * skip back to the first of the month's day of the week.
768 1.17 atatat */
769 1.17 atatat diw = day_in_week(reform->date, reform->month, reform->year);
770 1.17 atatat diw = (diw + 8 - (reform->date % 7)) % 7;
771 1.17 atatat diy = day_in_year(1, reform->month, reform->year);
772 1.17 atatat
773 1.17 atatat /*
774 1.17 atatat * We might need all four of these (if you switch from Julian
775 1.17 atatat * to Gregorian at some point after 9900, you get a gap of 73
776 1.17 atatat * days, and that can affect four months), and it doesn't hurt
777 1.17 atatat * all that much to precompute them, so there.
778 1.17 atatat */
779 1.17 atatat date = 1;
780 1.17 atatat days = 0;
781 1.17 atatat for (i = 0; i < 4; i++)
782 1.17 atatat reform_day_array(reform->month + i, reform->year,
783 1.17 atatat &days, &date, &diw, &diy,
784 1.17 atatat shift_days[0][i],
785 1.17 atatat shift_days[1][i]);
786 1.17 atatat }
787 1.17 atatat
788 1.17 atatat /*
789 1.17 atatat * reform_day_array --
790 1.17 atatat * Pre-calculates the given month's calendar (in both "standard"
791 1.17 atatat * and "julian day" representations) with respect for days
792 1.17 atatat * skipped during a reform period.
793 1.17 atatat */
794 1.17 atatat void
795 1.17 atatat reform_day_array(int month, int year, int *done, int *date, int *diw, int *diy,
796 1.17 atatat int *scal, int *jcal)
797 1.17 atatat {
798 1.17 atatat int mdays;
799 1.17 atatat
800 1.17 atatat /*
801 1.17 atatat * If the reform was in the month of october or later, then
802 1.17 atatat * the month number from the caller could "overflow".
803 1.17 atatat */
804 1.17 atatat if (month > 12) {
805 1.17 atatat month -= 12;
806 1.17 atatat year++;
807 1.17 atatat }
808 1.17 atatat
809 1.17 atatat /*
810 1.17 atatat * Erase months, and set crib number. The crib number is used
811 1.17 atatat * later to determine if the month to be displayed is here or
812 1.17 atatat * should be built on the fly with the generic routine
813 1.17 atatat */
814 1.17 atatat memmove(scal, empty, MAXDAYS * sizeof(int));
815 1.17 atatat scal[MAXDAYS] = month + year * 12;
816 1.17 atatat memmove(jcal, empty, MAXDAYS * sizeof(int));
817 1.17 atatat jcal[MAXDAYS] = month + year * 12;
818 1.17 atatat
819 1.17 atatat /*
820 1.17 atatat * It doesn't matter what the actual month is when figuring
821 1.17 atatat * out if this is a leap year or not, just so long as February
822 1.17 atatat * gets the right number of days in it.
823 1.17 atatat */
824 1.17 atatat mdays = days_in_month[g_leap_year(year, 3, 1)][month];
825 1.17 atatat
826 1.17 atatat /*
827 1.17 atatat * Bounce back to the first "row" in the day array, and fill
828 1.17 atatat * in any days that actually occur.
829 1.17 atatat */
830 1.17 atatat for (*diw %= 7; (*date - *done) <= mdays; (*date)++, (*diy)++) {
831 1.17 atatat /*
832 1.17 atatat * "date" doesn't get reset by the caller across calls
833 1.17 atatat * to this routine, so we can actually tell that we're
834 1.17 atatat * looking at April the 41st. Much easier than trying
835 1.17 atatat * to calculate the absolute julian day for a given
836 1.17 atatat * date and then checking that.
837 1.17 atatat */
838 1.17 atatat if (*date < reform->date ||
839 1.17 atatat *date >= reform->date + reform->missing_days) {
840 1.17 atatat scal[*diw] = *date - *done;
841 1.17 atatat jcal[*diw] = *diy;
842 1.17 atatat (*diw)++;
843 1.17 atatat }
844 1.17 atatat }
845 1.17 atatat *done += mdays;
846 1.17 atatat }
847 1.17 atatat
848 1.14 yamt int
849 1.14 yamt getnum(const char *p)
850 1.14 yamt {
851 1.14 yamt long result;
852 1.14 yamt char *ep;
853 1.14 yamt
854 1.14 yamt errno = 0;
855 1.14 yamt result = strtoul(p, &ep, 10);
856 1.14 yamt if (p[0] == '\0' || *ep != '\0')
857 1.14 yamt goto error;
858 1.14 yamt if (errno == ERANGE && result == ULONG_MAX)
859 1.14 yamt goto error;
860 1.14 yamt if (result > INT_MAX)
861 1.14 yamt goto error;
862 1.14 yamt
863 1.14 yamt return (int)result;
864 1.14 yamt
865 1.14 yamt error:
866 1.14 yamt errx(1, "bad number: %s", p);
867 1.14 yamt /*NOTREACHED*/
868 1.14 yamt }
869 1.14 yamt
870 1.6 glass void
871 1.15 atatat init_hilite(void)
872 1.15 atatat {
873 1.15 atatat static char control[128];
874 1.15 atatat char cap[1024];
875 1.15 atatat char *tc;
876 1.15 atatat
877 1.15 atatat hilite++;
878 1.15 atatat
879 1.15 atatat if (!isatty(fileno(stdout)))
880 1.15 atatat return;
881 1.15 atatat
882 1.15 atatat tc = getenv("TERM");
883 1.15 atatat if (tc == NULL)
884 1.15 atatat tc = "dumb";
885 1.15 atatat if (tgetent(&cap[0], tc) != 1)
886 1.15 atatat return;
887 1.15 atatat
888 1.15 atatat tc = &control[0];
889 1.15 atatat if ((md = tgetstr(hilite > 1 ? "mr" : "md", &tc)))
890 1.15 atatat *tc++ = '\0';
891 1.15 atatat if ((me = tgetstr("me", &tc)))
892 1.15 atatat *tc++ = '\0';
893 1.15 atatat if (me == NULL || md == NULL)
894 1.15 atatat md = me = NULL;
895 1.15 atatat }
896 1.15 atatat
897 1.15 atatat void
898 1.12 perry usage(void)
899 1.1 cgd {
900 1.6 glass
901 1.14 yamt (void)fprintf(stderr,
902 1.17 atatat "usage: cal [-hjry3] [-d day-of-week] [-B before] [-A after] "
903 1.17 atatat "[-R reform-spec]\n [[month] year]\n");
904 1.1 cgd exit(1);
905 1.1 cgd }
906