1 /*
2 **********************************************************************
3 * Copyright (C) 1998-2012, International Business Machines Corporation
4 * and others. All Rights Reserved.
5 **********************************************************************
6 *
7 * File date.c
8 *
9 * Modification History:
10 *
11 * Date Name Description
12 * 06/16/99 stephen Creation.
13 *******************************************************************************
14 */
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19
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"
25
26 #include "uprint.h"
27
28 #if UCONFIG_NO_FORMATTING
29
main(int argc,char ** argv)30 int main(int argc, char **argv)
31 {
32 printf("%s: Sorry, UCONFIG_NO_FORMATTING was turned on (see uconfig.h). No formatting can be done. \n", argv[0]);
33 return 0;
34 }
35 #else
36
37
38 /* Protos */
39 static void usage(void);
40
41 static void version(void);
42
43 static void cal(int32_t month, int32_t year,
44 UBool useLongNames, UErrorCode *status);
45
46 static void get_symbols(const UDateFormat *fmt,
47 UDateFormatSymbolType type,
48 UChar *array[],
49 int32_t arrayLength,
50 int32_t lowestIndex,
51 int32_t firstIndex,
52 UErrorCode *status);
53
54 static void free_symbols(UChar *array[],
55 int32_t arrayLength);
56
57 static void get_days(const UDateFormat *fmt,
58 UChar *days [], UBool useLongNames,
59 int32_t fdow, UErrorCode *status);
60
61 static void free_days(UChar *days[]);
62
63 static void get_months(const UDateFormat *fmt,
64 UChar *months [], UBool useLongNames,
65 UErrorCode *status);
66
67 static void free_months(UChar *months[]);
68
69 static void indent(int32_t count, FILE *f);
70
71 static void print_days(UChar *days [], FILE *f, UErrorCode *status);
72
73 static void print_month(UCalendar *c,
74 UChar *days [],
75 UBool useLongNames, int32_t fdow,
76 UErrorCode *status);
77
78 static void print_year(UCalendar *c,
79 UChar *days [], UChar *months [],
80 UBool useLongNames, int32_t fdow,
81 UErrorCode *status);
82
83 /* The version of cal */
84 #define CAL_VERSION "1.0"
85
86 /* Number of days in a week */
87 #define DAY_COUNT 7
88
89 /* Number of months in a year (yes, 13) */
90 #define MONTH_COUNT 13
91
92 /* Separation between months in year view */
93 #define MARGIN_WIDTH 4
94
95 /* Size of stack buffers */
96 #define BUF_SIZE 64
97
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 };
104
105
106 int
main(int argc,char ** argv)107 main(int argc,
108 char **argv)
109 {
110 int printUsage = 0;
111 int printVersion = 0;
112 UBool useLongNames = 0;
113 int optInd = 1;
114 char *arg;
115 int32_t month = -1, year = -1;
116 UErrorCode status = U_ZERO_ERROR;
117
118
119 /* parse the options */
120 for(optInd = 1; optInd < argc; ++optInd) {
121 arg = argv[optInd];
122
123 /* version info */
124 if(strcmp(arg, "-v") == 0 || strcmp(arg, "--version") == 0) {
125 printVersion = 1;
126 }
127 /* usage info */
128 else if(strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
129 printUsage = 1;
130 }
131 /* use long day names */
132 else if(strcmp(arg, "-l") == 0 || strcmp(arg, "--long") == 0) {
133 useLongNames = 1;
134 }
135 /* POSIX.1 says all arguments after -- are not options */
136 else if(strcmp(arg, "--") == 0) {
137 /* skip the -- */
138 ++optInd;
139 break;
140 }
141 /* unrecognized option */
142 else if(strncmp(arg, "-", strlen("-")) == 0) {
143 printf("cal: invalid option -- %s\n", arg+1);
144 printUsage = 1;
145 }
146 /* done with options, display cal */
147 else {
148 break;
149 }
150 }
151
152 /* Get the month and year to display, if specified */
153 if(optInd != argc) {
154
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);
159
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);
163
164 /* Display usage */
165 printUsage = 1;
166 }
167
168 /* Adjust because months are 0-based */
169 --month;
170 }
171 /* Only year specified */
172 else {
173 sscanf(argv[optInd], "%d", (int*)&year);
174 }
175 }
176
177 /* print usage info */
178 if(printUsage) {
179 usage();
180 return 0;
181 }
182
183 /* print version info */
184 if(printVersion) {
185 version();
186 return 0;
187 }
188
189 /* print the cal */
190 cal(month, year, useLongNames, &status);
191
192 /* ICU cleanup. Deallocate any memory ICU may be holding. */
193 u_cleanup();
194
195 return (U_FAILURE(status) ? 1 : 0);
196 }
197
198 /* Usage information */
199 static void
usage()200 usage()
201 {
202 puts("Usage: icucal [OPTIONS] [[MONTH] YEAR]");
203 puts("");
204 puts("Options:");
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.");
208 puts("");
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");
212 puts("");
213 puts("For an interesting calendar, look at October 1582");
214 }
215
216 /* Version information */
217 static void
version()218 version()
219 {
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);
223 }
224
225 static void
cal(int32_t month,int32_t year,UBool useLongNames,UErrorCode * status)226 cal(int32_t month,
227 int32_t year,
228 UBool useLongNames,
229 UErrorCode *status)
230 {
231 UCalendar *c;
232 UChar *days [DAY_COUNT];
233 UChar *months [MONTH_COUNT];
234 int32_t fdow;
235
236 if(U_FAILURE(*status)) return;
237
238 /* Create a new calendar */
239 c = ucal_open(0, -1, uloc_getDefault(), UCAL_TRADITIONAL, status);
240
241 /* Determine if we are printing a calendar for one month or for a year */
242
243 /* Print an entire year */
244 if(month == -1 && year != -1) {
245
246 /* Set the year */
247 ucal_set(c, UCAL_YEAR, year);
248
249 /* Determine the first day of the week */
250 fdow = ucal_getAttribute(c, UCAL_FIRST_DAY_OF_WEEK);
251
252 /* Print the calendar for the year */
253 print_year(c, days, months, useLongNames, fdow, status);
254 }
255
256 /* Print only one month */
257 else {
258
259 /* Set the month and the year, if specified */
260 if(month != -1)
261 ucal_set(c, UCAL_MONTH, month);
262 if(year != -1)
263 ucal_set(c, UCAL_YEAR, year);
264
265 /* Determine the first day of the week */
266 fdow = ucal_getAttribute(c, UCAL_FIRST_DAY_OF_WEEK);
267
268 /* Print the calendar for the month */
269 print_month(c, days, useLongNames, fdow, status);
270 }
271
272 /* Clean up */
273 ucal_close(c);
274 }
275 /*
276 * Get a set of DateFormat symbols of a given type.
277 *
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.)
281 *
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.
285 */
get_symbols(const UDateFormat * fmt,UDateFormatSymbolType type,UChar * array[],int32_t arrayLength,int32_t lowestIndex,int32_t firstIndex,UErrorCode * status)286 static void get_symbols(const UDateFormat *fmt,
287 UDateFormatSymbolType type,
288 UChar *array[],
289 int32_t arrayLength,
290 int32_t lowestIndex,
291 int32_t firstIndex,
292 UErrorCode *status)
293 {
294 int32_t count, i;
295
296 if (U_FAILURE(*status)) {
297 return;
298 }
299
300 count = udat_countSymbols(fmt, type);
301
302 if(count != arrayLength + lowestIndex) {
303 return;
304 }
305
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);
309
310 array[idx] = (UChar *) malloc(sizeof(UChar) * size);
311
312 *status = U_ZERO_ERROR;
313 udat_getSymbols(fmt, type, idx + lowestIndex, array[idx], size, status);
314 }
315 }
316
317 /* Free the symbols allocated by get_symbols(). */
free_symbols(UChar * array[],int32_t arrayLength)318 static void free_symbols(UChar *array[],
319 int32_t arrayLength)
320 {
321 int32_t i;
322
323 for(i = 0; i < arrayLength; i++) {
324 free(array[i]);
325 }
326 }
327
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) */
332 static void
get_days(const UDateFormat * fmt,UChar * days[],UBool useLongNames,int32_t fdow,UErrorCode * status)333 get_days(const UDateFormat *fmt,
334 UChar *days [],
335 UBool useLongNames,
336 int32_t fdow,
337 UErrorCode *status)
338 {
339 UDateFormatSymbolType dayType = (useLongNames ? UDAT_WEEKDAYS : UDAT_SHORT_WEEKDAYS);
340
341 if(U_FAILURE(*status))
342 return;
343
344 /* fdow is 1-based */
345 --fdow;
346
347 get_symbols(fmt, dayType, days, DAY_COUNT, 1, fdow, status);
348 }
349
free_days(UChar * days[])350 static void free_days(UChar *days[])
351 {
352 free_symbols(days, DAY_COUNT);
353 }
354
355 /* Get the month names for the specified locale, in either long or
356 short form. */
357 static void
get_months(const UDateFormat * fmt,UChar * months[],UBool useLongNames,UErrorCode * status)358 get_months(const UDateFormat *fmt,
359 UChar *months [],
360 UBool useLongNames,
361 UErrorCode *status)
362 {
363 UDateFormatSymbolType monthType = (useLongNames ? UDAT_MONTHS : UDAT_SHORT_MONTHS);
364
365 if(U_FAILURE(*status))
366 return;
367
368 get_symbols(fmt, monthType, months, MONTH_COUNT - 1, 0, 0, status); /* some locales have 13 months, no idea why */
369 }
370
free_months(UChar * months[])371 static void free_months(UChar *months[])
372 {
373 free_symbols(months, MONTH_COUNT - 1);
374 }
375
376 /* Indent a certain number of spaces */
377 static void
indent(int32_t count,FILE * f)378 indent(int32_t count,
379 FILE *f)
380 {
381 char c [BUF_SIZE];
382
383 if(count <= 0)
384 {
385 return;
386 }
387
388 if(count < BUF_SIZE) {
389 memset(c, (int)' ', count);
390 fwrite(c, sizeof(char), count, f);
391 }
392 else {
393 int32_t i;
394 for(i = 0; i < count; ++i)
395 putc(' ', f);
396 }
397 }
398
399 /* Print the days */
400 static void
print_days(UChar * days[],FILE * f,UErrorCode * status)401 print_days(UChar *days [],
402 FILE *f,
403 UErrorCode *status)
404 {
405 int32_t i;
406
407 if(U_FAILURE(*status)) return;
408
409 /* Print the day names */
410 for(i = 0; i < DAY_COUNT; ++i) {
411 uprint(days[i], f, status);
412 putc(' ', f);
413 }
414 }
415
416 /* Print out a calendar for c's current month */
417 static void
print_month(UCalendar * c,UChar * days[],UBool useLongNames,int32_t fdow,UErrorCode * status)418 print_month(UCalendar *c,
419 UChar *days [],
420 UBool useLongNames,
421 int32_t fdow,
422 UErrorCode *status)
423 {
424 int32_t width, pad, i, day;
425 int32_t lens [DAY_COUNT];
426 int32_t firstday, current;
427 UNumberFormat *nfmt;
428 UDateFormat *dfmt;
429 UChar s [BUF_SIZE];
430 const UChar *pat = (useLongNames ? sLongPat : sShortPat);
431 int32_t len = (useLongNames ? 9 : 8);
432
433 if(U_FAILURE(*status)) return;
434
435
436 /* ========== Generate the header containing the month and year */
437
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);
440
441 /* Format the date */
442 udat_format(dfmt, ucal_getMillis(c, status), s, BUF_SIZE, 0, status);
443
444
445 /* ========== Get the day names */
446 get_days(dfmt, days, useLongNames, fdow, status);
447
448 /* ========== Print the header */
449
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]);
454 width += lens[i];
455 }
456
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);
461 putc('\n', stdout);
462
463
464 /* ========== Print the day names */
465
466 print_days(days, stdout, status);
467 putc('\n', stdout);
468
469
470 /* ========== Print the calendar */
471
472 /* Get the first of the month */
473 ucal_set(c, UCAL_DATE, 1);
474 firstday = ucal_get(c, UCAL_DAY_OF_WEEK, status);
475
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. */
480 firstday -= fdow;
481
482 /* Open the formatter */
483 nfmt = unum_open(UNUM_DECIMAL, NULL,0,NULL,NULL, status);
484
485 /* Indent the correct number of spaces for the first week */
486 current = firstday;
487 if(current < 0)
488 {
489 current += 7;
490 }
491 for(i = 0; i < current; ++i)
492 indent(lens[i] + 1, stdout);
493
494 /* Finally, print out the days */
495 day = ucal_get(c, UCAL_DATE, status);
496 do {
497
498 /* Format the current day string */
499 unum_format(nfmt, day, s, BUF_SIZE, 0, status);
500
501 /* Calculate the justification and indent */
502 pad = lens[current] - u_strlen(s);
503 indent(pad, stdout);
504
505 /* Print the day number out, followed by a space */
506 uprint(s, stdout, status);
507 putc(' ', stdout);
508
509 /* Update the current day */
510 ++current;
511 current %= DAY_COUNT;
512
513 /* If we're at day 0 (first day of the week), insert a newline */
514 if(current == 0) {
515 putc('\n', stdout);
516 }
517
518 /* Go to the next day */
519 ucal_add(c, UCAL_DATE, 1, status);
520 day = ucal_get(c, UCAL_DATE, status);
521
522 } while(day != 1);
523
524 /* Output a trailing newline */
525 putc('\n', stdout);
526
527 /* Clean up */
528 free_days(days);
529 unum_close(nfmt);
530 udat_close(dfmt);
531 }
532
533 /* Print out a calendar for c's current year */
534 static void
print_year(UCalendar * c,UChar * days[],UChar * months[],UBool useLongNames,int32_t fdow,UErrorCode * status)535 print_year(UCalendar *c,
536 UChar *days [],
537 UChar *months [],
538 UBool useLongNames,
539 int32_t fdow,
540 UErrorCode *status)
541 {
542 int32_t width, pad, i, j;
543 int32_t lens [DAY_COUNT];
544 UNumberFormat *nfmt;
545 UDateFormat *dfmt;
546 UChar s [BUF_SIZE];
547 const UChar pat [] = { 0x0079, 0x0079, 0x0079, 0x0079 };
548 int32_t len = 4;
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;
553
554 if(U_FAILURE(*status)) return;
555
556 /* Alias */
557 left_cal = c;
558
559 /* ========== Generate the header containing the year (only) */
560
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);
563
564 /* Format the date */
565 udat_format(dfmt, ucal_getMillis(left_cal, status), s, BUF_SIZE, 0, status);
566
567 /* ========== Get the month and day names */
568 get_days(dfmt, days, useLongNames, fdow, status);
569 get_months(dfmt, months, useLongNames, status);
570
571 /* ========== Print the header, centered */
572
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]);
577 width += lens[i];
578 }
579
580 /* width is the width for 1 calendar; we are displaying in 2 cols
581 with MARGIN_WIDTH spaces between months */
582
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);
587 putc('\n', stdout);
588 putc('\n', stdout);
589
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);
593
594 /* Open the formatter */
595 nfmt = unum_open(UNUM_DECIMAL,NULL, 0,NULL,NULL, status);
596
597 /* ========== Calculate and display the months, two at a time */
598 for(i = 0; i < MONTH_COUNT - 1; i += 2) {
599
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);
608 putc('\n', stdout);
609
610 /* Print the day names, twice */
611 print_days(days, stdout, status);
612 indent(MARGIN_WIDTH, stdout);
613 print_days(days, stdout, status);
614 putc('\n', stdout);
615
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);
621
622 left_firstday = ucal_get(left_cal, UCAL_DAY_OF_WEEK, status);
623 right_firstday = ucal_get(right_cal, UCAL_DAY_OF_WEEK, status);
624
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
629 is also 1-based. */
630
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;
636
637 right_firstday += (DAY_COUNT - fdow);
638 right_firstday %= DAY_COUNT;
639
640 left_current = left_firstday;
641 right_current = right_firstday;
642
643 left_day = ucal_get(left_cal, UCAL_DATE, status);
644 right_day = ucal_get(right_cal, UCAL_DATE, status);
645
646 left_month = ucal_get(left_cal, UCAL_MONTH, status);
647 right_month = ucal_get(right_cal, UCAL_MONTH, status);
648
649 /* Finally, print out the days */
650 while(left_month == i || right_month == i + 1) {
651
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);
657 left_current = 0;
658 }
659
660 while(left_month == i) {
661
662 /* If the day is the first, indent the correct number of
663 spaces for the first week */
664 if(left_day == 1) {
665 for(j = 0; j < left_current; ++j)
666 indent(lens[j] + 1, stdout);
667 }
668
669 /* Format the current day string */
670 unum_format(nfmt, left_day, s, BUF_SIZE, 0, status);
671
672 /* Calculate the justification and indent */
673 pad = lens[left_current] - u_strlen(s);
674 indent(pad, stdout);
675
676 /* Print the day number out, followed by a space */
677 uprint(s, stdout, status);
678 putc(' ', stdout);
679
680 /* Update the current day */
681 ++left_current;
682 left_current %= DAY_COUNT;
683
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);
687
688 /* Determine the month */
689 left_month = ucal_get(left_cal, UCAL_MONTH, status);
690
691 /* If we're at day 0 (first day of the week), break and go to
692 the next month */
693 if(left_current == 0) {
694 break;
695 }
696 };
697
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);
703 }
704
705 /* Indent between the two months */
706 indent(MARGIN_WIDTH, stdout);
707
708 while(right_month == i + 1) {
709
710 /* If the day is the first, indent the correct number of
711 spaces for the first week */
712 if(right_day == 1) {
713 for(j = 0; j < right_current; ++j)
714 indent(lens[j] + 1, stdout);
715 }
716
717 /* Format the current day string */
718 unum_format(nfmt, right_day, s, BUF_SIZE, 0, status);
719
720 /* Calculate the justification and indent */
721 pad = lens[right_current] - u_strlen(s);
722 indent(pad, stdout);
723
724 /* Print the day number out, followed by a space */
725 uprint(s, stdout, status);
726 putc(' ', stdout);
727
728 /* Update the current day */
729 ++right_current;
730 right_current %= DAY_COUNT;
731
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);
735
736 /* Determine the month */
737 right_month = ucal_get(right_cal, UCAL_MONTH, status);
738
739 /* If we're at day 0 (first day of the week), break out */
740 if(right_current == 0) {
741 break;
742 }
743
744 };
745
746 /* Output a newline */
747 putc('\n', stdout);
748 }
749
750 /* Output a trailing newline */
751 putc('\n', stdout);
752 }
753
754 /* Clean up */
755 free_months(months);
756 free_days(days);
757 udat_close(dfmt);
758 unum_close(nfmt);
759 ucal_close(right_cal);
760 }
761
762 #endif
763