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