1 /* 2 *********************************************************************** 3 * Copyright (C) 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 ********************************************************************** 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 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 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 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 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 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 */ 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(). */ 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 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 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 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 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 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 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 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 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