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