2 ***********************************************************************
3 * © 2016 and later: Unicode, Inc. and others.
4 * License & terms of use: http://www.unicode.org/copyright.html#License
5 ***********************************************************************
6 **********************************************************************
7 * Copyright (C) 1998-2012, International Business Machines Corporation
8 * and others. All Rights Reserved.
9 **********************************************************************
13 * Modification History:
15 * Date Name Description
16 * 06/16/99 stephen Creation.
17 *******************************************************************************
24 #include "unicode/uloc.h"
25 #include "unicode/udat.h"
26 #include "unicode/ucal.h"
27 #include "unicode/ustring.h"
28 #include "unicode/uclean.h"
32 #if UCONFIG_NO_FORMATTING
34 int main(int argc
, char **argv
)
36 printf("%s: Sorry, UCONFIG_NO_FORMATTING was turned on (see uconfig.h). No formatting can be done. \n", argv
[0]);
43 static void usage(void);
45 static void version(void);
47 static void cal(int32_t month
, int32_t year
,
48 UBool useLongNames
, UErrorCode
*status
);
50 static void get_symbols(const UDateFormat
*fmt
,
51 UDateFormatSymbolType type
,
58 static void free_symbols(UChar
*array
[],
61 static void get_days(const UDateFormat
*fmt
,
62 UChar
*days
[], UBool useLongNames
,
63 int32_t fdow
, UErrorCode
*status
);
65 static void free_days(UChar
*days
[]);
67 static void get_months(const UDateFormat
*fmt
,
68 UChar
*months
[], UBool useLongNames
,
71 static void free_months(UChar
*months
[]);
73 static void indent(int32_t count
, FILE *f
);
75 static void print_days(UChar
*days
[], FILE *f
, UErrorCode
*status
);
77 static void print_month(UCalendar
*c
,
79 UBool useLongNames
, int32_t fdow
,
82 static void print_year(UCalendar
*c
,
83 UChar
*days
[], UChar
*months
[],
84 UBool useLongNames
, int32_t fdow
,
87 /* The version of cal */
88 #define CAL_VERSION "1.0"
90 /* Number of days in a week */
93 /* Number of months in a year (yes, 13) */
94 #define MONTH_COUNT 13
96 /* Separation between months in year view */
97 #define MARGIN_WIDTH 4
99 /* Size of stack buffers */
102 /* Patterm string - "MMM yyyy" */
103 static const UChar sShortPat
[] = { 0x004D, 0x004D, 0x004D, 0x0020,
104 0x0079, 0x0079, 0x0079, 0x0079 };
105 /* Pattern string - "MMMM yyyy" */
106 static const UChar sLongPat
[] = { 0x004D, 0x004D, 0x004D, 0x004D, 0x0020,
107 0x0079, 0x0079, 0x0079, 0x0079 };
115 int printVersion
= 0;
116 UBool useLongNames
= 0;
119 int32_t month
= -1, year
= -1;
120 UErrorCode status
= U_ZERO_ERROR
;
123 /* parse the options */
124 for(optInd
= 1; optInd
< argc
; ++optInd
) {
128 if(strcmp(arg
, "-v") == 0 || strcmp(arg
, "--version") == 0) {
132 else if(strcmp(arg
, "-h") == 0 || strcmp(arg
, "--help") == 0) {
135 /* use long day names */
136 else if(strcmp(arg
, "-l") == 0 || strcmp(arg
, "--long") == 0) {
139 /* POSIX.1 says all arguments after -- are not options */
140 else if(strcmp(arg
, "--") == 0) {
145 /* unrecognized option */
146 else if(strncmp(arg
, "-", strlen("-")) == 0) {
147 printf("cal: invalid option -- %s\n", arg
+1);
150 /* done with options, display cal */
156 /* Get the month and year to display, if specified */
159 /* Month and year specified */
160 if(argc
- optInd
== 2) {
161 sscanf(argv
[optInd
], "%d", (int*)&month
);
162 sscanf(argv
[optInd
+ 1], "%d", (int*)&year
);
164 /* Make sure the month value is legal */
165 if(month
< 0 || month
> 12) {
166 printf("icucal: Bad value for month -- %d\n", (int)month
);
172 /* Adjust because months are 0-based */
175 /* Only year specified */
177 sscanf(argv
[optInd
], "%d", (int*)&year
);
181 /* print usage info */
187 /* print version info */
194 cal(month
, year
, useLongNames
, &status
);
196 /* ICU cleanup. Deallocate any memory ICU may be holding. */
199 return (U_FAILURE(status
) ? 1 : 0);
202 /* Usage information */
206 puts("Usage: icucal [OPTIONS] [[MONTH] YEAR]");
209 puts(" -h, --help Print this message and exit.");
210 puts(" -v, --version Print the version number of cal and exit.");
211 puts(" -l, --long Use long names.");
213 puts("Arguments (optional):");
214 puts(" MONTH An integer (1-12) indicating the month to display");
215 puts(" YEAR An integer indicating the year to display");
217 puts("For an interesting calendar, look at October 1582");
220 /* Version information */
224 printf("icucal version %s (ICU version %s), created by Stephen F. Booth.\n",
225 CAL_VERSION
, U_ICU_VERSION
);
226 puts(U_COPYRIGHT_STRING
);
236 UChar
*days
[DAY_COUNT
];
237 UChar
*months
[MONTH_COUNT
];
240 if(U_FAILURE(*status
)) return;
242 /* Create a new calendar */
243 c
= ucal_open(0, -1, uloc_getDefault(), UCAL_TRADITIONAL
, status
);
245 /* Determine if we are printing a calendar for one month or for a year */
247 /* Print an entire year */
248 if(month
== -1 && year
!= -1) {
251 ucal_set(c
, UCAL_YEAR
, year
);
253 /* Determine the first day of the week */
254 fdow
= ucal_getAttribute(c
, UCAL_FIRST_DAY_OF_WEEK
);
256 /* Print the calendar for the year */
257 print_year(c
, days
, months
, useLongNames
, fdow
, status
);
260 /* Print only one month */
263 /* Set the month and the year, if specified */
265 ucal_set(c
, UCAL_MONTH
, month
);
267 ucal_set(c
, UCAL_YEAR
, year
);
269 /* Determine the first day of the week */
270 fdow
= ucal_getAttribute(c
, UCAL_FIRST_DAY_OF_WEEK
);
272 /* Print the calendar for the month */
273 print_month(c
, days
, useLongNames
, fdow
, status
);
280 * Get a set of DateFormat symbols of a given type.
282 * lowestIndex is the index of the first symbol to fetch.
283 * (e.g. it will be one to fetch day names, since Sunday is
284 * day 1 *not* day 0.)
286 * firstIndex is the index of the symbol to place first in
287 * the output array. This is used when fetching day names
288 * in locales where the week doesn't start on Sunday.
290 static void get_symbols(const UDateFormat
*fmt
,
291 UDateFormatSymbolType type
,
300 if (U_FAILURE(*status
)) {
304 count
= udat_countSymbols(fmt
, type
);
306 if(count
!= arrayLength
+ lowestIndex
) {
310 for(i
= 0; i
< arrayLength
; i
++) {
311 int32_t idx
= (i
+ firstIndex
) % arrayLength
;
312 int32_t size
= 1 + udat_getSymbols(fmt
, type
, idx
+ lowestIndex
, NULL
, 0, status
);
314 array
[idx
] = (UChar
*) malloc(sizeof(UChar
) * size
);
316 *status
= U_ZERO_ERROR
;
317 udat_getSymbols(fmt
, type
, idx
+ lowestIndex
, array
[idx
], size
, status
);
321 /* Free the symbols allocated by get_symbols(). */
322 static void free_symbols(UChar
*array
[],
327 for(i
= 0; i
< arrayLength
; i
++) {
332 /* Get the day names for the specified locale, in either long or short
333 form. Also, reorder the days so that they are in the proper order
334 for the locale (not all locales begin weeks on Sunday; in France,
335 weeks start on Monday) */
337 get_days(const UDateFormat
*fmt
,
343 UDateFormatSymbolType dayType
= (useLongNames
? UDAT_WEEKDAYS
: UDAT_SHORT_WEEKDAYS
);
345 if(U_FAILURE(*status
))
348 /* fdow is 1-based */
351 get_symbols(fmt
, dayType
, days
, DAY_COUNT
, 1, fdow
, status
);
354 static void free_days(UChar
*days
[])
356 free_symbols(days
, DAY_COUNT
);
359 /* Get the month names for the specified locale, in either long or
362 get_months(const UDateFormat
*fmt
,
367 UDateFormatSymbolType monthType
= (useLongNames
? UDAT_MONTHS
: UDAT_SHORT_MONTHS
);
369 if(U_FAILURE(*status
))
372 get_symbols(fmt
, monthType
, months
, MONTH_COUNT
- 1, 0, 0, status
); /* some locales have 13 months, no idea why */
375 static void free_months(UChar
*months
[])
377 free_symbols(months
, MONTH_COUNT
- 1);
380 /* Indent a certain number of spaces */
382 indent(int32_t count
,
392 if(count
< BUF_SIZE
) {
393 memset(c
, (int)' ', count
);
394 fwrite(c
, sizeof(char), count
, f
);
398 for(i
= 0; i
< count
; ++i
)
405 print_days(UChar
*days
[],
411 if(U_FAILURE(*status
)) return;
413 /* Print the day names */
414 for(i
= 0; i
< DAY_COUNT
; ++i
) {
415 uprint(days
[i
], f
, status
);
420 /* Print out a calendar for c's current month */
422 print_month(UCalendar
*c
,
428 int32_t width
, pad
, i
, day
;
429 int32_t lens
[DAY_COUNT
];
430 int32_t firstday
, current
;
434 const UChar
*pat
= (useLongNames
? sLongPat
: sShortPat
);
435 int32_t len
= (useLongNames
? 9 : 8);
437 if(U_FAILURE(*status
)) return;
440 /* ========== Generate the header containing the month and year */
442 /* Open a formatter with a month and year only pattern */
443 dfmt
= udat_open(UDAT_PATTERN
,UDAT_PATTERN
,NULL
,NULL
,0,pat
, len
,status
);
445 /* Format the date */
446 udat_format(dfmt
, ucal_getMillis(c
, status
), s
, BUF_SIZE
, 0, status
);
449 /* ========== Get the day names */
450 get_days(dfmt
, days
, useLongNames
, fdow
, status
);
452 /* ========== Print the header */
454 /* Calculate widths for justification */
455 width
= 6; /* 6 spaces, 1 between each day name */
456 for(i
= 0; i
< DAY_COUNT
; ++i
) {
457 lens
[i
] = u_strlen(days
[i
]);
461 /* Print the header, centered among the day names */
462 pad
= width
- u_strlen(s
);
463 indent(pad
/ 2, stdout
);
464 uprint(s
, stdout
, status
);
468 /* ========== Print the day names */
470 print_days(days
, stdout
, status
);
474 /* ========== Print the calendar */
476 /* Get the first of the month */
477 ucal_set(c
, UCAL_DATE
, 1);
478 firstday
= ucal_get(c
, UCAL_DAY_OF_WEEK
, status
);
480 /* The day of the week for the first day of the month is based on
481 1-based days of the week, which were also reordered when placed
482 in the days array. Account for this here by offsetting by the
483 first day of the week for the locale, which is also 1-based. */
486 /* Open the formatter */
487 nfmt
= unum_open(UNUM_DECIMAL
, NULL
,0,NULL
,NULL
, status
);
489 /* Indent the correct number of spaces for the first week */
495 for(i
= 0; i
< current
; ++i
)
496 indent(lens
[i
] + 1, stdout
);
498 /* Finally, print out the days */
499 day
= ucal_get(c
, UCAL_DATE
, status
);
502 /* Format the current day string */
503 unum_format(nfmt
, day
, s
, BUF_SIZE
, 0, status
);
505 /* Calculate the justification and indent */
506 pad
= lens
[current
] - u_strlen(s
);
509 /* Print the day number out, followed by a space */
510 uprint(s
, stdout
, status
);
513 /* Update the current day */
515 current
%= DAY_COUNT
;
517 /* If we're at day 0 (first day of the week), insert a newline */
522 /* Go to the next day */
523 ucal_add(c
, UCAL_DATE
, 1, status
);
524 day
= ucal_get(c
, UCAL_DATE
, status
);
528 /* Output a trailing newline */
537 /* Print out a calendar for c's current year */
539 print_year(UCalendar
*c
,
546 int32_t width
, pad
, i
, j
;
547 int32_t lens
[DAY_COUNT
];
551 const UChar pat
[] = { 0x0079, 0x0079, 0x0079, 0x0079 };
553 UCalendar
*left_cal
, *right_cal
;
554 int32_t left_day
, right_day
;
555 int32_t left_firstday
, right_firstday
, left_current
, right_current
;
556 int32_t left_month
, right_month
;
558 if(U_FAILURE(*status
)) return;
563 /* ========== Generate the header containing the year (only) */
565 /* Open a formatter with a month and year only pattern */
566 dfmt
= udat_open(UDAT_PATTERN
,UDAT_PATTERN
,NULL
,NULL
,0,pat
, len
, status
);
568 /* Format the date */
569 udat_format(dfmt
, ucal_getMillis(left_cal
, status
), s
, BUF_SIZE
, 0, status
);
571 /* ========== Get the month and day names */
572 get_days(dfmt
, days
, useLongNames
, fdow
, status
);
573 get_months(dfmt
, months
, useLongNames
, status
);
575 /* ========== Print the header, centered */
577 /* Calculate widths for justification */
578 width
= 6; /* 6 spaces, 1 between each day name */
579 for(i
= 0; i
< DAY_COUNT
; ++i
) {
580 lens
[i
] = u_strlen(days
[i
]);
584 /* width is the width for 1 calendar; we are displaying in 2 cols
585 with MARGIN_WIDTH spaces between months */
587 /* Print the header, centered among the day names */
588 pad
= 2 * width
+ MARGIN_WIDTH
- u_strlen(s
);
589 indent(pad
/ 2, stdout
);
590 uprint(s
, stdout
, status
);
594 /* Generate a copy of the calendar to use */
595 right_cal
= ucal_open(0, -1, uloc_getDefault(), UCAL_TRADITIONAL
, status
);
596 ucal_setMillis(right_cal
, ucal_getMillis(left_cal
, status
), status
);
598 /* Open the formatter */
599 nfmt
= unum_open(UNUM_DECIMAL
,NULL
, 0,NULL
,NULL
, status
);
601 /* ========== Calculate and display the months, two at a time */
602 for(i
= 0; i
< MONTH_COUNT
- 1; i
+= 2) {
604 /* Print the month names for the two current months */
605 pad
= width
- u_strlen(months
[i
]);
606 indent(pad
/ 2, stdout
);
607 uprint(months
[i
], stdout
, status
);
608 indent(pad
/ 2 + MARGIN_WIDTH
, stdout
);
609 pad
= width
- u_strlen(months
[i
+ 1]);
610 indent(pad
/ 2, stdout
);
611 uprint(months
[i
+ 1], stdout
, status
);
614 /* Print the day names, twice */
615 print_days(days
, stdout
, status
);
616 indent(MARGIN_WIDTH
, stdout
);
617 print_days(days
, stdout
, status
);
620 /* Setup the two calendars */
621 ucal_set(left_cal
, UCAL_MONTH
, i
);
622 ucal_set(left_cal
, UCAL_DATE
, 1);
623 ucal_set(right_cal
, UCAL_MONTH
, i
+ 1);
624 ucal_set(right_cal
, UCAL_DATE
, 1);
626 left_firstday
= ucal_get(left_cal
, UCAL_DAY_OF_WEEK
, status
);
627 right_firstday
= ucal_get(right_cal
, UCAL_DAY_OF_WEEK
, status
);
629 /* The day of the week for the first day of the month is based on
630 1-based days of the week. However, the days were reordered
631 when placed in the days array. Account for this here by
632 offsetting by the first day of the week for the locale, which
635 /* We need to mod by DAY_COUNT since fdow can be > firstday. IE,
636 if fdow = 2 = Monday (like in France) and the first day of the
637 month is a 1 = Sunday, we want firstday to be 6, not -1 */
638 left_firstday
+= (DAY_COUNT
- fdow
);
639 left_firstday
%= DAY_COUNT
;
641 right_firstday
+= (DAY_COUNT
- fdow
);
642 right_firstday
%= DAY_COUNT
;
644 left_current
= left_firstday
;
645 right_current
= right_firstday
;
647 left_day
= ucal_get(left_cal
, UCAL_DATE
, status
);
648 right_day
= ucal_get(right_cal
, UCAL_DATE
, status
);
650 left_month
= ucal_get(left_cal
, UCAL_MONTH
, status
);
651 right_month
= ucal_get(right_cal
, UCAL_MONTH
, status
);
653 /* Finally, print out the days */
654 while(left_month
== i
|| right_month
== i
+ 1) {
656 /* If the left month is finished printing, but the right month
657 still has days to be printed, indent the width of the days
658 strings and reset the left calendar's current day to 0 */
659 if(left_month
!= i
&& right_month
== i
+ 1) {
660 indent(width
+ 1, stdout
);
664 while(left_month
== i
) {
666 /* If the day is the first, indent the correct number of
667 spaces for the first week */
669 for(j
= 0; j
< left_current
; ++j
)
670 indent(lens
[j
] + 1, stdout
);
673 /* Format the current day string */
674 unum_format(nfmt
, left_day
, s
, BUF_SIZE
, 0, status
);
676 /* Calculate the justification and indent */
677 pad
= lens
[left_current
] - u_strlen(s
);
680 /* Print the day number out, followed by a space */
681 uprint(s
, stdout
, status
);
684 /* Update the current day */
686 left_current
%= DAY_COUNT
;
688 /* Go to the next day */
689 ucal_add(left_cal
, UCAL_DATE
, 1, status
);
690 left_day
= ucal_get(left_cal
, UCAL_DATE
, status
);
692 /* Determine the month */
693 left_month
= ucal_get(left_cal
, UCAL_MONTH
, status
);
695 /* If we're at day 0 (first day of the week), break and go to
697 if(left_current
== 0) {
702 /* If the current day isn't 0, indent to make up for missing
703 days at the end of the month */
704 if(left_current
!= 0) {
705 for(j
= left_current
; j
< DAY_COUNT
; ++j
)
706 indent(lens
[j
] + 1, stdout
);
709 /* Indent between the two months */
710 indent(MARGIN_WIDTH
, stdout
);
712 while(right_month
== i
+ 1) {
714 /* If the day is the first, indent the correct number of
715 spaces for the first week */
717 for(j
= 0; j
< right_current
; ++j
)
718 indent(lens
[j
] + 1, stdout
);
721 /* Format the current day string */
722 unum_format(nfmt
, right_day
, s
, BUF_SIZE
, 0, status
);
724 /* Calculate the justification and indent */
725 pad
= lens
[right_current
] - u_strlen(s
);
728 /* Print the day number out, followed by a space */
729 uprint(s
, stdout
, status
);
732 /* Update the current day */
734 right_current
%= DAY_COUNT
;
736 /* Go to the next day */
737 ucal_add(right_cal
, UCAL_DATE
, 1, status
);
738 right_day
= ucal_get(right_cal
, UCAL_DATE
, status
);
740 /* Determine the month */
741 right_month
= ucal_get(right_cal
, UCAL_MONTH
, status
);
743 /* If we're at day 0 (first day of the week), break out */
744 if(right_current
== 0) {
750 /* Output a newline */
754 /* Output a trailing newline */
763 ucal_close(right_cal
);