2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
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
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.
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
58 #include <sys/types.h>
70 #include "closestream.h"
76 #if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW)
77 # ifdef HAVE_NCURSES_H
79 # elif defined(HAVE_NCURSES_NCURSES_H)
80 # include <ncurses/ncurses.h>
84 static void my_setupterm(const char *term
, int fildes
, int *errret
)
86 setupterm((char *)term
, fildes
, errret
);
89 static void my_putstring(char *s
)
94 static const char *my_tgetstr(char *s
__attribute__((__unused__
)), char *ss
)
96 const char *ret
= tigetstr(ss
);
97 if (!ret
|| ret
== (char *)-1)
103 #elif defined(HAVE_LIBTERMCAP)
104 # include <termcap.h>
106 char termbuffer
[4096];
108 char *strbuf
= termbuffer
;
110 static void my_setupterm(const char *term
, int fildes
, int *errret
)
112 *errret
= tgetent(tcbuffer
, term
);
115 static void my_putstring(char *s
)
117 tputs(s
, 1, putchar
);
120 static const char *my_tgetstr(char *s
, char *ss
__attribute__((__unused__
)))
122 const char *ret
= tgetstr(s
, &strbuf
);
129 #else /* ! (HAVE_LIBTERMCAP || HAVE_LIBNCURSES || HAVE_LIBNCURSESW) */
131 static void my_putstring(char *s
)
136 #endif /* end of LIBTERMCAP / NCURSES */
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 */
144 #include "widechar.h"
146 /* allow compile-time define to over-ride default */
151 #if ( NUM_MONTHS != 1 && NUM_MONTHS !=3 )
152 #error NUM_MONTHS must be 1 or 3
166 #define FIRST_WEEKDAY SATURDAY /* Jan 1st, 1 was a Saturday */
167 #define REFORMATION_WEEKDAY THURSDAY /* after reformation it was Thursday */
168 #define REFORMATION_YEAR 1752 /* Signed-off-by: Lord Chesterfield */
169 #define REFORMATION_MONTH 9 /* September */
170 #define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
171 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */
173 #define DAYS_IN_YEAR 365 /* the common case, leap years are calculated */
174 #define MONTHS_IN_YEAR 12
175 #define DAYS_IN_MONTH 31
176 #define MAXDAYS 42 /* slots in a month array */
177 #define SPACE -1 /* used in day array */
179 #define SMALLEST_YEAR 1
180 #define GREATEST_YEAR 9999
182 #define DAY_LEN 3 /* 3 spaces per day */
183 #define WEEK_LEN (DAYS_IN_WEEK * DAY_LEN)
185 #define MONTH_COLS 3 /* month columns in year view */
187 #define J_DAY_LEN 4 /* 4 spaces per day */
188 #define J_WEEK_LEN (DAYS_IN_WEEK * J_DAY_LEN)
190 #define J_MONTH_COLS 2
192 #define TODAY_FLAG 0x400 /* flag day for highlighting */
194 #define FMT_ST_LINES 8
195 #define FMT_ST_CHARS 300 /* 90 suffices in most locales */
198 char s
[FMT_ST_LINES
][FMT_ST_CHARS
];
201 static int days_in_month
[2][13] = {
202 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
203 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
206 /* September 1752 is special, and has static assignments for both date
207 * and Julian representations. */
208 int d_sep1752
[MAXDAYS
/ 2] = {
209 SPACE
, SPACE
, 1, 2, 14, 15, 16,
210 17, 18, 19, 20, 21, 22, 23,
211 24, 25, 26, 27, 28, 29, 30
212 }, j_sep1752
[MAXDAYS
/ 2] = {
213 SPACE
, SPACE
, 245, 246, 258, 259, 260,
214 261, 262, 263, 264, 265, 266, 267,
215 268, 269, 270, 271, 272, 273, 274
216 }, empty
[MAXDAYS
] = {
217 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
218 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
219 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
220 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
221 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
,
222 SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
, SPACE
226 /* utf-8 can have up to 6 bytes per char; and an extra byte for ending \0 */
227 char day_headings
[J_WEEK_LEN
* 6 + 1];
228 /* weekstart = 1 => " M Tu W Th F S S " */
229 const char *full_month
[MONTHS_IN_YEAR
];
231 /* 0 => sunday, 1 => monday */
232 int weekstart
= SUNDAY
;
235 /* function prototypes */
236 static int leap_year(int year
);
237 static int centuries_since_1700(int year
, int centuries
);
238 static int leap_years_since_year_1(int year
);
239 char * ascii_day(char *, int);
240 int center_str(const char* src
, char* dest
, size_t dest_size
, size_t width
);
241 void center(const char *, size_t, int);
242 void day_array(int, int, int, int *);
243 int day_in_week(int, int, int);
244 int day_in_year(int, int, int);
245 void yearly(int, int, int);
246 void do_monthly(int, int, int, struct fmt_st
*);
247 void monthly(int, int, int);
248 void monthly3(int, int, int);
249 void trim_trailing_spaces(char *);
250 static void __attribute__ ((__noreturn__
)) usage(FILE * out
);
251 void headers_init(int);
254 main(int argc
, char **argv
) {
255 struct tm
*local_time
;
257 int ch
, day
= 0, month
= 0, year
= 0, yflag
= 0;
258 int num_months
= NUM_MONTHS
;
259 int colormode
= UL_COLORMODE_AUTO
;
262 OPT_COLOR
= CHAR_MAX
+ 1
265 static const struct option longopts
[] = {
266 {"one", no_argument
, NULL
, '1'},
267 {"three", no_argument
, NULL
, '3'},
268 {"sunday", no_argument
, NULL
, 's'},
269 {"monday", no_argument
, NULL
, 'm'},
270 {"julian", no_argument
, NULL
, 'j'},
271 {"year", no_argument
, NULL
, 'y'},
272 {"color", optional_argument
, NULL
, OPT_COLOR
},
273 {"version", no_argument
, NULL
, 'V'},
274 {"help", no_argument
, NULL
, 'h'},
278 setlocale(LC_ALL
, "");
279 bindtextdomain(PACKAGE
, LOCALEDIR
);
281 atexit(close_stdout
);
283 #if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBTERMCAP)
284 if ((term
= getenv("TERM"))) {
286 my_setupterm(term
, STDOUT_FILENO
, &ret
);
288 Senter
= my_tgetstr("so","smso");
289 Sexit
= my_tgetstr("se","rmso");
290 Slen
= strlen(Senter
) + strlen(Sexit
);
296 * The traditional Unix cal utility starts the week at Sunday,
297 * while ISO 8601 starts at Monday. We read the start day from
298 * the locale database, which can be overridden with the
299 * -s (Sunday) or -m (Monday) options.
301 #if HAVE_DECL__NL_TIME_WEEK_1STDAY
303 * You need to use 2 locale variables to get the first day of the week.
304 * This is needed to support first_weekday=2 and first_workday=1 for
305 * the rare case where working days span across 2 weeks.
306 * This shell script shows the combinations and calculations involved:
308 * for LANG in en_US ru_RU fr_FR csb_PL POSIX; do
309 * printf "%s:\t%s + %s -1 = " $LANG $(locale week-1stday first_weekday)
310 * date -d"$(locale week-1stday) +$(($(locale first_weekday)-1))day" +%w
313 * en_US: 19971130 + 1 -1 = 0 #0 = sunday
314 * ru_RU: 19971130 + 2 -1 = 1
315 * fr_FR: 19971201 + 1 -1 = 1
316 * csb_PL: 19971201 + 2 -1 = 2
317 * POSIX: 19971201 + 7 -1 = 0
321 union { unsigned int word
; char *string
; } val
;
322 val
.string
= nl_langinfo(_NL_TIME_WEEK_1STDAY
);
325 wfd
= day_in_week(wfd
% 100, (wfd
/ 100) % 100, wfd
/ (100 * 100));
326 weekstart
= (wfd
+ *nl_langinfo(_NL_TIME_FIRST_WEEKDAY
) - 1) % DAYS_IN_WEEK
;
330 while ((ch
= getopt_long(argc
, argv
, "13mjsyVh", longopts
, NULL
)) != -1)
333 num_months
= 1; /* default */
339 weekstart
= SUNDAY
; /* default */
352 char *p
= *optarg
== '=' ? optarg
+ 1 : optarg
;
353 colormode
= colormode_from_string(p
);
355 errx(EXIT_FAILURE
, _("unsupported color mode: '%s'"), p
);
359 printf(UTIL_LINUX_VERSION
);
371 local_time
= localtime(&now
);
375 day
= strtos32_or_err(*argv
++, _("illegal day value"));
376 if (day
< 1 || DAYS_IN_MONTH
< day
)
377 errx(EXIT_FAILURE
, _("illegal day value: use 1-%d"), DAYS_IN_MONTH
);
380 month
= strtos32_or_err(*argv
++, _("illegal month value: use 1-12"));
381 if (month
< 1 || MONTHS_IN_YEAR
< month
)
382 errx(EXIT_FAILURE
, _("illegal month value: use 1-12"));
385 year
= strtos32_or_err(*argv
++, _("illegal year value: use 1-9999"));
386 if (year
< SMALLEST_YEAR
|| GREATEST_YEAR
< year
)
387 errx(EXIT_FAILURE
, _("illegal year value: use 1-9999"));
389 int dm
= days_in_month
[leap_year(year
)][month
];
391 errx(EXIT_FAILURE
, _("illegal day value: use 1-%d"), dm
);
392 day
= day_in_year(day
, month
, year
);
393 } else if ((local_time
->tm_year
+ 1900) == year
) {
394 day
= local_time
->tm_yday
+ 1;
400 day
= local_time
->tm_yday
+ 1;
401 year
= local_time
->tm_year
+ 1900;
402 month
= local_time
->tm_mon
+ 1;
407 headers_init(julian
);
409 if (!colors_init(colormode
))
413 yearly(day
, year
, julian
);
414 else if (num_months
== 1)
415 monthly(day
, month
, year
);
416 else if (num_months
== 3)
417 monthly3(day
, month
, year
);
422 /* leap year -- account for gregorian reformation in 1752 */
423 static int leap_year(int year
)
425 if (year
<= REFORMATION_YEAR
)
428 return ( !(year
% 4) && (year
% 100) ) || !(year
% 400);
431 /* number of centuries since 1700 */
432 static int centuries_since_1700(int year
, int n
)
434 if (year
< REFORMATION_YEAR
)
437 return ((year
/ (100 * n
)) - ((REFORMATION_YEAR
/ 100) / n
));
440 /* number of leap years between year 1 and this year, not inclusive */
441 static int leap_years_since_year_1(int year
)
443 return (year
/ 4 - centuries_since_1700(year
, 1) +
444 centuries_since_1700(year
, 4));
447 void headers_init(int julian
)
449 int i
, wd
, spaces
= julian
? J_DAY_LEN
- 1 : DAY_LEN
- 1;
450 char *cur_dh
= day_headings
;
452 for (i
= 0; i
< DAYS_IN_WEEK
; i
++) {
454 wd
= (i
+ weekstart
) % DAYS_IN_WEEK
;
457 strcat(cur_dh
++, " ");
459 sizeof(day_headings
) - (cur_dh
- day_headings
);
460 if (space_left
<= spaces
)
463 center_str(nl_langinfo(ABDAY_1
+ wd
), cur_dh
,
467 for (i
= 0; i
< MONTHS_IN_YEAR
; i
++)
468 full_month
[i
] = nl_langinfo(MON_1
+ i
);
472 do_monthly(int day
, int month
, int year
, struct fmt_st
*out
) {
473 int col
, row
, days
[MAXDAYS
];
474 char *p
, lineout
[FMT_ST_CHARS
];
475 int width
= (julian
? J_WEEK_LEN
: WEEK_LEN
) - 1;
477 day_array(day
, month
, year
, days
);
480 * %s is the month name, %d the year number.
481 * you can change the order and/or add something here; eg for
482 * Basque the translation should be: "%2$dko %1$s", and
483 * the Vietnamese should be "%s na(m %d", etc.
485 snprintf(lineout
, sizeof(lineout
), _("%s %d"),
486 full_month
[month
- 1], year
);
487 center_str(lineout
, out
->s
[0], ARRAY_SIZE(out
->s
[0]), width
);
489 snprintf(out
->s
[1], FMT_ST_CHARS
, "%s", day_headings
);
490 for (row
= 0; row
< DAYS_IN_WEEK
- 1; row
++) {
492 for (col
= 0, p
= lineout
; col
< DAYS_IN_WEEK
; col
++) {
493 int xd
= days
[row
* DAYS_IN_WEEK
+ col
];
494 if (xd
!= SPACE
&& (xd
& TODAY_FLAG
))
496 p
= ascii_day(p
, xd
);
499 trim_trailing_spaces(lineout
);
500 snprintf(out
->s
[row
+2], FMT_ST_CHARS
, "%s", lineout
);
502 Hrow
= out
->s
[row
+2];
507 monthly(int day
, int month
, int year
) {
511 do_monthly(day
, month
, year
, &out
);
512 for (i
= 0; i
< FMT_ST_LINES
; i
++) {
513 my_putstring(out
.s
[i
]);
519 monthly3(int day
, int month
, int year
) {
520 char lineout
[FMT_ST_CHARS
];
523 struct fmt_st out_prev
;
524 struct fmt_st out_curm
;
525 struct fmt_st out_next
;
526 int prev_month
, prev_year
;
527 int next_month
, next_year
;
530 prev_month
= MONTHS_IN_YEAR
;
531 prev_year
= year
- 1;
533 prev_month
= month
- 1;
536 if (month
== MONTHS_IN_YEAR
) {
538 next_year
= year
+ 1;
540 next_month
= month
+ 1;
544 do_monthly(day
, prev_month
, prev_year
, &out_prev
);
545 do_monthly(day
, month
, year
, &out_curm
);
546 do_monthly(day
, next_month
, next_year
, &out_next
);
548 width
= (julian
? J_WEEK_LEN
: WEEK_LEN
) -1;
549 for (i
= 0; i
< 2; i
++) {
550 snprintf(lineout
, sizeof(lineout
),
551 "%s %s %s\n", out_prev
.s
[i
], out_curm
.s
[i
], out_next
.s
[i
]);
552 my_putstring(lineout
);
554 for (i
= 2; i
< FMT_ST_LINES
; i
++) {
556 w1
= w2
= w3
= width
;
558 #if defined(HAVE_LIBNCURSES) || defined(HAVE_LIBNCURSESW) || defined(HAVE_LIBTERMCAP)
559 /* adjust width to allow for non printable characters */
560 w1
+= (out_prev
.s
[i
] == Hrow
? Slen
: 0);
561 w2
+= (out_curm
.s
[i
] == Hrow
? Slen
: 0);
562 w3
+= (out_next
.s
[i
] == Hrow
? Slen
: 0);
564 snprintf(lineout
, sizeof(lineout
), "%-*s %-*s %-*s\n",
569 my_putstring(lineout
);
574 yearly(int day
, int year
, int julian
) {
575 int col
, *dp
, i
, month
, row
, which_cal
;
576 int maxrow
, sep_len
, week_len
;
577 int days
[MONTHS_IN_YEAR
][MAXDAYS
];
578 char *p
, lineout
[100];
581 maxrow
= J_MONTH_COLS
;
582 sep_len
= J_HEAD_SEP
;
583 week_len
= J_WEEK_LEN
;
589 snprintf(lineout
, sizeof(lineout
), "%d", year
);
590 /* 2013-04-28: The -1 near sep_len makes year header to be
591 * aligned exactly how it has been aligned for long time, but it
592 * is unexplainable. */
593 center(lineout
, (week_len
+ sep_len
) * maxrow
- sep_len
- 1, 0);
594 my_putstring("\n\n");
596 for (i
= 0; i
< MONTHS_IN_YEAR
; i
++)
597 day_array(day
, i
+ 1, year
, days
[i
]);
598 for (month
= 0; month
< MONTHS_IN_YEAR
; month
+= maxrow
) {
599 center(full_month
[month
], week_len
- 1, sep_len
+ 1);
601 center(full_month
[month
+ 1], week_len
- 1, 0);
603 center(full_month
[month
+ 1], week_len
- 1, sep_len
+ 1);
604 center(full_month
[month
+ 2], week_len
- 1, 0);
607 snprintf(lineout
, sizeof(lineout
),
608 "\n%s%*s %s\n", day_headings
, sep_len
, "", day_headings
);
610 snprintf(lineout
, sizeof(lineout
),
611 "\n%s%*s %s%*s %s\n", day_headings
, sep_len
,
612 "", day_headings
, sep_len
, "", day_headings
);
613 my_putstring(lineout
);
614 for (row
= 0; row
< DAYS_IN_WEEK
- 1; row
++) {
616 for (which_cal
= 0; which_cal
< maxrow
; which_cal
++) {
617 dp
= &days
[month
+ which_cal
][row
* DAYS_IN_WEEK
];
618 for (col
= 0; col
< DAYS_IN_WEEK
; col
++)
619 p
= ascii_day(p
, *dp
++);
620 p
+= sprintf(p
, " ");
623 trim_trailing_spaces(lineout
);
624 my_putstring(lineout
);
633 * Fill in an array of 42 integers with a calendar. Assume for a moment
634 * that you took the (maximum) 6 rows in a calendar and stretched them
635 * out end to end. You would have 42 numbers or spaces. This routine
636 * builds that array for any month from Jan. 1 through Dec. 9999.
639 day_array(int day
, int month
, int year
, int *days
) {
640 int julday
, daynum
, dw
, dm
;
643 memcpy(days
, empty
, MAXDAYS
* sizeof(int));
644 if (year
== REFORMATION_YEAR
&& month
== REFORMATION_MONTH
) {
645 sep1752
= julian
? j_sep1752
: d_sep1752
;
646 memcpy(days
, sep1752
+ weekstart
,
647 ((MAXDAYS
/ 2) - weekstart
) * sizeof(int));
648 for (dm
= 0; dm
< MAXDAYS
/ 2; dm
++)
649 if (j_sep1752
[dm
] == day
)
650 days
[dm
] |= TODAY_FLAG
;
653 dm
= days_in_month
[leap_year(year
)][month
];
654 dw
= (day_in_week(1, month
, year
) - weekstart
+ DAYS_IN_WEEK
) % DAYS_IN_WEEK
;
655 julday
= day_in_year(1, month
, year
);
656 daynum
= julian
? julday
: 1;
660 days
[dw
] |= TODAY_FLAG
;
667 * return the 1 based day number within the year
670 day_in_year(int day
, int month
, int year
) {
673 leap
= leap_year(year
);
674 for (i
= 1; i
< month
; i
++)
675 day
+= days_in_month
[leap
][i
];
681 * return the 0 based day number for any date from 1 Jan. 1 to
682 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates
683 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
687 day_in_week(int day
, int month
, int year
) {
691 (long)(year
- SMALLEST_YEAR
) * DAYS_IN_YEAR
+
692 leap_years_since_year_1(year
- SMALLEST_YEAR
)
693 + day_in_year(day
, month
, year
);
694 if (temp
< FIRST_MISSING_DAY
)
695 return ((temp
- 1 + FIRST_WEEKDAY
) % DAYS_IN_WEEK
);
696 if (temp
>= (FIRST_MISSING_DAY
+ NUMBER_MISSING_DAYS
))
697 return (((temp
- 1 + FIRST_WEEKDAY
) - NUMBER_MISSING_DAYS
) % DAYS_IN_WEEK
);
698 return(REFORMATION_WEEKDAY
);
702 ascii_day(char *p
, int day
) {
705 static char *aday
[] = {
707 " 1", " 2", " 3", " 4", " 5", " 6", " 7",
708 " 8", " 9", "10", "11", "12", "13", "14",
709 "15", "16", "17", "18", "19", "20", "21",
710 "22", "23", "24", "25", "26", "27", "28",
715 int len
= julian
? J_DAY_LEN
: DAY_LEN
;
719 if (day
& TODAY_FLAG
) {
721 p
+= sprintf(p
, "%s", Senter
);
725 if ((val
= day
/ 100)) {
738 *p
++ = day
% 10 + '0';
744 p
+= sprintf(p
, "%s", Sexit
);
750 trim_trailing_spaces(char *s
)
756 while (p
> s
&& isspace(*--p
))
764 * Center string, handling multibyte characters appropriately.
765 * In addition if the string is too large for the width it's truncated.
766 * The number of trailing spaces may be 1 less than the number of leading spaces.
769 center_str(const char* src
, char* dest
, size_t dest_size
, size_t width
)
771 return mbsalign(src
, dest
, dest_size
, &width
,
772 MBS_ALIGN_CENTER
, MBA_UNIBYTE_FALLBACK
);
776 center(const char *str
, size_t len
, int separate
)
778 char lineout
[FMT_ST_CHARS
];
780 center_str(str
, lineout
, ARRAY_SIZE(lineout
), len
);
781 my_putstring(lineout
);
784 snprintf(lineout
, sizeof(lineout
), "%*s", separate
, "");
785 my_putstring(lineout
);
789 static void __attribute__ ((__noreturn__
)) usage(FILE * out
)
791 fputs(USAGE_HEADER
, out
);
792 fprintf(out
, _(" %s [options] [[[day] month] year]\n"), program_invocation_short_name
);
793 fputs(USAGE_OPTIONS
, out
);
794 fputs(_(" -1, --one show only current month (default)\n"), out
);
795 fputs(_(" -3, --three show previous, current and next month\n"), out
);
796 fputs(_(" -s, --sunday Sunday as first day of week\n"), out
);
797 fputs(_(" -m, --monday Monday as first day of week\n"), out
);
798 fputs(_(" -j, --julian output Julian dates\n"), out
);
799 fputs(_(" -y, --year show whole current year\n"), out
);
800 fputs(_(" --color[=<when>] colorize messages (auto, always or never)\n"), out
);
801 fputs(USAGE_SEPARATOR
, out
);
802 fputs(USAGE_HELP
, out
);
803 fputs(USAGE_VERSION
, out
);
804 fprintf(out
, USAGE_MAN_TAIL("cal(1)"));
805 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);