1/* 2********************************************************************** 3* Copyright (C) 1998-2009, 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 30int 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 */ 39static void usage(void); 40 41static void version(void); 42 43static void cal(int32_t month, int32_t year, 44 UBool useLongNames, UErrorCode *status); 45 46static 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 54static void free_symbols(UChar *array[], 55 int32_t arrayLength); 56 57static void get_days(const UDateFormat *fmt, 58 UChar *days [], UBool useLongNames, 59 int32_t fdow, UErrorCode *status); 60 61static void free_days(UChar *days[]); 62 63static void get_months(const UDateFormat *fmt, 64 UChar *months [], UBool useLongNames, 65 UErrorCode *status); 66 67static void free_months(UChar *months[]); 68 69static void indent(int32_t count, FILE *f); 70 71static void print_days(UChar *days [], FILE *f, UErrorCode *status); 72 73static void print_month(UCalendar *c, 74 UChar *days [], 75 UBool useLongNames, int32_t fdow, 76 UErrorCode *status); 77 78static 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" */ 99static const UChar sShortPat [] = { 0x004D, 0x004D, 0x004D, 0x0020, 1000x0079, 0x0079, 0x0079, 0x0079 }; 101/* Pattern string - "MMMM yyyy" */ 102static const UChar sLongPat [] = { 0x004D, 0x004D, 0x004D, 0x004D, 0x0020, 1030x0079, 0x0079, 0x0079, 0x0079 }; 104 105 106int 107main(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 */ 199static void 200usage() 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 */ 217static void 218version() 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 225static void 226cal(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 */ 286static 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(). */ 318static 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 329form. Also, reorder the days so that they are in the proper order 330for the locale (not all locales begin weeks on Sunday; in France, 331weeks start on Monday) */ 332static void 333get_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 350static 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 356short form. */ 357static void 358get_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 371static void free_months(UChar *months[]) 372{ 373 free_symbols(months, MONTH_COUNT - 1); 374} 375 376/* Indent a certain number of spaces */ 377static void 378indent(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 */ 400static void 401print_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 */ 417static void 418print_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_IGNORE,UDAT_IGNORE,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 */ 534static void 535print_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_IGNORE,UDAT_IGNORE,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