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