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