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