]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/cal.c
cal: use EXIT_* and program_invocation_short_name
[thirdparty/util-linux.git] / misc-utils / cal.c
1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kim Letkeman.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 /* 1999-02-01 Jean-Francois Bignolles: added option '-m' to display
38 * monday as the first day of the week.
39 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
40 * - added Native Language Support
41 *
42 * 2000-09-01 Michael Charles Pruznick <dummy@netwiz.net>
43 * Added "-3" option to print prev/next month with current.
44 * Added over-ridable default NUM_MONTHS and "-1" option to
45 * get traditional output when -3 is the default. I hope that
46 * enough people will like -3 as the default that one day the
47 * product can be shipped that way.
48 *
49 * 2001-05-07 Pablo Saratxaga <pablo@mandrakesoft.com>
50 * Fixed the bugs with multi-byte charset (zg: cjk, utf-8)
51 * displaying. made the 'month year' ("%s %d") header translatable
52 * so it can be adapted to conventions used by different languages
53 * added support to read "first_weekday" locale information
54 * still to do: support for 'cal_direction' (will require a major
55 * rewrite of the displaying) and proper handling of RTL scripts
56 */
57
58 #include <sys/types.h>
59
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <time.h>
65 #include <unistd.h>
66 #include <err.h>
67 #include <errno.h>
68
69 #include "c.h"
70 #include "nls.h"
71 #include "mbsalign.h"
72
73 #if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW)
74
75 #ifdef HAVE_NCURSES_H
76 #include <ncurses.h>
77 #elif defined(HAVE_NCURSES_NCURSES_H)
78 #include <ncurses/ncurses.h>
79 #endif
80
81 #include <term.h> /* include after <curses.h> */
82
83 static void
84 my_setupterm(const char *term, int fildes, int *errret) {
85 setupterm((char*)term, fildes, errret);
86 }
87
88 static void
89 my_putstring(char *s) {
90 putp(s);
91 }
92
93 static const char *
94 my_tgetstr(char *s, char *ss) {
95 const char* ret = tigetstr(ss);
96 if (!ret || ret==(char*)-1)
97 return "";
98 else
99 return ret;
100 }
101
102 #elif defined(HAVE_LIBTERMCAP)
103
104 #include <termcap.h>
105
106 char termbuffer[4096];
107 char tcbuffer[4096];
108 char *strbuf = termbuffer;
109
110 static void
111 my_setupterm(const char *term, int fildes, int *errret) {
112 *errret = tgetent(tcbuffer, term);
113 }
114
115 static void
116 my_putstring(char *s) {
117 tputs (s, 1, putchar);
118 }
119
120 static const char *
121 my_tgetstr(char *s, char *ss) {
122 const char* ret = tgetstr(s, &strbuf);
123 if (!ret)
124 return "";
125 else
126 return ret;
127 }
128
129 #else /* ! (HAVE_LIBTERMCAP || HAVE_LIBNCURSES || HAVE_LIBNCURSESW) */
130
131 static void
132 my_putstring(char *s) {
133 fputs(s, stdout);
134 }
135
136 #endif
137
138
139 const char *term="";
140 const char *Senter="", *Sexit="";/* enter and exit standout mode */
141 int Slen; /* strlen of Senter+Sexit */
142 char *Hrow; /* pointer to highlighted row in month */
143
144 #ifdef HAVE_LANGINFO_H
145 # include <langinfo.h>
146 #endif
147
148 #include "widechar.h"
149
150 /* allow compile-time define to over-ride default */
151 #ifndef NUM_MONTHS
152 #define NUM_MONTHS 1
153 #endif
154
155 #if ( NUM_MONTHS != 1 && NUM_MONTHS !=3 )
156 #error NUM_MONTHS must be 1 or 3
157 #endif
158
159 #define THURSDAY 4 /* for reformation */
160 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
161
162 #define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
163 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
164
165 #define MAXDAYS 42 /* slots in a month array */
166 #define SPACE -1 /* used in day array */
167
168 static int days_in_month[2][13] = {
169 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
170 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
171 };
172
173 #define SEP1752_OFS 4 /* sep1752[4] is a Sunday */
174
175 /* 1 Sep 1752 is represented by sep1752[6] and j_sep1752[6] */
176 int sep1752[MAXDAYS+6] = {
177 SPACE, SPACE, SPACE, SPACE,
178 SPACE, SPACE, 1, 2, 14, 15, 16,
179 17, 18, 19, 20, 21, 22, 23,
180 24, 25, 26, 27, 28, 29, 30,
181 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
182 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
183 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
184 SPACE, SPACE
185 }, j_sep1752[MAXDAYS+6] = {
186 SPACE, SPACE, SPACE, SPACE,
187 SPACE, SPACE, 245, 246, 258, 259, 260,
188 261, 262, 263, 264, 265, 266, 267,
189 268, 269, 270, 271, 272, 273, 274,
190 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
191 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
192 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
193 SPACE, SPACE
194 }, empty[MAXDAYS] = {
195 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
196 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
197 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
198 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
199 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
200 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE
201 };
202
203 #define DAY_LEN 3 /* 3 spaces per day */
204 #define J_DAY_LEN 4 /* 4 spaces per day */
205 #define WEEK_LEN 21 /* 7 days * 3 characters */
206 #define J_WEEK_LEN 28 /* 7 days * 4 characters */
207 #define HEAD_SEP 2 /* spaces between day headings */
208 #define J_HEAD_SEP 2
209
210 /* utf-8 can have up to 6 bytes per char; and an extra byte for ending \0 */
211 char day_headings[WEEK_LEN*6+1];
212 /* weekstart = 1 => " M Tu W Th F S S " */
213 char j_day_headings[J_WEEK_LEN*6+1];
214 /* weekstart = 1 => " M Tu W Th F S S " */
215 const char *full_month[12];
216
217 /* leap year -- account for gregorian reformation in 1752 */
218 #define leap_year(yr) \
219 ((yr) <= 1752 ? !((yr) % 4) : \
220 (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
221
222 /* number of centuries since 1700, not inclusive */
223 #define centuries_since_1700(yr) \
224 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
225
226 /* number of centuries since 1700 whose modulo of 400 is 0 */
227 #define quad_centuries_since_1700(yr) \
228 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
229
230 /* number of leap years between year 1 and this year, not inclusive */
231 #define leap_years_since_year_1(yr) \
232 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
233
234 /* 0 => sunday, 1 => monday */
235 int weekstart=0;
236 int julian;
237
238 #define TODAY_FLAG 0x400 /* flag day for highlighting */
239
240 #define FMT_ST_LINES 8
241 #define FMT_ST_CHARS 300 /* 90 suffices in most locales */
242 struct fmt_st
243 {
244 char s[FMT_ST_LINES][FMT_ST_CHARS];
245 };
246
247 char * ascii_day(char *, int);
248 int center_str(const char* src, char* dest, size_t dest_size, size_t width);
249 void center(const char *, size_t, int);
250 void day_array(int, int, int, int *);
251 int day_in_week(int, int, int);
252 int day_in_year(int, int, int);
253 void yearly(int, int);
254 void j_yearly(int, int);
255 void do_monthly(int, int, int, struct fmt_st*);
256 void monthly(int, int, int);
257 void monthly3(int, int, int);
258 void trim_trailing_spaces(char *);
259 void usage(void);
260 void headers_init(void);
261
262 int
263 main(int argc, char **argv) {
264 struct tm *local_time;
265 time_t now;
266 int ch, day, month, year, yflag;
267 int num_months = NUM_MONTHS;
268
269 setlocale(LC_ALL, "");
270 bindtextdomain(PACKAGE, LOCALEDIR);
271 textdomain(PACKAGE);
272
273 #if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBTERMCAP)
274 if ((term = getenv("TERM"))) {
275 int ret;
276 my_setupterm(term, 1, &ret);
277 if (ret > 0) {
278 Senter = my_tgetstr("so","smso");
279 Sexit = my_tgetstr("se","rmso");
280 Slen = strlen(Senter) + strlen(Sexit);
281 }
282 }
283 #endif
284
285 /*
286 * The traditional Unix cal utility starts the week at Sunday,
287 * while ISO 8601 starts at Monday. We read the start day from
288 * the locale database, which can be overridden with the
289 * -s (Sunday) or -m (Monday) options.
290 */
291 #if HAVE_DECL__NL_TIME_WEEK_1STDAY
292 /*
293 * You need to use 2 locale variables to get the first day of the week.
294 * This is needed to support first_weekday=2 and first_workday=1 for
295 * the rare case where working days span across 2 weeks.
296 * This shell script shows the combinations and calculations involved:
297
298 for LANG in en_US ru_RU fr_FR csb_PL POSIX; do
299 printf "%s:\t%s + %s -1 = " $LANG $(locale week-1stday first_weekday)
300 date -d"$(locale week-1stday) +$(($(locale first_weekday)-1))day" +%w
301 done
302
303 en_US: 19971130 + 1 -1 = 0 #0 = sunday
304 ru_RU: 19971130 + 2 -1 = 1
305 fr_FR: 19971201 + 1 -1 = 1
306 csb_PL: 19971201 + 2 -1 = 2
307 POSIX: 19971201 + 7 -1 = 0
308 */
309 {
310 int wfd;
311 union { unsigned int word; char *string; } val;
312 val.string = nl_langinfo(_NL_TIME_WEEK_1STDAY);
313
314 wfd = val.word;
315 wfd = day_in_week(wfd % 100, (wfd / 100) % 100, wfd / (100 * 100));
316 weekstart = (wfd + *nl_langinfo(_NL_TIME_FIRST_WEEKDAY) - 1) % 7;
317 }
318 #endif
319
320 yflag = 0;
321 while ((ch = getopt(argc, argv, "13mjsyV")) != -1)
322 switch(ch) {
323 case '1':
324 num_months = 1; /* default */
325 break;
326 case '3':
327 num_months = 3;
328 break;
329 case 's':
330 weekstart = 0; /* default */
331 break;
332 case 'm':
333 weekstart = 1;
334 break;
335 case 'j':
336 julian = 1;
337 break;
338 case 'y':
339 yflag = 1;
340 break;
341 case 'V':
342 printf(_("%s from %s\n"), program_invocation_short_name,
343 PACKAGE_STRING);
344 return EXIT_SUCCESS;
345 case '?':
346 default:
347 usage();
348 }
349 argc -= optind;
350 argv += optind;
351
352 time(&now);
353 local_time = localtime(&now);
354
355 day = month = year = 0;
356 switch(argc) {
357 case 3:
358 if ((day = atoi(*argv++)) < 1 || day > 31)
359 errx(1, _("illegal day value: use 1-%d"), 31);
360 /* FALLTHROUGH */
361 case 2:
362 if ((month = atoi(*argv++)) < 1 || month > 12)
363 errx(1, _("illegal month value: use 1-12"));
364 /* FALLTHROUGH */
365 case 1:
366 if ((year = atoi(*argv)) < 1 || year > 9999)
367 errx(1, _("illegal year value: use 1-9999"));
368 if (day) {
369 int dm = days_in_month[leap_year(year)][month];
370 if (day > dm)
371 errx(1, _("illegal day value: use 1-%d"), dm);
372 day = day_in_year(day, month, year);
373 } else if ((local_time->tm_year + 1900) == year) {
374 day = local_time->tm_yday + 1;
375 }
376 if (!month)
377 yflag=1;
378 break;
379 case 0:
380 day = local_time->tm_yday + 1;
381 year = local_time->tm_year + 1900;
382 month = local_time->tm_mon + 1;
383 break;
384 default:
385 usage();
386 }
387 headers_init();
388
389 if (!isatty(1))
390 day = 0; /* don't highlight */
391
392 if (yflag && julian)
393 j_yearly(day, year);
394 else if (yflag)
395 yearly(day, year);
396 else if (num_months == 1)
397 monthly(day, month, year);
398 else if (num_months == 3)
399 monthly3(day, month, year);
400
401 return EXIT_SUCCESS;
402 }
403
404 void headers_init(void)
405 {
406 int i, wd;
407 char *cur_dh = day_headings, *cur_j_dh = j_day_headings;
408
409 strcpy(day_headings,"");
410 strcpy(j_day_headings,"");
411
412 #ifdef HAVE_LANGINFO_H
413 # define weekday(wd) nl_langinfo(ABDAY_1+wd)
414 #else
415 # define weekday(wd) _time_info->abbrev_wkday[wd]
416 #endif
417
418 for(i = 0 ; i < 7 ; i++ ) {
419 ssize_t space_left;
420 wd = (i + weekstart) % 7;
421
422 if (i)
423 strcat(cur_dh++, " ");
424 space_left = sizeof(day_headings) - (cur_dh - day_headings);
425 if(space_left <= 2)
426 break;
427 cur_dh += center_str(weekday(wd), cur_dh, space_left, 2);
428
429 if (i)
430 strcat(cur_j_dh++, " ");
431 space_left = sizeof(j_day_headings) - (cur_j_dh - j_day_headings);
432 if(space_left <= 3)
433 break;
434 cur_j_dh += center_str(weekday(wd), cur_j_dh, space_left, 3);
435 }
436
437 #undef weekday
438
439 for (i = 0; i < 12; i++) {
440 #ifdef HAVE_LANGINFO_H
441 full_month[i] = nl_langinfo(MON_1+i);
442 #else
443 full_month[i] = _time_info->full_month[i];
444 #endif
445 }
446 }
447
448 void
449 do_monthly(int day, int month, int year, struct fmt_st *out) {
450 int col, row, days[MAXDAYS];
451 char *p, lineout[FMT_ST_CHARS];
452 int width = (julian ? J_WEEK_LEN : WEEK_LEN) - 1;
453
454 day_array(day, month, year, days);
455
456 /*
457 * %s is the month name, %d the year number.
458 * you can change the order and/or add something here; eg for
459 * Basque the translation should be: "%2$dko %1$s", and
460 * the Vietnamese should be "%s na(m %d", etc.
461 */
462 snprintf(lineout, sizeof(lineout), _("%s %d"),
463 full_month[month - 1], year);
464 center_str(lineout, out->s[0], ARRAY_SIZE(out->s[0]), width);
465
466 snprintf(out->s[1], FMT_ST_CHARS, "%s",
467 julian ? j_day_headings : day_headings);
468 for (row = 0; row < 6; row++) {
469 int has_hl = 0;
470 for (col = 0, p = lineout; col < 7; col++) {
471 int xd = days[row * 7 + col];
472 if (xd != SPACE && (xd & TODAY_FLAG))
473 has_hl = 1;
474 p = ascii_day(p, xd);
475 }
476 *p = '\0';
477 trim_trailing_spaces(lineout);
478 snprintf(out->s[row+2], FMT_ST_CHARS, "%s", lineout);
479 if (has_hl)
480 Hrow = out->s[row+2];
481 }
482 }
483
484 void
485 monthly(int day, int month, int year) {
486 int i;
487 struct fmt_st out;
488
489 do_monthly(day, month, year, &out);
490 for (i = 0; i < FMT_ST_LINES; i++) {
491 my_putstring(out.s[i]);
492 putchar('\n');
493 }
494 }
495
496 void
497 monthly3(int day, int month, int year) {
498 char lineout[FMT_ST_CHARS];
499 int i;
500 int width;
501 struct fmt_st out_prev;
502 struct fmt_st out_curm;
503 struct fmt_st out_next;
504 int prev_month, prev_year;
505 int next_month, next_year;
506
507 if (month == 1) {
508 prev_month = 12;
509 prev_year = year - 1;
510 } else {
511 prev_month = month - 1;
512 prev_year = year;
513 }
514 if (month == 12) {
515 next_month = 1;
516 next_year = year + 1;
517 } else {
518 next_month = month + 1;
519 next_year = year;
520 }
521
522 do_monthly(day, prev_month, prev_year, &out_prev);
523 do_monthly(day, month, year, &out_curm);
524 do_monthly(day, next_month, next_year, &out_next);
525
526 width = (julian ? J_WEEK_LEN : WEEK_LEN) -1;
527 for (i = 0; i < 2; i++)
528 printf("%s %s %s\n", out_prev.s[i], out_curm.s[i], out_next.s[i]);
529 for (i = 2; i < FMT_ST_LINES; i++) {
530 int w1, w2, w3;
531 w1 = w2 = w3 = width;
532
533 #if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBTERMCAP)
534 /* adjust width to allow for non printable characters */
535 w1 += (out_prev.s[i] == Hrow ? Slen : 0);
536 w2 += (out_curm.s[i] == Hrow ? Slen : 0);
537 w3 += (out_next.s[i] == Hrow ? Slen : 0);
538 #endif
539 snprintf(lineout, sizeof(lineout), "%-*s %-*s %-*s\n",
540 w1, out_prev.s[i],
541 w2, out_curm.s[i],
542 w3, out_next.s[i]);
543
544 my_putstring(lineout);
545 }
546 }
547
548 void
549 j_yearly(int day, int year) {
550 int col, *dp, i, month, row, which_cal;
551 int days[12][MAXDAYS];
552 char *p, lineout[80];
553
554 snprintf(lineout, sizeof(lineout), "%d", year);
555 center(lineout, J_WEEK_LEN*2 + J_HEAD_SEP - 1, 0);
556 printf("\n\n");
557
558 for (i = 0; i < 12; i++)
559 day_array(day, i + 1, year, days[i]);
560 memset(lineout, ' ', sizeof(lineout) - 1);
561 lineout[sizeof(lineout) - 1] = '\0';
562 for (month = 0; month < 12; month += 2) {
563 center(full_month[month], J_WEEK_LEN-1, J_HEAD_SEP+1);
564 center(full_month[month + 1], J_WEEK_LEN-1, 0);
565 printf("\n%s%*s %s\n", j_day_headings, J_HEAD_SEP, "",
566 j_day_headings);
567 for (row = 0; row < 6; row++) {
568 p = lineout;
569 for (which_cal = 0; which_cal < 2; which_cal++) {
570 dp = &days[month + which_cal][row * 7];
571 for (col = 0; col < 7; col++)
572 p = ascii_day(p, *dp++);
573 p += sprintf(p, " ");
574 }
575 *p = '\0';
576 trim_trailing_spaces(lineout);
577 my_putstring(lineout);
578 putchar('\n');
579 }
580 }
581 printf("\n");
582 }
583
584 void
585 yearly(int day, int year) {
586 int col, *dp, i, month, row, which_cal;
587 int days[12][MAXDAYS];
588 char *p, lineout[100];
589
590 snprintf(lineout, sizeof(lineout), "%d", year);
591 center(lineout, WEEK_LEN*3 + HEAD_SEP*2 - 1, 0);
592 printf("\n\n");
593
594 for (i = 0; i < 12; i++)
595 day_array(day, i + 1, year, days[i]);
596 memset(lineout, ' ', sizeof(lineout) - 1);
597 lineout[sizeof(lineout) - 1] = '\0';
598 for (month = 0; month < 12; month += 3) {
599 center(full_month[month], WEEK_LEN-1, HEAD_SEP+1);
600 center(full_month[month + 1], WEEK_LEN-1, HEAD_SEP+1);
601 center(full_month[month + 2], WEEK_LEN-1, 0);
602 printf("\n%s%*s %s%*s %s\n", day_headings, HEAD_SEP,
603 "", day_headings, HEAD_SEP, "", day_headings);
604 for (row = 0; row < 6; row++) {
605 p = lineout;
606 for (which_cal = 0; which_cal < 3; which_cal++) {
607 dp = &days[month + which_cal][row * 7];
608 for (col = 0; col < 7; col++)
609 p = ascii_day(p, *dp++);
610 p += sprintf(p, " ");
611 }
612 *p = '\0';
613 trim_trailing_spaces(lineout);
614 my_putstring(lineout);
615 putchar('\n');
616 }
617 }
618 putchar('\n');
619 }
620
621 /*
622 * day_array --
623 * Fill in an array of 42 integers with a calendar. Assume for a moment
624 * that you took the (maximum) 6 rows in a calendar and stretched them
625 * out end to end. You would have 42 numbers or spaces. This routine
626 * builds that array for any month from Jan. 1 through Dec. 9999.
627 */
628 void
629 day_array(int day, int month, int year, int *days) {
630 int julday, daynum, dw, dm;
631 int *d_sep1752;
632
633 if (month == 9 && year == 1752) {
634 int sep1752_ofs = (weekstart + SEP1752_OFS) % 7;
635 d_sep1752 = julian ? j_sep1752 : sep1752;
636 memcpy(days, d_sep1752 + sep1752_ofs, MAXDAYS * sizeof(int));
637 for (dm=0; dm<MAXDAYS; dm++)
638 if (j_sep1752[dm + sep1752_ofs] == day)
639 days[dm] |= TODAY_FLAG;
640 return;
641 }
642 memcpy(days, empty, MAXDAYS * sizeof(int));
643 dm = days_in_month[leap_year(year)][month];
644 dw = (day_in_week(1, month, year) - weekstart + 7) % 7;
645 julday = day_in_year(1, month, year);
646 daynum = julian ? julday : 1;
647 while (dm--) {
648 days[dw] = daynum++;
649 if (julday++ == day)
650 days[dw] |= TODAY_FLAG;
651 dw++;
652 }
653 }
654
655 /*
656 * day_in_year --
657 * return the 1 based day number within the year
658 */
659 int
660 day_in_year(int day, int month, int year) {
661 int i, leap;
662
663 leap = leap_year(year);
664 for (i = 1; i < month; i++)
665 day += days_in_month[leap][i];
666 return day;
667 }
668
669 /*
670 * day_in_week
671 * return the 0 based day number for any date from 1 Jan. 1 to
672 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
673 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
674 * missing days.
675 */
676 int
677 day_in_week(int day, int month, int year) {
678 long temp;
679
680 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
681 + day_in_year(day, month, year);
682 if (temp < FIRST_MISSING_DAY)
683 return ((temp - 1 + SATURDAY) % 7);
684 if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
685 return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
686 return (THURSDAY);
687 }
688
689 char *
690 ascii_day(char *p, int day) {
691 int display, val;
692 int highlight = 0;
693 static char *aday[] = {
694 "",
695 " 1", " 2", " 3", " 4", " 5", " 6", " 7",
696 " 8", " 9", "10", "11", "12", "13", "14",
697 "15", "16", "17", "18", "19", "20", "21",
698 "22", "23", "24", "25", "26", "27", "28",
699 "29", "30", "31",
700 };
701
702 if (day == SPACE) {
703 int len = julian ? J_DAY_LEN : DAY_LEN;
704 memset(p, ' ', len);
705 return p+len;
706 }
707 if (day & TODAY_FLAG) {
708 day &= ~TODAY_FLAG;
709 p += sprintf(p, "%s", Senter);
710 highlight = 1;
711 }
712 if (julian) {
713 if ((val = day / 100)) {
714 day %= 100;
715 *p++ = val + '0';
716 display = 1;
717 } else {
718 *p++ = ' ';
719 display = 0;
720 }
721 val = day / 10;
722 if (val || display)
723 *p++ = val + '0';
724 else
725 *p++ = ' ';
726 *p++ = day % 10 + '0';
727 } else {
728 *p++ = aday[day][0];
729 *p++ = aday[day][1];
730 }
731 if (highlight)
732 p += sprintf(p, "%s", Sexit);
733 *p++ = ' ';
734 return p;
735 }
736
737 void
738 trim_trailing_spaces(s)
739 char *s;
740 {
741 char *p;
742
743 for (p = s; *p; ++p)
744 continue;
745 while (p > s && isspace(*--p))
746 continue;
747 if (p > s)
748 ++p;
749 *p = '\0';
750 }
751
752 /*
753 * Center string, handling multibyte characters appropriately.
754 * In addition if the string is too large for the width it's truncated.
755 * The number of trailing spaces may be 1 less than the number of leading spaces.
756 */
757 int
758 center_str(const char* src, char* dest, size_t dest_size, size_t width)
759 {
760 return mbsalign(src, dest, dest_size, &width,
761 MBS_ALIGN_CENTER, MBA_UNIBYTE_FALLBACK);
762 }
763
764 void
765 center(str, len, separate)
766 const char *str;
767 size_t len;
768 int separate;
769 {
770 char lineout[FMT_ST_CHARS];
771 center_str(str, lineout, ARRAY_SIZE(lineout), len);
772 fputs(lineout, stdout);
773 if (separate)
774 printf("%*s", separate, "");
775 }
776
777 void
778 usage()
779 {
780
781 fprintf(stderr, _("usage: %s [-13smjyV] [[[day] month] year]\n"),
782 program_invocation_short_name);
783 exit(EXIT_FAILURE);
784 }