]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/cal.c
cal: add test code
[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 <locale.h>
67 #include "errs.h"
68 #include "nls.h"
69
70 #if defined(HAVE_NCURSES)
71
72 #ifdef HAVE_NCURSES_H
73 #include <ncurses.h>
74 #elif defined(HAVE_NCURSES_NCURSES_H)
75 #include <ncurses/ncurses.h>
76 #endif
77
78 #include <term.h> /* include after <curses.h> */
79
80 static void
81 my_setupterm(const char *term, int fildes, int *errret) {
82 setupterm((char*)term, fildes, errret);
83 }
84
85 static void
86 my_putstring(char *s) {
87 putp(s);
88 }
89
90 static const char *
91 my_tgetstr(char *s, char *ss) {
92 const char* ret = tigetstr(ss);
93 if (!ret || ret==(char*)-1)
94 return "";
95 else
96 return ret;
97 }
98
99 #elif defined(HAVE_LIBTERMCAP)
100
101 #include <termcap.h>
102
103 char termbuffer[4096];
104 char tcbuffer[4096];
105 char *strbuf = termbuffer;
106
107 static void
108 my_setupterm(const char *term, int fildes, int *errret) {
109 *errret = tgetent(tcbuffer, term);
110 }
111
112 static void
113 my_putstring(char *s) {
114 tputs (s, 1, putchar);
115 }
116
117 static const char *
118 my_tgetstr(char *s, char *ss) {
119 const char* ret = tgetstr(s, &strbuf);
120 if (!ret)
121 return "";
122 else
123 return ret;
124 }
125
126 #else /* ! (HAVE_LIBTERMCAP || HAVE_NCURSES) */
127
128 static void
129 my_putstring(char *s) {
130 fputs(s, stdout);
131 }
132
133 #endif
134
135
136 const char *term="";
137 const char *Senter="", *Sexit="";/* enter and exit standout mode */
138 int Slen; /* strlen of Senter+Sexit */
139 char *Hrow; /* pointer to highlighted row in month */
140
141 #ifdef HAVE_LANGINFO_H
142 # include <langinfo.h>
143 #else
144 # include <localeinfo.h> /* libc4 only */
145 #endif
146
147 #include "widechar.h"
148
149 #define SIZE(a) (sizeof(a)/sizeof((a)[0]))
150
151 /* allow compile-time define to over-ride default */
152 #ifndef NUM_MONTHS
153 #define NUM_MONTHS 1
154 #endif
155
156 #if ( NUM_MONTHS != 1 && NUM_MONTHS !=3 )
157 #error NUM_MONTHS must be 1 or 3
158 #endif
159
160 #define THURSDAY 4 /* for reformation */
161 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */
162
163 #define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
164 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
165
166 #define MAXDAYS 43 /* max slots in a month array */
167 #define SPACE -1 /* used in day array */
168
169 static int days_in_month[2][13] = {
170 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
171 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
172 };
173
174 int sep1752[MAXDAYS] = {
175 SPACE, SPACE, 1, 2, 14, 15, 16,
176 17, 18, 19, 20, 21, 22, 23,
177 24, 25, 26, 27, 28, 29, 30,
178 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
179 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
180 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
181 SPACE
182 }, j_sep1752[MAXDAYS] = {
183 SPACE, SPACE, 245, 246, 258, 259, 260,
184 261, 262, 263, 264, 265, 266, 267,
185 268, 269, 270, 271, 272, 273, 274,
186 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
187 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
188 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
189 SPACE
190 }, empty[MAXDAYS] = {
191 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
192 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
193 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
194 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
195 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
196 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE,
197 SPACE
198 };
199
200 #define DAY_LEN 3 /* 3 spaces per day */
201 #define J_DAY_LEN 4 /* 4 spaces per day */
202 #define WEEK_LEN 21 /* 7 days * 3 characters */
203 #define J_WEEK_LEN 28 /* 7 days * 4 characters */
204 #define HEAD_SEP 2 /* spaces between day headings */
205 #define J_HEAD_SEP 2
206
207 /* utf-8 can have up to 6 bytes per char; and an extra byte for ending \0 */
208 char day_headings[WEEK_LEN*6+1];
209 /* week1stday = 1 => " M Tu W Th F S S " */
210 char j_day_headings[J_WEEK_LEN*6+1];
211 /* week1stday = 1 => " M Tu W Th F S S " */
212 const char *full_month[12];
213
214 /* leap year -- account for gregorian reformation in 1752 */
215 #define leap_year(yr) \
216 ((yr) <= 1752 ? !((yr) % 4) : \
217 (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
218
219 /* number of centuries since 1700, not inclusive */
220 #define centuries_since_1700(yr) \
221 ((yr) > 1700 ? (yr) / 100 - 17 : 0)
222
223 /* number of centuries since 1700 whose modulo of 400 is 0 */
224 #define quad_centuries_since_1700(yr) \
225 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
226
227 /* number of leap years between year 1 and this year, not inclusive */
228 #define leap_years_since_year_1(yr) \
229 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
230
231 /* 0 => sunday (default), 1 => monday */
232 int week1stday=0;
233 int julian;
234
235 #define TODAY_FLAG 0x400 /* flag day for highlighting */
236
237 #define FMT_ST_LINES 8
238 #define FMT_ST_CHARS 300 /* 90 suffices in most locales */
239 struct fmt_st
240 {
241 char s[FMT_ST_LINES][FMT_ST_CHARS];
242 };
243
244 char * ascii_day(char *, int);
245 void center_str(const char* src, char* dest, size_t dest_size, int width);
246 void center(const char *, int, int);
247 void day_array(int, int, int, int *);
248 int day_in_week(int, int, int);
249 int day_in_year(int, int, int);
250 void yearly(int, int);
251 void j_yearly(int, int);
252 void do_monthly(int, int, int, struct fmt_st*);
253 void monthly(int, int, int);
254 void monthly3(int, int, int);
255 void trim_trailing_spaces(char *);
256 void usage(void);
257 void headers_init(void);
258 extern char *__progname;
259
260 int
261 main(int argc, char **argv) {
262 struct tm *local_time;
263 time_t now;
264 int ch, day, month, year, yflag;
265 char *progname, *p;
266 int num_months = NUM_MONTHS;
267
268 progname = argv[0];
269 if ((p = strrchr(progname, '/')) != NULL)
270 progname = p+1;
271 __progname = progname;
272
273 setlocale(LC_ALL, "");
274 bindtextdomain(PACKAGE, LOCALEDIR);
275 textdomain(PACKAGE);
276
277 #if defined(HAVE_NCURSES) || defined(HAVE_LIBTERMCAP)
278 if ((term = getenv("TERM"))) {
279 int ret;
280 my_setupterm(term, 1, &ret);
281 if (ret > 0) {
282 Senter = my_tgetstr("so","smso");
283 Sexit = my_tgetstr("se","rmso");
284 Slen = strlen(Senter) + strlen(Sexit);
285 }
286 }
287 #endif
288
289 #if 0 /* setting week1stday is against man page */
290 /*
291 * What *is* the first day of the week? Note that glibc does not
292 * provide any information today, it (almost) always answers Monday.
293 * Sunday is the Jewish and Christian tradition.
294 * Sometimes an answer is built into the language:
295 * German calls Wednesday "Mittwoch", so starts at Sunday.
296 * Portuguese calls Monday "segunda-feira", so starts at Sunday.
297 * Russian calls Friday "pyatnitsa", so starts at Monday.
298 * ISO 8601 decided to start at Monday.
299 *
300 * The traditional Unix cal utility starts at Sunday.
301 * We start at Sunday and have an option -m for starting at Monday.
302 *
303 * At some future time this may become -s for Sunday, -m for Monday,
304 * no option for glibc-determined locale-dependent version.
305 */
306 #ifdef HAVE_LANGINFO_H
307 week1stday = (int)(nl_langinfo(_NL_TIME_FIRST_WEEKDAY))[0];
308 #endif
309 #endif
310
311 yflag = 0;
312 while ((ch = getopt(argc, argv, "13mjsyV")) != -1)
313 switch(ch) {
314 case '1':
315 num_months = 1; /* default */
316 break;
317 case '3':
318 num_months = 3;
319 break;
320 case 's':
321 week1stday = 0; /* default */
322 break;
323 case 'm':
324 week1stday = 1;
325 break;
326 case 'j':
327 julian = 1;
328 break;
329 case 'y':
330 yflag = 1;
331 break;
332 case 'V':
333 printf(_("%s from %s\n"),
334 progname, PACKAGE_STRING);
335 return 0;
336 case '?':
337 default:
338 usage();
339 }
340 argc -= optind;
341 argv += optind;
342
343 day = month = year = 0;
344 switch(argc) {
345 case 2:
346 if ((month = atoi(*argv++)) < 1 || month > 12)
347 errx(1, _("illegal month value: use 1-12"));
348 /* FALLTHROUGH */
349 case 1:
350 if ((year = atoi(*argv)) < 1 || year > 9999)
351 errx(1, _("illegal year value: use 1-9999"));
352 break;
353 case 0:
354 {
355 #ifdef TEST_CAL
356 char *e = getenv("TEST_TIME");
357
358 if (e && isdigit((unsigned char) *e))
359 now = atol(e);
360 else
361 #endif
362 time(&now);
363 }
364 local_time = localtime(&now);
365 if (isatty(1))
366 day = local_time->tm_yday + 1;
367 year = local_time->tm_year + 1900;
368 if (!yflag)
369 month = local_time->tm_mon + 1;
370 break;
371 default:
372 usage();
373 }
374 headers_init();
375
376 if (month && num_months == 1)
377 monthly(day, month, year);
378 else if (month && num_months == 3)
379 monthly3(day, month, year);
380 else if (julian)
381 j_yearly(day, year);
382 else
383 yearly(day, year);
384 exit(0);
385 }
386
387 #ifndef HAVE_WIDECHAR
388 static char *eos(char *s) {
389 while (s && *s)
390 s++;
391 return s;
392 }
393 #endif
394
395 void headers_init(void)
396 {
397 int i, wd;
398 #ifdef HAVE_WIDECHAR
399 wchar_t day_headings_wc[22],j_day_headings_wc[29];
400 char *cur_dh = day_headings, *cur_j_dh = j_day_headings;
401
402 wcscpy(day_headings_wc, L"");
403 wcscpy(j_day_headings_wc, L"");
404 #endif
405
406 strcpy(day_headings,"");
407 strcpy(j_day_headings,"");
408
409 #ifdef HAVE_LANGINFO_H
410 # define weekday(wd) nl_langinfo(ABDAY_1+wd)
411 #else
412 # define weekday(wd) _time_info->abbrev_wkday[wd]
413 #endif
414
415 for(i = 0 ; i < 7 ; i++ ) {
416 ssize_t space_left;
417 wd = (i + week1stday) % 7;
418 #ifdef HAVE_WIDECHAR
419 swprintf(day_headings_wc, SIZE(day_headings_wc), L"%1.2s ", weekday(wd));
420 swprintf(j_day_headings_wc, SIZE(j_day_headings_wc), L"%3.3s ", weekday(wd));
421
422 space_left = sizeof(day_headings) - (cur_dh - day_headings);
423 if(space_left <= 0)
424 break;
425 cur_dh += wcstombs(cur_dh, day_headings_wc, space_left);
426
427 space_left = sizeof(j_day_headings)-(cur_j_dh-j_day_headings);
428 if(space_left <= 0)
429 break;
430 cur_j_dh += wcstombs(cur_j_dh,j_day_headings_wc, space_left);
431 #else
432 sprintf(eos(day_headings), "%2.2s ", weekday(wd));
433 sprintf(eos(j_day_headings), "%3.3s ", weekday(wd));
434 #endif
435 }
436
437 trim_trailing_spaces(day_headings);
438 trim_trailing_spaces(j_day_headings);
439 #undef weekday
440
441 for (i = 0; i < 12; i++) {
442 #ifdef HAVE_LANGINFO_H
443 full_month[i] = nl_langinfo(MON_1+i);
444 #else
445 full_month[i] = _time_info->full_month[i];
446 #endif
447 }
448 }
449
450 void
451 do_monthly(int day, int month, int year, struct fmt_st *out) {
452 int col, row, len, days[MAXDAYS];
453 char *p, lineout[FMT_ST_CHARS];
454 int width = (julian ? J_WEEK_LEN : WEEK_LEN) - 1;
455
456 day_array(day, month, year, days);
457
458 /*
459 * %s is the month name, %d the year number.
460 * you can change the order and/or add something here; eg for
461 * Basque the translation should be: "%2$dko %1$s", and
462 * the Vietnamese should be "%s na(m %d", etc.
463 */
464 len = sprintf(lineout, _("%s %d"), full_month[month - 1], year);
465 center_str(lineout, out->s[0], SIZE(out->s[0]), width);
466
467 sprintf(out->s[1],"%s",
468 julian ? j_day_headings : day_headings);
469 for (row = 0; row < 6; row++) {
470 int has_hl = 0;
471 for (col = 0, p = lineout; col < 7; col++) {
472 int xd = days[row * 7 + col];
473 if (xd != SPACE && (xd & TODAY_FLAG))
474 has_hl = 1;
475 p = ascii_day(p, xd);
476 }
477 *p = '\0';
478 trim_trailing_spaces(lineout);
479 sprintf(out->s[row+2], "%s", lineout);
480 if (has_hl)
481 Hrow = out->s[row+2];
482 }
483 }
484
485 void
486 monthly(int day, int month, int year) {
487 int i;
488 struct fmt_st out;
489
490 do_monthly(day, month, year, &out);
491 for (i = 0; i < FMT_ST_LINES; i++) {
492 my_putstring(out.s[i]);
493 putchar('\n');
494 }
495 }
496
497 void
498 monthly3(int day, int month, int year) {
499 char lineout[FMT_ST_CHARS];
500 int i;
501 int width;
502 struct fmt_st out_prev;
503 struct fmt_st out_curm;
504 struct fmt_st out_next;
505 int prev_month, prev_year;
506 int next_month, next_year;
507
508 if (month == 1) {
509 prev_month = 12;
510 prev_year = year - 1;
511 } else {
512 prev_month = month - 1;
513 prev_year = year;
514 }
515 if (month == 12) {
516 next_month = 1;
517 next_year = year + 1;
518 } else {
519 next_month = month + 1;
520 next_year = year;
521 }
522
523 do_monthly(day, prev_month, prev_year, &out_prev);
524 do_monthly(day, month, year, &out_curm);
525 do_monthly(day, next_month, next_year, &out_next);
526
527 width = (julian ? J_WEEK_LEN : WEEK_LEN) -1;
528 for (i = 0; i < 2; i++)
529 printf("%s %s %s\n", out_prev.s[i], out_curm.s[i], out_next.s[i]);
530 for (i = 2; i < FMT_ST_LINES; i++) {
531 int w1, w2, w3;
532 w1 = w2 = w3 = width;
533
534 #if defined(HAVE_NCURSES) || defined(HAVE_LIBTERMCAP)
535 /* adjust width to allow for non printable characters */
536 w1 += (out_prev.s[i] == Hrow ? Slen : 0);
537 w2 += (out_curm.s[i] == Hrow ? Slen : 0);
538 w3 += (out_next.s[i] == Hrow ? Slen : 0);
539 #endif
540 snprintf(lineout, SIZE(lineout), "%-*s %-*s %-*s\n",
541 w1, out_prev.s[i],
542 w2, out_curm.s[i],
543 w3, out_next.s[i]);
544
545 my_putstring(lineout);
546 }
547 }
548
549 void
550 j_yearly(int day, int year) {
551 int col, *dp, i, month, row, which_cal;
552 int days[12][MAXDAYS];
553 char *p, lineout[80];
554
555 sprintf(lineout, "%d", year);
556 center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
557 printf("\n\n");
558
559 for (i = 0; i < 12; i++)
560 day_array(day, i + 1, year, days[i]);
561 memset(lineout, ' ', sizeof(lineout) - 1);
562 lineout[sizeof(lineout) - 1] = '\0';
563 for (month = 0; month < 12; month += 2) {
564 center(full_month[month], J_WEEK_LEN, J_HEAD_SEP);
565 center(full_month[month + 1], J_WEEK_LEN, 0);
566 printf("\n%s%*s %s\n", j_day_headings, J_HEAD_SEP, "",
567 j_day_headings);
568 for (row = 0; row < 6; row++) {
569 p = lineout;
570 for (which_cal = 0; which_cal < 2; which_cal++) {
571 dp = &days[month + which_cal][row * 7];
572 for (col = 0; col < 7; col++)
573 p = ascii_day(p, *dp++);
574 p += sprintf(p, " ");
575 }
576 *p = '\0';
577 trim_trailing_spaces(lineout);
578 my_putstring(lineout);
579 putchar('\n');
580 }
581 }
582 printf("\n");
583 }
584
585 void
586 yearly(int day, int year) {
587 int col, *dp, i, month, row, which_cal;
588 int days[12][MAXDAYS];
589 char *p, lineout[100];
590
591 sprintf(lineout, "%d", year);
592 center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
593 printf("\n\n");
594
595 for (i = 0; i < 12; i++)
596 day_array(day, i + 1, year, days[i]);
597 memset(lineout, ' ', sizeof(lineout) - 1);
598 lineout[sizeof(lineout) - 1] = '\0';
599 for (month = 0; month < 12; month += 3) {
600 center(full_month[month], WEEK_LEN, HEAD_SEP);
601 center(full_month[month + 1], WEEK_LEN, HEAD_SEP);
602 center(full_month[month + 2], WEEK_LEN, 0);
603 printf("\n%s%*s %s%*s %s\n", day_headings, HEAD_SEP,
604 "", day_headings, HEAD_SEP, "", day_headings);
605 for (row = 0; row < 6; row++) {
606 p = lineout;
607 for (which_cal = 0; which_cal < 3; which_cal++) {
608 dp = &days[month + which_cal][row * 7];
609 for (col = 0; col < 7; col++)
610 p = ascii_day(p, *dp++);
611 p += sprintf(p, " ");
612 }
613 *p = '\0';
614 trim_trailing_spaces(lineout);
615 my_putstring(lineout);
616 putchar('\n');
617 }
618 }
619 putchar('\n');
620 }
621
622 /*
623 * day_array --
624 * Fill in an array of 42 integers with a calendar. Assume for a moment
625 * that you took the (maximum) 6 rows in a calendar and stretched them
626 * out end to end. You would have 42 numbers or spaces. This routine
627 * builds that array for any month from Jan. 1 through Dec. 9999.
628 */
629 void
630 day_array(int day, int month, int year, int *days) {
631 int julday, daynum, dw, dm;
632 int *d_sep1752;
633
634 if (month == 9 && year == 1752) {
635 d_sep1752 = julian ? j_sep1752 : sep1752;
636 memcpy(days, d_sep1752 + week1stday, MAXDAYS * sizeof(int));
637 return;
638 }
639 memcpy(days, empty, MAXDAYS * sizeof(int));
640 dm = days_in_month[leap_year(year)][month];
641 dw = (day_in_week(1, month, year) - week1stday + 7) % 7;
642 julday = day_in_year(1, month, year);
643 daynum = julian ? julday : 1;
644 while (dm--) {
645 days[dw] = daynum++;
646 if (julday++ == day)
647 days[dw] |= TODAY_FLAG;
648 dw++;
649 }
650 }
651
652 /*
653 * day_in_year --
654 * return the 1 based day number within the year
655 */
656 int
657 day_in_year(int day, int month, int year) {
658 int i, leap;
659
660 leap = leap_year(year);
661 for (i = 1; i < month; i++)
662 day += days_in_month[leap][i];
663 return day;
664 }
665
666 /*
667 * day_in_week
668 * return the 0 based day number for any date from 1 Jan. 1 to
669 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
670 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
671 * missing days.
672 */
673 int
674 day_in_week(int day, int month, int year) {
675 long temp;
676
677 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
678 + day_in_year(day, month, year);
679 if (temp < FIRST_MISSING_DAY)
680 return ((temp - 1 + SATURDAY) % 7);
681 if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
682 return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
683 return (THURSDAY);
684 }
685
686 char *
687 ascii_day(char *p, int day) {
688 int display, val;
689 int highlight = 0;
690 static char *aday[] = {
691 "",
692 " 1", " 2", " 3", " 4", " 5", " 6", " 7",
693 " 8", " 9", "10", "11", "12", "13", "14",
694 "15", "16", "17", "18", "19", "20", "21",
695 "22", "23", "24", "25", "26", "27", "28",
696 "29", "30", "31",
697 };
698
699 if (day == SPACE) {
700 int len = julian ? J_DAY_LEN : DAY_LEN;
701 memset(p, ' ', len);
702 return p+len;
703 }
704 if (day & TODAY_FLAG) {
705 day &= ~TODAY_FLAG;
706 p += sprintf(p, Senter);
707 highlight = 1;
708 }
709 if (julian) {
710 if ((val = day / 100)) {
711 day %= 100;
712 *p++ = val + '0';
713 display = 1;
714 } else {
715 *p++ = ' ';
716 display = 0;
717 }
718 val = day / 10;
719 if (val || display)
720 *p++ = val + '0';
721 else
722 *p++ = ' ';
723 *p++ = day % 10 + '0';
724 } else {
725 *p++ = aday[day][0];
726 *p++ = aday[day][1];
727 }
728 if (highlight)
729 p += sprintf(p, Sexit);
730 *p++ = ' ';
731 return p;
732 }
733
734 void
735 trim_trailing_spaces(s)
736 char *s;
737 {
738 char *p;
739
740 for (p = s; *p; ++p)
741 continue;
742 while (p > s && isspace(*--p))
743 continue;
744 if (p > s)
745 ++p;
746 *p = '\0';
747 }
748
749 /*
750 * Center string, handling multibyte characters appropriately.
751 * In addition if the string is too large for the width it's truncated.
752 */
753 void
754 center_str(const char* src, char* dest, size_t dest_size, int width)
755 {
756 #ifdef HAVE_WIDECHAR
757 wchar_t str_wc[FMT_ST_CHARS];
758 #endif
759 char str[FMT_ST_CHARS];
760 const char* str_to_print=src;
761 int len, spaces, wide_char_enabled=0;
762
763 len = strlen(src);
764
765 #ifdef HAVE_WIDECHAR
766 if (mbstowcs(str_wc, src, FMT_ST_CHARS) > 0) {
767 wide_char_enabled = 1;
768 len = wcswidth(str_wc, SIZE(str_wc));
769 }
770 #endif
771 if (len > width) {
772 str_to_print=str;
773 if (wide_char_enabled) {
774 #ifdef HAVE_WIDECHAR
775 str_wc[width]=L'\0';
776 wcstombs(str, str_wc, SIZE(str));
777 #endif
778 } else {
779 strncpy(str, src, SIZE(str));
780 str[width]='\0';
781 }
782 }
783
784 spaces = width - len;
785 spaces = ( spaces < 0 ? 0 : spaces );
786
787 snprintf(dest, dest_size, "%*s%s%*s",
788 spaces / 2, "",
789 str_to_print,
790 spaces / 2 + spaces % 2, "" );
791 }
792
793 void
794 center(str, len, separate)
795 const char *str;
796 int len;
797 int separate;
798 {
799 char lineout[FMT_ST_CHARS];
800 center_str(str, lineout, SIZE(lineout), len);
801 fputs(lineout, stdout);
802 if (separate)
803 (void)printf("%*s", separate, "");
804 }
805
806 void
807 usage()
808 {
809
810 (void)fprintf(stderr, _("usage: cal [-13smjyV] [[month] year]\n"));
811 exit(1);
812 }