2 **********************************************************************
3 * Copyright (C) 1998-2012, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 **********************************************************************
9 * Modification History:
11 * Date Name Description
12 * 06/16/99 stephen Creation.
13 *******************************************************************************
20 #include "unicode/uloc.h"
21 #include "unicode/udat.h"
22 #include "unicode/ucal.h"
23 #include "unicode/ustring.h"
24 #include "unicode/uclean.h"
28 #if UCONFIG_NO_FORMATTING
30 int main(int argc
, char **argv
)
32 printf("%s: Sorry, UCONFIG_NO_FORMATTING was turned on (see uconfig.h). No formatting can be done. \n", argv
[0]);
39 static void usage(void);
41 static void version(void);
43 static void cal(int32_t month
, int32_t year
,
44 UBool useLongNames
, UErrorCode
*status
);
46 static void get_symbols(const UDateFormat
*fmt
,
47 UDateFormatSymbolType type
,
54 static void free_symbols(UChar
*array
[],
57 static void get_days(const UDateFormat
*fmt
,
58 UChar
*days
[], UBool useLongNames
,
59 int32_t fdow
, UErrorCode
*status
);
61 static void free_days(UChar
*days
[]);
63 static void get_months(const UDateFormat
*fmt
,
64 UChar
*months
[], UBool useLongNames
,
67 static void free_months(UChar
*months
[]);
69 static void indent(int32_t count
, FILE *f
);
71 static void print_days(UChar
*days
[], FILE *f
, UErrorCode
*status
);
73 static void print_month(UCalendar
*c
,
75 UBool useLongNames
, int32_t fdow
,
78 static void print_year(UCalendar
*c
,
79 UChar
*days
[], UChar
*months
[],
80 UBool useLongNames
, int32_t fdow
,
83 /* The version of cal */
84 #define CAL_VERSION "1.0"
86 /* Number of days in a week */
89 /* Number of months in a year (yes, 13) */
90 #define MONTH_COUNT 13
92 /* Separation between months in year view */
93 #define MARGIN_WIDTH 4
95 /* Size of stack buffers */
98 /* Patterm string - "MMM yyyy" */
99 static const UChar sShortPat
[] = { 0x004D, 0x004D, 0x004D, 0x0020,
100 0x0079, 0x0079, 0x0079, 0x0079 };
101 /* Pattern string - "MMMM yyyy" */
102 static const UChar sLongPat
[] = { 0x004D, 0x004D, 0x004D, 0x004D, 0x0020,
103 0x0079, 0x0079, 0x0079, 0x0079 };
111 int printVersion
= 0;
112 UBool useLongNames
= 0;
115 int32_t month
= -1, year
= -1;
116 UErrorCode status
= U_ZERO_ERROR
;
119 /* parse the options */
120 for(optInd
= 1; optInd
< argc
; ++optInd
) {
124 if(strcmp(arg
, "-v") == 0 || strcmp(arg
, "--version") == 0) {
128 else if(strcmp(arg
, "-h") == 0 || strcmp(arg
, "--help") == 0) {
131 /* use long day names */
132 else if(strcmp(arg
, "-l") == 0 || strcmp(arg
, "--long") == 0) {
135 /* POSIX.1 says all arguments after -- are not options */
136 else if(strcmp(arg
, "--") == 0) {
141 /* unrecognized option */
142 else if(strncmp(arg
, "-", strlen("-")) == 0) {
143 printf("cal: invalid option -- %s\n", arg
+1);
146 /* done with options, display cal */
152 /* Get the month and year to display, if specified */
155 /* Month and year specified */
156 if(argc
- optInd
== 2) {
157 sscanf(argv
[optInd
], "%d", (int*)&month
);
158 sscanf(argv
[optInd
+ 1], "%d", (int*)&year
);
160 /* Make sure the month value is legal */
161 if(month
< 0 || month
> 12) {
162 printf("icucal: Bad value for month -- %d\n", (int)month
);
168 /* Adjust because months are 0-based */
171 /* Only year specified */
173 sscanf(argv
[optInd
], "%d", (int*)&year
);
177 /* print usage info */
183 /* print version info */
190 cal(month
, year
, useLongNames
, &status
);
192 /* ICU cleanup. Deallocate any memory ICU may be holding. */
195 return (U_FAILURE(status
) ? 1 : 0);
198 /* Usage information */
202 puts("Usage: icucal [OPTIONS] [[MONTH] YEAR]");
205 puts(" -h, --help Print this message and exit.");
206 puts(" -v, --version Print the version number of cal and exit.");
207 puts(" -l, --long Use long names.");
209 puts("Arguments (optional):");
210 puts(" MONTH An integer (1-12) indicating the month to display");
211 puts(" YEAR An integer indicating the year to display");
213 puts("For an interesting calendar, look at October 1582");
216 /* Version information */
220 printf("icucal version %s (ICU version %s), created by Stephen F. Booth.\n",
221 CAL_VERSION
, U_ICU_VERSION
);
222 puts(U_COPYRIGHT_STRING
);
232 UChar
*days
[DAY_COUNT
];
233 UChar
*months
[MONTH_COUNT
];
236 if(U_FAILURE(*status
)) return;
238 /* Create a new calendar */
239 c
= ucal_open(0, -1, uloc_getDefault(), UCAL_TRADITIONAL
, status
);
241 /* Determine if we are printing a calendar for one month or for a year */
243 /* Print an entire year */
244 if(month
== -1 && year
!= -1) {
247 ucal_set(c
, UCAL_YEAR
, year
);
249 /* Determine the first day of the week */
250 fdow
= ucal_getAttribute(c
, UCAL_FIRST_DAY_OF_WEEK
);
252 /* Print the calendar for the year */
253 print_year(c
, days
, months
, useLongNames
, fdow
, status
);
256 /* Print only one month */
259 /* Set the month and the year, if specified */
261 ucal_set(c
, UCAL_MONTH
, month
);
263 ucal_set(c
, UCAL_YEAR
, year
);
265 /* Determine the first day of the week */
266 fdow
= ucal_getAttribute(c
, UCAL_FIRST_DAY_OF_WEEK
);
268 /* Print the calendar for the month */
269 print_month(c
, days
, useLongNames
, fdow
, status
);
276 * Get a set of DateFormat symbols of a given type.
278 * lowestIndex is the index of the first symbol to fetch.
279 * (e.g. it will be one to fetch day names, since Sunday is
280 * day 1 *not* day 0.)
282 * firstIndex is the index of the symbol to place first in
283 * the output array. This is used when fetching day names
284 * in locales where the week doesn't start on Sunday.
286 static void get_symbols(const UDateFormat
*fmt
,
287 UDateFormatSymbolType type
,
296 if (U_FAILURE(*status
)) {
300 count
= udat_countSymbols(fmt
, type
);
302 if(count
!= arrayLength
+ lowestIndex
) {
306 for(i
= 0; i
< arrayLength
; i
++) {
307 int32_t idx
= (i
+ firstIndex
) % arrayLength
;
308 int32_t size
= 1 + udat_getSymbols(fmt
, type
, idx
+ lowestIndex
, NULL
, 0, status
);
310 array
[idx
] = (UChar
*) malloc(sizeof(UChar
) * size
);
312 *status
= U_ZERO_ERROR
;
313 udat_getSymbols(fmt
, type
, idx
+ lowestIndex
, array
[idx
], size
, status
);
317 /* Free the symbols allocated by get_symbols(). */
318 static void free_symbols(UChar
*array
[],
323 for(i
= 0; i
< arrayLength
; i
++) {
328 /* Get the day names for the specified locale, in either long or short
329 form. Also, reorder the days so that they are in the proper order
330 for the locale (not all locales begin weeks on Sunday; in France,
331 weeks start on Monday) */
333 get_days(const UDateFormat
*fmt
,
339 UDateFormatSymbolType dayType
= (useLongNames
? UDAT_WEEKDAYS
: UDAT_SHORT_WEEKDAYS
);
341 if(U_FAILURE(*status
))
344 /* fdow is 1-based */
347 get_symbols(fmt
, dayType
, days
, DAY_COUNT
, 1, fdow
, status
);
350 static void free_days(UChar
*days
[])
352 free_symbols(days
, DAY_COUNT
);
355 /* Get the month names for the specified locale, in either long or
358 get_months(const UDateFormat
*fmt
,
363 UDateFormatSymbolType monthType
= (useLongNames
? UDAT_MONTHS
: UDAT_SHORT_MONTHS
);
365 if(U_FAILURE(*status
))
368 get_symbols(fmt
, monthType
, months
, MONTH_COUNT
- 1, 0, 0, status
); /* some locales have 13 months, no idea why */
371 static void free_months(UChar
*months
[])
373 free_symbols(months
, MONTH_COUNT
- 1);
376 /* Indent a certain number of spaces */
378 indent(int32_t count
,
388 if(count
< BUF_SIZE
) {
389 memset(c
, (int)' ', count
);
390 fwrite(c
, sizeof(char), count
, f
);
394 for(i
= 0; i
< count
; ++i
)
401 print_days(UChar
*days
[],
407 if(U_FAILURE(*status
)) return;
409 /* Print the day names */
410 for(i
= 0; i
< DAY_COUNT
; ++i
) {
411 uprint(days
[i
], f
, status
);
416 /* Print out a calendar for c's current month */
418 print_month(UCalendar
*c
,
424 int32_t width
, pad
, i
, day
;
425 int32_t lens
[DAY_COUNT
];
426 int32_t firstday
, current
;
430 const UChar
*pat
= (useLongNames
? sLongPat
: sShortPat
);
431 int32_t len
= (useLongNames
? 9 : 8);
433 if(U_FAILURE(*status
)) return;
436 /* ========== Generate the header containing the month and year */
438 /* Open a formatter with a month and year only pattern */
439 dfmt
= udat_open(UDAT_PATTERN
,UDAT_PATTERN
,NULL
,NULL
,0,pat
, len
,status
);
441 /* Format the date */
442 udat_format(dfmt
, ucal_getMillis(c
, status
), s
, BUF_SIZE
, 0, status
);
445 /* ========== Get the day names */
446 get_days(dfmt
, days
, useLongNames
, fdow
, status
);
448 /* ========== Print the header */
450 /* Calculate widths for justification */
451 width
= 6; /* 6 spaces, 1 between each day name */
452 for(i
= 0; i
< DAY_COUNT
; ++i
) {
453 lens
[i
] = u_strlen(days
[i
]);
457 /* Print the header, centered among the day names */
458 pad
= width
- u_strlen(s
);
459 indent(pad
/ 2, stdout
);
460 uprint(s
, stdout
, status
);
464 /* ========== Print the day names */
466 print_days(days
, stdout
, status
);
470 /* ========== Print the calendar */
472 /* Get the first of the month */
473 ucal_set(c
, UCAL_DATE
, 1);
474 firstday
= ucal_get(c
, UCAL_DAY_OF_WEEK
, status
);
476 /* The day of the week for the first day of the month is based on
477 1-based days of the week, which were also reordered when placed
478 in the days array. Account for this here by offsetting by the
479 first day of the week for the locale, which is also 1-based. */
482 /* Open the formatter */
483 nfmt
= unum_open(UNUM_DECIMAL
, NULL
,0,NULL
,NULL
, status
);
485 /* Indent the correct number of spaces for the first week */
491 for(i
= 0; i
< current
; ++i
)
492 indent(lens
[i
] + 1, stdout
);
494 /* Finally, print out the days */
495 day
= ucal_get(c
, UCAL_DATE
, status
);
498 /* Format the current day string */
499 unum_format(nfmt
, day
, s
, BUF_SIZE
, 0, status
);
501 /* Calculate the justification and indent */
502 pad
= lens
[current
] - u_strlen(s
);
505 /* Print the day number out, followed by a space */
506 uprint(s
, stdout
, status
);
509 /* Update the current day */
511 current
%= DAY_COUNT
;
513 /* If we're at day 0 (first day of the week), insert a newline */
518 /* Go to the next day */
519 ucal_add(c
, UCAL_DATE
, 1, status
);
520 day
= ucal_get(c
, UCAL_DATE
, status
);
524 /* Output a trailing newline */
533 /* Print out a calendar for c's current year */
535 print_year(UCalendar
*c
,
542 int32_t width
, pad
, i
, j
;
543 int32_t lens
[DAY_COUNT
];
547 const UChar pat
[] = { 0x0079, 0x0079, 0x0079, 0x0079 };
549 UCalendar
*left_cal
, *right_cal
;
550 int32_t left_day
, right_day
;
551 int32_t left_firstday
, right_firstday
, left_current
, right_current
;
552 int32_t left_month
, right_month
;
554 if(U_FAILURE(*status
)) return;
559 /* ========== Generate the header containing the year (only) */
561 /* Open a formatter with a month and year only pattern */
562 dfmt
= udat_open(UDAT_PATTERN
,UDAT_PATTERN
,NULL
,NULL
,0,pat
, len
, status
);
564 /* Format the date */
565 udat_format(dfmt
, ucal_getMillis(left_cal
, status
), s
, BUF_SIZE
, 0, status
);
567 /* ========== Get the month and day names */
568 get_days(dfmt
, days
, useLongNames
, fdow
, status
);
569 get_months(dfmt
, months
, useLongNames
, status
);
571 /* ========== Print the header, centered */
573 /* Calculate widths for justification */
574 width
= 6; /* 6 spaces, 1 between each day name */
575 for(i
= 0; i
< DAY_COUNT
; ++i
) {
576 lens
[i
] = u_strlen(days
[i
]);
580 /* width is the width for 1 calendar; we are displaying in 2 cols
581 with MARGIN_WIDTH spaces between months */
583 /* Print the header, centered among the day names */
584 pad
= 2 * width
+ MARGIN_WIDTH
- u_strlen(s
);
585 indent(pad
/ 2, stdout
);
586 uprint(s
, stdout
, status
);
590 /* Generate a copy of the calendar to use */
591 right_cal
= ucal_open(0, -1, uloc_getDefault(), UCAL_TRADITIONAL
, status
);
592 ucal_setMillis(right_cal
, ucal_getMillis(left_cal
, status
), status
);
594 /* Open the formatter */
595 nfmt
= unum_open(UNUM_DECIMAL
,NULL
, 0,NULL
,NULL
, status
);
597 /* ========== Calculate and display the months, two at a time */
598 for(i
= 0; i
< MONTH_COUNT
- 1; i
+= 2) {
600 /* Print the month names for the two current months */
601 pad
= width
- u_strlen(months
[i
]);
602 indent(pad
/ 2, stdout
);
603 uprint(months
[i
], stdout
, status
);
604 indent(pad
/ 2 + MARGIN_WIDTH
, stdout
);
605 pad
= width
- u_strlen(months
[i
+ 1]);
606 indent(pad
/ 2, stdout
);
607 uprint(months
[i
+ 1], stdout
, status
);
610 /* Print the day names, twice */
611 print_days(days
, stdout
, status
);
612 indent(MARGIN_WIDTH
, stdout
);
613 print_days(days
, stdout
, status
);
616 /* Setup the two calendars */
617 ucal_set(left_cal
, UCAL_MONTH
, i
);
618 ucal_set(left_cal
, UCAL_DATE
, 1);
619 ucal_set(right_cal
, UCAL_MONTH
, i
+ 1);
620 ucal_set(right_cal
, UCAL_DATE
, 1);
622 left_firstday
= ucal_get(left_cal
, UCAL_DAY_OF_WEEK
, status
);
623 right_firstday
= ucal_get(right_cal
, UCAL_DAY_OF_WEEK
, status
);
625 /* The day of the week for the first day of the month is based on
626 1-based days of the week. However, the days were reordered
627 when placed in the days array. Account for this here by
628 offsetting by the first day of the week for the locale, which
631 /* We need to mod by DAY_COUNT since fdow can be > firstday. IE,
632 if fdow = 2 = Monday (like in France) and the first day of the
633 month is a 1 = Sunday, we want firstday to be 6, not -1 */
634 left_firstday
+= (DAY_COUNT
- fdow
);
635 left_firstday
%= DAY_COUNT
;
637 right_firstday
+= (DAY_COUNT
- fdow
);
638 right_firstday
%= DAY_COUNT
;
640 left_current
= left_firstday
;
641 right_current
= right_firstday
;
643 left_day
= ucal_get(left_cal
, UCAL_DATE
, status
);
644 right_day
= ucal_get(right_cal
, UCAL_DATE
, status
);
646 left_month
= ucal_get(left_cal
, UCAL_MONTH
, status
);
647 right_month
= ucal_get(right_cal
, UCAL_MONTH
, status
);
649 /* Finally, print out the days */
650 while(left_month
== i
|| right_month
== i
+ 1) {
652 /* If the left month is finished printing, but the right month
653 still has days to be printed, indent the width of the days
654 strings and reset the left calendar's current day to 0 */
655 if(left_month
!= i
&& right_month
== i
+ 1) {
656 indent(width
+ 1, stdout
);
660 while(left_month
== i
) {
662 /* If the day is the first, indent the correct number of
663 spaces for the first week */
665 for(j
= 0; j
< left_current
; ++j
)
666 indent(lens
[j
] + 1, stdout
);
669 /* Format the current day string */
670 unum_format(nfmt
, left_day
, s
, BUF_SIZE
, 0, status
);
672 /* Calculate the justification and indent */
673 pad
= lens
[left_current
] - u_strlen(s
);
676 /* Print the day number out, followed by a space */
677 uprint(s
, stdout
, status
);
680 /* Update the current day */
682 left_current
%= DAY_COUNT
;
684 /* Go to the next day */
685 ucal_add(left_cal
, UCAL_DATE
, 1, status
);
686 left_day
= ucal_get(left_cal
, UCAL_DATE
, status
);
688 /* Determine the month */
689 left_month
= ucal_get(left_cal
, UCAL_MONTH
, status
);
691 /* If we're at day 0 (first day of the week), break and go to
693 if(left_current
== 0) {
698 /* If the current day isn't 0, indent to make up for missing
699 days at the end of the month */
700 if(left_current
!= 0) {
701 for(j
= left_current
; j
< DAY_COUNT
; ++j
)
702 indent(lens
[j
] + 1, stdout
);
705 /* Indent between the two months */
706 indent(MARGIN_WIDTH
, stdout
);
708 while(right_month
== i
+ 1) {
710 /* If the day is the first, indent the correct number of
711 spaces for the first week */
713 for(j
= 0; j
< right_current
; ++j
)
714 indent(lens
[j
] + 1, stdout
);
717 /* Format the current day string */
718 unum_format(nfmt
, right_day
, s
, BUF_SIZE
, 0, status
);
720 /* Calculate the justification and indent */
721 pad
= lens
[right_current
] - u_strlen(s
);
724 /* Print the day number out, followed by a space */
725 uprint(s
, stdout
, status
);
728 /* Update the current day */
730 right_current
%= DAY_COUNT
;
732 /* Go to the next day */
733 ucal_add(right_cal
, UCAL_DATE
, 1, status
);
734 right_day
= ucal_get(right_cal
, UCAL_DATE
, status
);
736 /* Determine the month */
737 right_month
= ucal_get(right_cal
, UCAL_MONTH
, status
);
739 /* If we're at day 0 (first day of the week), break out */
740 if(right_current
== 0) {
746 /* Output a newline */
750 /* Output a trailing newline */
759 ucal_close(right_cal
);