1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kim Letkeman. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the University of California, Berkeley. The name of the 14 * University may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 */ 20 21 #ifndef lint 22 char copyright[] = 23 "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 24 All rights reserved.\n"; 25 #endif /* not lint */ 26 27 #ifndef lint 28 static char sccsid[] = "@(#)cal.c 4.7 (Berkeley) 11/27/89"; 29 #endif /* not lint */ 30 31 #include <sys/types.h> 32 #include <sys/time.h> 33 #include <stdio.h> 34 #include <ctype.h> 35 36 #define THURSDAY 4 /* for reformation */ 37 #define SATURDAY 6 /* 1 Jan 1 was a Saturday */ 38 #define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */ 39 #define NUMBER_MISSING_DAYS 11 /* 11 day correction */ 40 #define SPACE -1 /* used in day array */ 41 42 static int days_in_month[2][13] = { 43 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 44 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 45 }; 46 47 static int sep1752[42] = { 48 SPACE, SPACE, 1, 2, 14, 15, 16, 49 17, 18, 19, 20, 21, 22, 23, 50 24, 25, 26, 27, 28, 29, 30, 51 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 52 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 53 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 54 }, j_sep1752[42] = { 55 SPACE, SPACE, 245, 246, 258, 259, 260, 56 261, 262, 263, 264, 265, 266, 267, 57 268, 269, 270, 271, 272, 273, 274, 58 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 59 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 60 SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, 61 }; 62 63 static char *month_names[12] = { 64 "January", "February", "March", "April", "May", "June", 65 "July", "August", "September", "October", "November", "December", 66 }; 67 68 static char *day_headings = " S M Tu W Th F S"; 69 static char *j_day_headings = " S M Tu W Th F S"; 70 71 /* leap year -- account for gregorian reformation in 1752 */ 72 #define leap_year(yr) \ 73 ((yr) <= 1752 ? !((yr) % 4) : \ 74 !((yr) % 4) && ((yr) % 100) || !((yr) % 400)) 75 76 /* number of centuries since 1700, not inclusive */ 77 #define centuries_since_1700(yr) \ 78 ((yr) > 1700 ? (yr) / 100 - 17 : 0) 79 80 /* number of centuries since 1700 whose modulo of 400 is 0 */ 81 #define quad_centuries_since_1700(yr) \ 82 ((yr) > 1600 ? ((yr) - 1600) / 400 : 0) 83 84 /* number of leap years between year 1 and this year, not inclusive */ 85 #define leap_years_since_year_1(yr) \ 86 ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) 87 88 int julian; 89 90 main(argc, argv) 91 int argc; 92 char **argv; 93 { 94 extern char *optarg; 95 extern int optind; 96 struct tm *local_time; 97 time_t now, time(); 98 int ch, month, year, yflag; 99 100 yflag = 0; 101 while ((ch = getopt(argc, argv, "jy")) != EOF) 102 switch(ch) { 103 case 'j': 104 julian = 1; 105 break; 106 case 'y': 107 yflag = 1; 108 break; 109 case '?': 110 default: 111 usage(); 112 } 113 argc -= optind; 114 argv += optind; 115 116 switch(argc) { 117 case 2: 118 if ((month = atoi(*argv++)) <= 0 || month > 12) { 119 (void)fprintf(stderr, "cal: illegal month value.\n"); 120 exit(1); 121 } 122 /* FALLTHROUGH */ 123 case 1: 124 if ((year = atoi(*argv)) <= 0 || year > 9999) { 125 (void)fprintf(stderr, "cal: illegal year value.\n"); 126 exit(1); 127 } 128 break; 129 case 0: 130 (void)time(&now); 131 local_time = localtime(&now); 132 year = local_time->tm_year + 1900; 133 if (!yflag) 134 month = local_time->tm_mon + 1; 135 break; 136 default: 137 usage(); 138 } 139 if (month) 140 monthly(month, year); 141 else if (julian) 142 j_yearly(year); 143 else 144 yearly(year); 145 exit(0); 146 } 147 148 #define DAY_LEN 3 /* 3 spaces per day */ 149 #define J_DAY_LEN 4 /* 4 spaces per day */ 150 #define WEEK_LEN 20 /* 7 * 3 - one space at the end */ 151 #define J_WEEK_LEN 27 /* 7 * 4 - one space at the end */ 152 #define HEAD_SEP 2 /* spaces between day headings */ 153 #define J_HEAD_SEP 3 154 155 monthly(month, year) 156 int month, year; 157 { 158 register int col, row; 159 register char *p; 160 int len, days[42]; 161 char lineout[30]; 162 163 day_array(month, year, days); 164 len = sprintf(lineout, "%s %d", month_names[month - 1], year); 165 (void)printf("%*s%s\n%s\n", 166 ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "", 167 lineout, julian ? j_day_headings : day_headings); 168 for (row = 0; row < 6; row++) { 169 for (col = 0, p = lineout; col < 7; col++, 170 p += julian ? J_DAY_LEN : DAY_LEN) 171 ascii_day(p, days[row * 7 + col]); 172 trim_trailing_spaces(lineout); 173 (void)printf("%s\n", lineout); 174 } 175 } 176 177 j_yearly(year) 178 int year; 179 { 180 register int col, *dp, i, month, row, which_cal; 181 register char *p; 182 int days[12][42]; 183 char lineout[80]; 184 185 (void)sprintf(lineout, "%d", year); 186 center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0); 187 (void)printf("\n\n"); 188 for (i = 0; i < 12; i++) 189 day_array(i + 1, year, &days[i][0]); 190 (void)memset(lineout, ' ', sizeof(lineout) - 1); 191 lineout[sizeof(lineout) - 1] = '\0'; 192 for (month = 0; month < 12; month += 2) { 193 center(month_names[month], J_WEEK_LEN, J_HEAD_SEP); 194 center(month_names[month + 1], J_WEEK_LEN, 0); 195 (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "", 196 j_day_headings); 197 for (row = 0; row < 6; row++) { 198 for (which_cal = 0; which_cal < 2; which_cal++) { 199 p = lineout + which_cal * (J_WEEK_LEN + 2); 200 dp = &days[month + which_cal][row * 7]; 201 for (col = 0; col < 7; col++, p += J_DAY_LEN) 202 ascii_day(p, *dp++); 203 } 204 trim_trailing_spaces(lineout); 205 (void)printf("%s\n", lineout); 206 } 207 } 208 (void)printf("\n"); 209 } 210 211 yearly(year) 212 int year; 213 { 214 register int col, *dp, i, month, row, which_cal; 215 register char *p; 216 int days[12][42]; 217 char lineout[80]; 218 219 (void)sprintf(lineout, "%d", year); 220 center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0); 221 (void)printf("\n\n"); 222 for (i = 0; i < 12; i++) 223 day_array(i + 1, year, &days[i][0]); 224 (void)memset(lineout, ' ', sizeof(lineout) - 1); 225 lineout[sizeof(lineout) - 1] = '\0'; 226 for (month = 0; month < 12; month += 3) { 227 center(month_names[month], WEEK_LEN, HEAD_SEP); 228 center(month_names[month + 1], WEEK_LEN, HEAD_SEP); 229 center(month_names[month + 2], WEEK_LEN, 0); 230 (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP, 231 "", day_headings, HEAD_SEP, "", day_headings); 232 for (row = 0; row < 6; row++) { 233 for (which_cal = 0; which_cal < 3; which_cal++) { 234 p = lineout + which_cal * (WEEK_LEN + 2); 235 dp = &days[month + which_cal][row * 7]; 236 for (col = 0; col < 7; col++, p += DAY_LEN) 237 ascii_day(p, *dp++); 238 } 239 trim_trailing_spaces(lineout); 240 (void)printf("%s\n", lineout); 241 } 242 } 243 (void)printf("\n"); 244 } 245 246 /* 247 * day_array -- 248 * Fill in an array of 42 integers with a calendar. Assume for a moment 249 * that you took the (maximum) 6 rows in a calendar and stretched them 250 * out end to end. You would have 42 numbers or spaces. This routine 251 * builds that array for any month from Jan. 1 through Dec. 9999. 252 */ 253 day_array(month, year, days) 254 register int *days; 255 int month, year; 256 { 257 register int i, day, dw, dm, *p; 258 259 dm = days_in_month[leap_year(year)][month]; 260 dw = day_in_week(1, month, year); 261 if (month == 9 && year == 1752) { 262 p = julian ? j_sep1752 : sep1752; 263 for (i = 0; i < 42; i++) 264 *days++ = *p++; 265 return; 266 } 267 for (i = 42, p = days; i--;) 268 *p++ = SPACE; 269 day = julian ? day_in_year(1, month, year) : 1; 270 while (dm--) 271 days[dw++] = day++; 272 } 273 274 /* 275 * day_in_year -- 276 * return the 1 based day number within the year 277 */ 278 day_in_year(day, month, year) 279 register int day, month; 280 int year; 281 { 282 register int i, leap; 283 284 leap = leap_year(year); 285 for (i = 1; i < month; i++) 286 day += days_in_month[leap][i]; 287 return(day); 288 } 289 290 /* 291 * day_in_week 292 * return the 0 based day number for any date from 1 Jan. 1 to 293 * 31 Dec. 9999. Assumes the Gregorian reformation eliminates 294 * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all 295 * missing days. 296 */ 297 day_in_week(day, month, year) 298 int day, month, year; 299 { 300 long temp; 301 302 temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) 303 + day_in_year(day, month, year); 304 if (temp < FIRST_MISSING_DAY) 305 return((temp - 1 + SATURDAY) % 7); 306 if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) 307 return(((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); 308 return(THURSDAY); 309 } 310 311 ascii_day(p, day) 312 register char *p; 313 register int day; 314 { 315 register int display, val; 316 317 if (day == SPACE) { 318 memset(p, ' ', julian ? 4 : 3); 319 return; 320 } 321 display = 0; 322 if (julian) { 323 if (val = day / 100) { 324 day %= 100; 325 *p++ = val + '0'; 326 display = 1; 327 } else 328 *p++ = ' '; 329 } 330 val = day / 10; 331 if (val || display) 332 *p++ = val + '0'; 333 else 334 *p++ = ' '; 335 *p++ = day % 10 + '0'; 336 *p = ' '; 337 } 338 339 trim_trailing_spaces(s) 340 register char *s; 341 { 342 register char *p; 343 344 for (p = s; *p; ++p); 345 while (p > s && isspace(*--p)); 346 if (p > s) 347 ++p; 348 *p = '\0'; 349 } 350 351 center(str, len, separate) 352 char *str; 353 register int len; 354 int separate; 355 { 356 len -= strlen(str); 357 (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, ""); 358 if (separate) 359 (void)printf("%*s", separate, ""); 360 } 361 362 usage() 363 { 364 (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n"); 365 exit(1); 366 } 367