cal.c revision 1.14 1 1.14 yamt /* $NetBSD: cal.c,v 1.14 2002/10/25 20:06:56 yamt 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.14 yamt __RCSID("$NetBSD: cal.c,v 1.14 2002/10/25 20:06:56 yamt 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.6 glass #include <time.h>
63 1.8 christos #include <tzfile.h>
64 1.6 glass #include <unistd.h>
65 1.1 cgd
66 1.1 cgd #define THURSDAY 4 /* for reformation */
67 1.1 cgd #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
68 1.1 cgd
69 1.5 ws #define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
70 1.1 cgd #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
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.1 cgd int sep1752[MAXDAYS] = {
81 1.1 cgd SPACE, SPACE, 1, 2, 14, 15, 16,
82 1.1 cgd 17, 18, 19, 20, 21, 22, 23,
83 1.1 cgd 24, 25, 26, 27, 28, 29, 30,
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 }, j_sep1752[MAXDAYS] = {
88 1.1 cgd SPACE, SPACE, 245, 246, 258, 259, 260,
89 1.1 cgd 261, 262, 263, 264, 265, 266, 267,
90 1.1 cgd 268, 269, 270, 271, 272, 273, 274,
91 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
92 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
93 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
94 1.1 cgd }, empty[MAXDAYS] = {
95 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
96 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
97 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
98 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
99 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
100 1.1 cgd SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
101 1.1 cgd };
102 1.1 cgd
103 1.1 cgd char *month_names[12] = {
104 1.1 cgd "January", "February", "March", "April", "May", "June",
105 1.1 cgd "July", "August", "September", "October", "November", "December",
106 1.1 cgd };
107 1.1 cgd
108 1.1 cgd char *day_headings = " S M Tu W Th F S";
109 1.1 cgd char *j_day_headings = " S M Tu W Th F S";
110 1.1 cgd
111 1.1 cgd /* leap year -- account for gregorian reformation in 1752 */
112 1.1 cgd #define leap_year(yr) \
113 1.1 cgd ((yr) <= 1752 ? !((yr) % 4) : \
114 1.9 ws (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
115 1.1 cgd
116 1.1 cgd /* number of centuries since 1700, not inclusive */
117 1.1 cgd #define centuries_since_1700(yr) \
118 1.1 cgd ((yr) > 1700 ? (yr) / 100 - 17 : 0)
119 1.1 cgd
120 1.1 cgd /* number of centuries since 1700 whose modulo of 400 is 0 */
121 1.1 cgd #define quad_centuries_since_1700(yr) \
122 1.1 cgd ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
123 1.1 cgd
124 1.1 cgd /* number of leap years between year 1 and this year, not inclusive */
125 1.1 cgd #define leap_years_since_year_1(yr) \
126 1.1 cgd ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
127 1.1 cgd
128 1.1 cgd int julian;
129 1.1 cgd
130 1.14 yamt int getnum(const char *);
131 1.12 perry void ascii_day(char *, int);
132 1.12 perry void center(char *, int, int);
133 1.12 perry void day_array(int, int, int *);
134 1.12 perry int day_in_week(int, int, int);
135 1.12 perry int day_in_year(int, int, int);
136 1.14 yamt void monthrange(int, int, int, int, int);
137 1.12 perry int main(int, char **);
138 1.12 perry void trim_trailing_spaces(char *);
139 1.12 perry void usage(void);
140 1.6 glass
141 1.6 glass int
142 1.12 perry main(int argc, char **argv)
143 1.1 cgd {
144 1.1 cgd struct tm *local_time;
145 1.6 glass time_t now;
146 1.14 yamt int ch, month, year, yflag;
147 1.14 yamt int before, after;
148 1.14 yamt int yearly = 0;
149 1.1 cgd
150 1.14 yamt before = after = 0;
151 1.7 lukem yflag = year = 0;
152 1.14 yamt while ((ch = getopt(argc, argv, "A:B:jy3")) != -1) {
153 1.12 perry switch (ch) {
154 1.14 yamt case 'A':
155 1.14 yamt after = getnum(optarg);
156 1.14 yamt break;
157 1.14 yamt case 'B':
158 1.14 yamt before = getnum(optarg);
159 1.14 yamt break;
160 1.1 cgd case 'j':
161 1.1 cgd julian = 1;
162 1.1 cgd break;
163 1.1 cgd case 'y':
164 1.1 cgd yflag = 1;
165 1.1 cgd break;
166 1.13 perry case '3':
167 1.14 yamt before = after = 1;
168 1.13 perry break;
169 1.1 cgd case '?':
170 1.1 cgd default:
171 1.1 cgd usage();
172 1.12 perry /* NOTREACHED */
173 1.1 cgd }
174 1.12 perry }
175 1.13 perry
176 1.1 cgd argc -= optind;
177 1.1 cgd argv += optind;
178 1.1 cgd
179 1.1 cgd month = 0;
180 1.12 perry switch (argc) {
181 1.1 cgd case 2:
182 1.6 glass if ((month = atoi(*argv++)) < 1 || month > 12)
183 1.6 glass errx(1, "illegal month value: use 1-12");
184 1.1 cgd /* FALLTHROUGH */
185 1.1 cgd case 1:
186 1.6 glass if ((year = atoi(*argv)) < 1 || year > 9999)
187 1.6 glass errx(1, "illegal year value: use 1-9999");
188 1.1 cgd break;
189 1.1 cgd case 0:
190 1.1 cgd (void)time(&now);
191 1.1 cgd local_time = localtime(&now);
192 1.8 christos year = local_time->tm_year + TM_YEAR_BASE;
193 1.1 cgd if (!yflag)
194 1.1 cgd month = local_time->tm_mon + 1;
195 1.1 cgd break;
196 1.1 cgd default:
197 1.1 cgd usage();
198 1.1 cgd }
199 1.1 cgd
200 1.14 yamt if (!month) {
201 1.14 yamt /* yearly */
202 1.14 yamt month = 1;
203 1.14 yamt before = 0;
204 1.14 yamt after = 11;
205 1.14 yamt yearly = 1;
206 1.14 yamt }
207 1.14 yamt
208 1.14 yamt monthrange(month, year, before, after, yearly);
209 1.13 perry
210 1.1 cgd exit(0);
211 1.1 cgd }
212 1.1 cgd
213 1.1 cgd #define DAY_LEN 3 /* 3 spaces per day */
214 1.1 cgd #define J_DAY_LEN 4 /* 4 spaces per day */
215 1.1 cgd #define WEEK_LEN 20 /* 7 * 3 - one space at the end */
216 1.1 cgd #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */
217 1.1 cgd #define HEAD_SEP 2 /* spaces between day headings */
218 1.1 cgd #define J_HEAD_SEP 2
219 1.14 yamt #define MONTH_PER_ROW 3 /* how many monthes in a row */
220 1.14 yamt #define J_MONTH_PER_ROW 2
221 1.1 cgd
222 1.6 glass void
223 1.14 yamt monthrange(int month, int year, int before, int after, int yearly)
224 1.1 cgd {
225 1.14 yamt int startmonth, startyear;
226 1.14 yamt int endmonth, endyear;
227 1.14 yamt int i, row;
228 1.14 yamt int days[3][MAXDAYS];
229 1.14 yamt char lineout[80];
230 1.14 yamt int inayear;
231 1.14 yamt int newyear;
232 1.14 yamt int day_len, week_len, head_sep;
233 1.14 yamt int month_per_row;
234 1.14 yamt int skip;
235 1.14 yamt
236 1.14 yamt if (julian) {
237 1.14 yamt day_len = J_DAY_LEN;
238 1.14 yamt week_len = J_WEEK_LEN;
239 1.14 yamt head_sep = J_HEAD_SEP;
240 1.14 yamt month_per_row = J_MONTH_PER_ROW;
241 1.14 yamt }
242 1.14 yamt else {
243 1.14 yamt day_len = DAY_LEN;
244 1.14 yamt week_len = WEEK_LEN;
245 1.14 yamt head_sep = HEAD_SEP;
246 1.14 yamt month_per_row = MONTH_PER_ROW;
247 1.14 yamt }
248 1.14 yamt
249 1.14 yamt month--;
250 1.14 yamt
251 1.14 yamt startyear = year - (before + 12 - 1 - month) / 12;
252 1.14 yamt startmonth = 12 - 1 - ((before + 12 - 1 - month) % 12);
253 1.14 yamt endyear = year + (month + after) / 12;
254 1.14 yamt endmonth = (month + after) % 12;
255 1.1 cgd
256 1.14 yamt if (startyear < 0 || endyear > 9999) {
257 1.14 yamt errx(1, "year should be in 1-9999\n");
258 1.1 cgd }
259 1.1 cgd
260 1.14 yamt year = startyear;
261 1.14 yamt month = startmonth;
262 1.14 yamt inayear = newyear = (year != endyear || yearly);
263 1.14 yamt if (inayear) {
264 1.14 yamt skip = month % month_per_row;
265 1.14 yamt month -= skip;
266 1.14 yamt }
267 1.14 yamt else {
268 1.14 yamt skip = 0;
269 1.14 yamt }
270 1.14 yamt
271 1.14 yamt do {
272 1.14 yamt if (newyear) {
273 1.14 yamt (void)snprintf(lineout, sizeof(lineout), "%d", year);
274 1.14 yamt center(lineout, week_len * month_per_row +
275 1.14 yamt head_sep * (month_per_row - 1), 0);
276 1.14 yamt (void)printf("\n\n");
277 1.14 yamt newyear = 0;
278 1.14 yamt }
279 1.14 yamt
280 1.14 yamt for (i = 0; i < skip; i++)
281 1.14 yamt center("", week_len, head_sep);
282 1.14 yamt
283 1.14 yamt for (; i < month_per_row; i++) {
284 1.14 yamt int sep;
285 1.14 yamt
286 1.14 yamt if (year == endyear && month + i > endmonth)
287 1.14 yamt break;
288 1.14 yamt
289 1.14 yamt sep = (i == month_per_row - 1) ? 0 : head_sep;
290 1.14 yamt day_array(month + i + 1, year, days[i]);
291 1.14 yamt if (inayear) {
292 1.14 yamt center(month_names[month + i], week_len, sep);
293 1.14 yamt }
294 1.14 yamt else {
295 1.14 yamt snprintf(lineout, sizeof(lineout), "%s %d",
296 1.14 yamt month_names[month + i], year);
297 1.14 yamt center(lineout, week_len, sep);
298 1.1 cgd }
299 1.1 cgd }
300 1.14 yamt printf("\n");
301 1.14 yamt
302 1.14 yamt for (i = 0; i < skip; i++)
303 1.14 yamt center("", week_len, head_sep);
304 1.14 yamt
305 1.14 yamt for (; i < month_per_row; i++) {
306 1.14 yamt int sep;
307 1.14 yamt
308 1.14 yamt if (year == endyear && month + i > endmonth)
309 1.14 yamt break;
310 1.14 yamt
311 1.14 yamt sep = (i == month_per_row - 1) ? 0 : head_sep;
312 1.14 yamt printf("%s%*s",
313 1.14 yamt (julian) ? j_day_headings : day_headings, sep, "");
314 1.14 yamt }
315 1.14 yamt printf("\n");
316 1.1 cgd
317 1.14 yamt memset(lineout, ' ', sizeof(lineout));
318 1.1 cgd for (row = 0; row < 6; row++) {
319 1.14 yamt char *p;
320 1.14 yamt for (i = 0; i < skip; i++) {
321 1.14 yamt p = lineout + i * (week_len + 2);
322 1.14 yamt memset(p, ' ', week_len);
323 1.14 yamt }
324 1.14 yamt for (; i < month_per_row; i++) {
325 1.14 yamt int col, *dp;
326 1.14 yamt
327 1.14 yamt if (year == endyear && month + i > endmonth)
328 1.14 yamt break;
329 1.14 yamt
330 1.14 yamt p = lineout + i * (week_len + 2);
331 1.14 yamt dp = &days[i][row * 7];
332 1.14 yamt for (col = 0; col < 7; col++, p += day_len)
333 1.1 cgd ascii_day(p, *dp++);
334 1.1 cgd }
335 1.3 cgd *p = '\0';
336 1.1 cgd trim_trailing_spaces(lineout);
337 1.1 cgd (void)printf("%s\n", lineout);
338 1.1 cgd }
339 1.13 perry
340 1.14 yamt skip = 0;
341 1.14 yamt month += month_per_row;
342 1.14 yamt if (month >= 12) {
343 1.14 yamt month -= 12;
344 1.14 yamt year++;
345 1.14 yamt newyear = 1;
346 1.13 perry }
347 1.14 yamt } while (year < endyear || (year == endyear && month <= endmonth));
348 1.13 perry }
349 1.13 perry
350 1.1 cgd /*
351 1.1 cgd * day_array --
352 1.1 cgd * Fill in an array of 42 integers with a calendar. Assume for a moment
353 1.1 cgd * that you took the (maximum) 6 rows in a calendar and stretched them
354 1.1 cgd * out end to end. You would have 42 numbers or spaces. This routine
355 1.1 cgd * builds that array for any month from Jan. 1 through Dec. 9999.
356 1.1 cgd */
357 1.6 glass void
358 1.12 perry day_array(int month, int year, int *days)
359 1.1 cgd {
360 1.6 glass int day, dw, dm;
361 1.1 cgd
362 1.1 cgd if (month == 9 && year == 1752) {
363 1.6 glass memmove(days,
364 1.6 glass julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
365 1.1 cgd return;
366 1.1 cgd }
367 1.6 glass memmove(days, empty, MAXDAYS * sizeof(int));
368 1.1 cgd dm = days_in_month[leap_year(year)][month];
369 1.1 cgd dw = day_in_week(1, month, year);
370 1.1 cgd day = julian ? day_in_year(1, month, year) : 1;
371 1.1 cgd while (dm--)
372 1.1 cgd days[dw++] = day++;
373 1.1 cgd }
374 1.1 cgd
375 1.1 cgd /*
376 1.1 cgd * day_in_year --
377 1.1 cgd * return the 1 based day number within the year
378 1.1 cgd */
379 1.6 glass int
380 1.12 perry day_in_year(int day, int month, int year)
381 1.1 cgd {
382 1.6 glass int i, leap;
383 1.1 cgd
384 1.1 cgd leap = leap_year(year);
385 1.1 cgd for (i = 1; i < month; i++)
386 1.1 cgd day += days_in_month[leap][i];
387 1.6 glass return (day);
388 1.1 cgd }
389 1.1 cgd
390 1.1 cgd /*
391 1.1 cgd * day_in_week
392 1.1 cgd * return the 0 based day number for any date from 1 Jan. 1 to
393 1.1 cgd * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
394 1.1 cgd * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
395 1.1 cgd * missing days.
396 1.1 cgd */
397 1.6 glass int
398 1.12 perry day_in_week(int day, int month, int year)
399 1.1 cgd {
400 1.1 cgd long temp;
401 1.1 cgd
402 1.1 cgd temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
403 1.1 cgd + day_in_year(day, month, year);
404 1.1 cgd if (temp < FIRST_MISSING_DAY)
405 1.6 glass return ((temp - 1 + SATURDAY) % 7);
406 1.1 cgd if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
407 1.6 glass return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
408 1.6 glass return (THURSDAY);
409 1.1 cgd }
410 1.1 cgd
411 1.6 glass void
412 1.12 perry ascii_day(char *p, int day)
413 1.1 cgd {
414 1.6 glass int display, val;
415 1.1 cgd static char *aday[] = {
416 1.1 cgd "",
417 1.1 cgd " 1", " 2", " 3", " 4", " 5", " 6", " 7",
418 1.1 cgd " 8", " 9", "10", "11", "12", "13", "14",
419 1.1 cgd "15", "16", "17", "18", "19", "20", "21",
420 1.1 cgd "22", "23", "24", "25", "26", "27", "28",
421 1.1 cgd "29", "30", "31",
422 1.1 cgd };
423 1.1 cgd
424 1.1 cgd if (day == SPACE) {
425 1.1 cgd memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
426 1.1 cgd return;
427 1.1 cgd }
428 1.1 cgd if (julian) {
429 1.7 lukem if ((val = day / 100) != 0) {
430 1.1 cgd day %= 100;
431 1.1 cgd *p++ = val + '0';
432 1.1 cgd display = 1;
433 1.1 cgd } else {
434 1.1 cgd *p++ = ' ';
435 1.1 cgd display = 0;
436 1.1 cgd }
437 1.1 cgd val = day / 10;
438 1.1 cgd if (val || display)
439 1.1 cgd *p++ = val + '0';
440 1.1 cgd else
441 1.1 cgd *p++ = ' ';
442 1.1 cgd *p++ = day % 10 + '0';
443 1.1 cgd } else {
444 1.1 cgd *p++ = aday[day][0];
445 1.1 cgd *p++ = aday[day][1];
446 1.1 cgd }
447 1.1 cgd *p = ' ';
448 1.1 cgd }
449 1.1 cgd
450 1.6 glass void
451 1.12 perry trim_trailing_spaces(char *s)
452 1.1 cgd {
453 1.6 glass char *p;
454 1.1 cgd
455 1.6 glass for (p = s; *p; ++p)
456 1.6 glass continue;
457 1.11 christos while (p > s && isspace((unsigned char)*--p))
458 1.6 glass continue;
459 1.1 cgd if (p > s)
460 1.1 cgd ++p;
461 1.1 cgd *p = '\0';
462 1.1 cgd }
463 1.1 cgd
464 1.6 glass void
465 1.12 perry center(char *str, int len, int separate)
466 1.1 cgd {
467 1.6 glass
468 1.1 cgd len -= strlen(str);
469 1.1 cgd (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
470 1.1 cgd if (separate)
471 1.1 cgd (void)printf("%*s", separate, "");
472 1.1 cgd }
473 1.1 cgd
474 1.14 yamt int
475 1.14 yamt getnum(const char *p)
476 1.14 yamt {
477 1.14 yamt long result;
478 1.14 yamt char *ep;
479 1.14 yamt
480 1.14 yamt errno = 0;
481 1.14 yamt result = strtoul(p, &ep, 10);
482 1.14 yamt if (p[0] == '\0' || *ep != '\0')
483 1.14 yamt goto error;
484 1.14 yamt if (errno == ERANGE && result == ULONG_MAX)
485 1.14 yamt goto error;
486 1.14 yamt if (result > INT_MAX)
487 1.14 yamt goto error;
488 1.14 yamt
489 1.14 yamt return (int)result;
490 1.14 yamt
491 1.14 yamt error:
492 1.14 yamt errx(1, "bad number: %s", p);
493 1.14 yamt /*NOTREACHED*/
494 1.14 yamt }
495 1.14 yamt
496 1.6 glass void
497 1.12 perry usage(void)
498 1.1 cgd {
499 1.6 glass
500 1.14 yamt (void)fprintf(stderr,
501 1.14 yamt "usage: cal [-jy3] [-B before] [-A after] [[month] year]\n");
502 1.1 cgd exit(1);
503 1.1 cgd }
504