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