1 /* $OpenBSD: day.c,v 1.21 2008/04/13 00:22:17 djm Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #ifndef lint 33 static const char copyright[] = 34 "@(#) Copyright (c) 1989, 1993\n\ 35 The Regents of the University of California. All rights reserved.\n"; 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static const char sccsid[] = "@(#)calendar.c 8.3 (Berkeley) 3/25/94"; 41 #else 42 static const char rcsid[] = "$OpenBSD: day.c,v 1.21 2008/04/13 00:22:17 djm Exp $"; 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/types.h> 47 #include <sys/uio.h> 48 49 #include <ctype.h> 50 #include <err.h> 51 #include <locale.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <time.h> 56 #include <tzfile.h> 57 58 #include "pathnames.h" 59 #include "calendar.h" 60 61 #define WEEKLY 1 62 #define MONTHLY 2 63 #define YEARLY 3 64 65 struct tm *tp; 66 int *cumdays, offset; 67 char dayname[10]; 68 enum calendars calendar; 69 u_long julian; 70 71 72 /* 1-based month, 0-based days, cumulative */ 73 int daytab[][14] = { 74 { 0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }, 75 { 0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 76 }; 77 78 static char *days[] = { 79 "sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL, 80 }; 81 82 static char *months[] = { 83 "jan", "feb", "mar", "apr", "may", "jun", 84 "jul", "aug", "sep", "oct", "nov", "dec", NULL, 85 }; 86 87 static struct fixs fndays[8]; /* full national days names */ 88 static struct fixs ndays[8]; /* short national days names */ 89 90 static struct fixs fnmonths[13]; /* full national months names */ 91 static struct fixs nmonths[13]; /* short national month names */ 92 93 94 void 95 setnnames(void) 96 { 97 char buf[80]; 98 int i, l; 99 struct tm tm; 100 101 for (i = 0; i < 7; i++) { 102 tm.tm_wday = i; 103 l = strftime(buf, sizeof(buf), "%a", &tm); 104 for (; l > 0 && isspace((int)buf[l - 1]); l--) 105 ; 106 buf[l] = '\0'; 107 if (ndays[i].name != NULL) 108 free(ndays[i].name); 109 if ((ndays[i].name = strdup(buf)) == NULL) 110 err(1, NULL); 111 ndays[i].len = strlen(buf); 112 113 l = strftime(buf, sizeof(buf), "%A", &tm); 114 for (; l > 0 && isspace((int)buf[l - 1]); l--) 115 ; 116 buf[l] = '\0'; 117 if (fndays[i].name != NULL) 118 free(fndays[i].name); 119 if ((fndays[i].name = strdup(buf)) == NULL) 120 err(1, NULL); 121 fndays[i].len = strlen(buf); 122 } 123 124 for (i = 0; i < 12; i++) { 125 tm.tm_mon = i; 126 l = strftime(buf, sizeof(buf), "%b", &tm); 127 for (; l > 0 && isspace((int)buf[l - 1]); l--) 128 ; 129 buf[l] = '\0'; 130 if (nmonths[i].name != NULL) 131 free(nmonths[i].name); 132 if ((nmonths[i].name = strdup(buf)) == NULL) 133 err(1, NULL); 134 nmonths[i].len = strlen(buf); 135 136 l = strftime(buf, sizeof(buf), "%B", &tm); 137 for (; l > 0 && isspace((int)buf[l - 1]); l--) 138 ; 139 buf[l] = '\0'; 140 if (fnmonths[i].name != NULL) 141 free(fnmonths[i].name); 142 if ((fnmonths[i].name = strdup(buf)) == NULL) 143 err(1, NULL); 144 fnmonths[i].len = strlen(buf); 145 } 146 /* Hardwired special events */ 147 spev[0].name = strdup(PESACH); 148 spev[0].nlen = PESACHLEN; 149 spev[0].getev = pesach; 150 spev[1].name = strdup(EASTER); 151 spev[1].nlen = EASTERNAMELEN; 152 spev[1].getev = easter; 153 spev[2].name = strdup(PASKHA); 154 spev[2].nlen = PASKHALEN; 155 spev[2].getev = paskha; 156 for (i = 0; i < NUMEV; i++) { 157 if (spev[i].name == NULL) 158 err(1, NULL); 159 spev[i].uname = NULL; 160 } 161 } 162 163 void 164 settime(time_t *now) 165 { 166 tp = localtime(now); 167 tp->tm_sec = 0; 168 tp->tm_min = 0; 169 /* Avoid getting caught by a timezone shift; set time to noon */ 170 tp->tm_isdst = 0; 171 tp->tm_hour = 12; 172 *now = mktime(tp); 173 if (isleap(tp->tm_year + TM_YEAR_BASE)) 174 cumdays = daytab[1]; 175 else 176 cumdays = daytab[0]; 177 /* Friday displays Monday's events */ 178 offset = tp->tm_wday == 5 ? 3 : 1; 179 if (f_SetdayAfter) 180 offset = 0; /* Except not when range is set explicitly */ 181 header[5].iov_base = dayname; 182 183 (void) setlocale(LC_TIME, "C"); 184 header[5].iov_len = strftime(dayname, sizeof(dayname), "%A", tp); 185 (void) setlocale(LC_TIME, ""); 186 187 setnnames(); 188 } 189 190 /* convert [Year][Month]Day into unix time (since 1970) 191 * Year: two or four digits, Month: two digits, Day: two digits 192 */ 193 time_t 194 Mktime(char *date) 195 { 196 time_t t; 197 int len; 198 struct tm tm; 199 200 (void)time(&t); 201 tp = localtime(&t); 202 203 len = strlen(date); 204 if (len < 2) 205 return((time_t)-1); 206 tm.tm_sec = 0; 207 tm.tm_min = 0; 208 /* Avoid getting caught by a timezone shift; set time to noon */ 209 tm.tm_isdst = 0; 210 tm.tm_hour = 12; 211 tm.tm_wday = 0; 212 tm.tm_mday = tp->tm_mday; 213 tm.tm_mon = tp->tm_mon; 214 tm.tm_year = tp->tm_year; 215 216 /* Day */ 217 tm.tm_mday = atoi(date + len - 2); 218 219 /* Month */ 220 if (len >= 4) { 221 *(date + len - 2) = '\0'; 222 tm.tm_mon = atoi(date + len - 4) - 1; 223 } 224 225 /* Year */ 226 if (len >= 6) { 227 *(date + len - 4) = '\0'; 228 tm.tm_year = atoi(date); 229 230 /* tm_year up TM_YEAR_BASE ... */ 231 if (tm.tm_year < 69) /* Y2K */ 232 tm.tm_year += 2000 - TM_YEAR_BASE; 233 else if (tm.tm_year < 100) 234 tm.tm_year += 1900 - TM_YEAR_BASE; 235 else if (tm.tm_year > TM_YEAR_BASE) 236 tm.tm_year -= TM_YEAR_BASE; 237 } 238 239 #if DEBUG 240 printf("Mktime: %d %d %d %s\n", (int)mktime(&tm), (int)t, len, 241 asctime(&tm)); 242 #endif 243 return(mktime(&tm)); 244 } 245 246 void 247 adjust_calendar(int *day, int *month) 248 { 249 switch (calendar) { 250 case GREGORIAN: 251 break; 252 253 case JULIAN: 254 *day += julian; 255 if (*day > (cumdays[*month + 1] - cumdays[*month])) { 256 *day -= (cumdays[*month + 1] - cumdays[*month]); 257 if (++*month > 12) 258 *month = 1; 259 } 260 break; 261 case LUNAR: 262 break; 263 } 264 } 265 266 /* 267 * Possible date formats include any combination of: 268 * 3-charmonth (January, Jan, Jan) 269 * 3-charweekday (Friday, Monday, mon.) 270 * numeric month or day (1, 2, 04) 271 * 272 * Any character except \t or '*' may separate them, or they may not be 273 * separated. Any line following a line that is matched, that starts 274 * with \t, is shown along with the matched line. 275 */ 276 struct match * 277 isnow(char *endp, int bodun) 278 { 279 int day = 0, flags = 0, month = 0, v1, v2, i; 280 int monthp, dayp, varp = 0; 281 struct match *matches = NULL, *tmp, *tmp2; 282 int interval = YEARLY; /* how frequently the event repeats. */ 283 int vwd = 0; /* Variable weekday */ 284 time_t tdiff, ttmp; 285 struct tm tmtmp; 286 287 /* 288 * CONVENTION 289 * 290 * Month: 1-12 291 * Monthname: Jan .. Dec 292 * Day: 1-31 293 * Weekday: Mon-Sun 294 * 295 */ 296 297 /* read first field */ 298 /* didn't recognize anything, skip it */ 299 if (!(v1 = getfield(endp, &endp, &flags))) 300 return (NULL); 301 302 /* adjust bodun rate */ 303 if (bodun && !bodun_always) 304 bodun = !arc4random_uniform(3); 305 306 /* Easter or Easter depending days */ 307 if (flags & F_SPECIAL) 308 vwd = v1; 309 310 /* 311 * 1. {Weekday,Day} XYZ ... 312 * 313 * where Day is > 12 314 */ 315 else if (flags & F_ISDAY || v1 > 12) { 316 317 /* found a day; day: 13-31 or weekday: 1-7 */ 318 day = v1; 319 320 /* {Day,Weekday} {Month,Monthname} ... */ 321 /* if no recognizable month, assume just a day alone -- this is 322 * very unlikely and can only happen after the first 12 days. 323 * --find month or use current month */ 324 if (!(month = getfield(endp, &endp, &flags))) { 325 month = tp->tm_mon + 1; 326 /* F_ISDAY is set only if a weekday was spelled out */ 327 /* F_ISDAY must be set if 0 < day < 8 */ 328 if ((day <= 7) && (day >= 1)) 329 interval = WEEKLY; 330 else 331 interval = MONTHLY; 332 } else if ((day <= 7) && (day >= 1)) 333 day += 10; 334 /* it's a weekday; make it the first one of the month */ 335 if (month == -1) { 336 month = tp->tm_mon + 1; 337 interval = MONTHLY; 338 } else if (calendar) 339 adjust_calendar(&day, &month); 340 if ((month > 12) || (month < 1)) 341 return (NULL); 342 } 343 344 /* 2. {Monthname} XYZ ... */ 345 else if (flags & F_ISMONTH) { 346 month = v1; 347 if (month == -1) { 348 month = tp->tm_mon + 1; 349 interval = MONTHLY; 350 } 351 /* Monthname {day,weekday} */ 352 /* if no recognizable day, assume the first day in month */ 353 if (!(day = getfield(endp, &endp, &flags))) 354 day = 1; 355 /* If a weekday was spelled out without an ordering, 356 * assume the first of that day in the month */ 357 if ((flags & F_ISDAY)) { 358 if ((day >= 1) && (day <=7)) 359 day += 10; 360 } else if (calendar) 361 adjust_calendar(&day, &month); 362 } 363 364 /* Hm ... */ 365 else { 366 v2 = getfield(endp, &endp, &flags); 367 368 /* 369 * {Day} {Monthname} ... 370 * where Day <= 12 371 */ 372 if (flags & F_ISMONTH) { 373 day = v1; 374 month = v2; 375 if (month == -1) { 376 month = tp->tm_mon + 1; 377 interval = MONTHLY; 378 } else if (calendar) 379 adjust_calendar(&day, &month); 380 } 381 382 /* {Month} {Weekday,Day} ... */ 383 else { 384 /* F_ISDAY set, v2 > 12, or no way to tell */ 385 month = v1; 386 /* if no recognizable day, assume the first */ 387 day = v2 ? v2 : 1; 388 if ((flags & F_ISDAY)) { 389 if ((day >= 1) && (day <= 7)) 390 day += 10; 391 } else 392 adjust_calendar(&day, &month); 393 } 394 } 395 396 /* convert Weekday into *next* Day, 397 * e.g.: 'Sunday' -> 22 398 * 'SundayLast' -> ?? 399 */ 400 if (flags & F_ISDAY) { 401 #if DEBUG 402 fprintf(stderr, "\nday: %d %s month %d\n", day, endp, month); 403 #endif 404 405 varp = 1; 406 /* variable weekday, SundayLast, MondayFirst ... */ 407 if (day < 0 || day >= 10) 408 vwd = day; 409 else { 410 day = tp->tm_mday + (((day - 1) - tp->tm_wday + 7) % 7); 411 interval = WEEKLY; 412 } 413 } else 414 /* Check for silliness. Note we still catch Feb 29 */ 415 if (!(flags & F_SPECIAL) && 416 (day > (cumdays[month + 1] - cumdays[month]) || day < 1)) { 417 if (!((month == 2 && day == 29) || 418 (interval == MONTHLY && day <= 31))) 419 return (NULL); 420 } 421 422 if (!(flags & F_SPECIAL)) { 423 monthp = month; 424 dayp = day; 425 day = cumdays[month] + day; 426 #if DEBUG 427 fprintf(stderr, "day2: day %d(%d) yday %d\n", dayp, day, tp->tm_yday); 428 #endif 429 /* Speed up processing for the most common situation: yearly events 430 * when the interval being checked is less than a month or so (this 431 * could be less than a year, but then we have to start worrying about 432 * leap years). Only one event can match, and it's easy to find. 433 * Note we can't check special events, because they can wander widely. 434 */ 435 if (((v1 = offset + f_dayAfter) < 50) && (interval == YEARLY)) { 436 memcpy(&tmtmp, tp, sizeof(struct tm)); 437 tmtmp.tm_mday = dayp; 438 tmtmp.tm_mon = monthp - 1; 439 if (vwd) { 440 /* We want the event next year if it's late now 441 * this year. The 50-day limit means we don't have to 442 * worry if next year is or isn't a leap year. 443 */ 444 if (tp->tm_yday > 300 && tmtmp.tm_mon <= 1) 445 variable_weekday(&vwd, tmtmp.tm_mon + 1, 446 tmtmp.tm_year + TM_YEAR_BASE + 1); 447 else 448 variable_weekday(&vwd, tmtmp.tm_mon + 1, 449 tmtmp.tm_year + TM_YEAR_BASE); 450 day = cumdays[tmtmp.tm_mon + 1] + vwd; 451 tmtmp.tm_mday = vwd; 452 } 453 v2 = day - tp->tm_yday; 454 if ((v2 > v1) || (v2 < 0)) { 455 if ((v2 += isleap(tp->tm_year + TM_YEAR_BASE) ? 366 : 365) 456 <= v1) 457 tmtmp.tm_year++; 458 else if(!bodun || (day - tp->tm_yday) != -1) 459 return(NULL); 460 } 461 if ((tmp = malloc(sizeof(struct match))) == NULL) 462 err(1, NULL); 463 464 if (bodun && (day - tp->tm_yday) == -1) { 465 tmp->when = f_time - 1 * SECSPERDAY; 466 tmtmp.tm_mday++; 467 tmp->bodun = 1; 468 } else { 469 tmp->when = f_time + v2 * SECSPERDAY; 470 tmp->bodun = 0; 471 } 472 473 (void)mktime(&tmtmp); 474 if (strftime(tmp->print_date, 475 sizeof(tmp->print_date), 476 /* "%a %b %d", &tm); Skip weekdays */ 477 "%b %d", &tmtmp) == 0) 478 tmp->print_date[sizeof(tmp->print_date) - 1] = '\0'; 479 480 tmp->var = varp; 481 tmp->next = NULL; 482 return(tmp); 483 } 484 } else { 485 varp = 1; 486 /* Set up v1 to the event number and ... */ 487 v1 = vwd % (NUMEV + 1) - 1; 488 vwd /= (NUMEV + 1); 489 if (v1 < 0) { 490 v1 += NUMEV + 1; 491 vwd--; 492 } 493 dayp = monthp = 1; /* Why not */ 494 } 495 496 /* Compare to past and coming instances of the event. The i == 0 part 497 * of the loop corresponds to this specific instance. Note that we 498 * can leave things sort of higgledy-piggledy since a mktime() happens 499 * on this before anything gets printed. Also note that even though 500 * we've effectively gotten rid of f_dayBefore, we still have to check 501 * the one prior event for situations like "the 31st of every month" 502 * and "yearly" events which could happen twice in one year but not in 503 * the next */ 504 tmp2 = matches; 505 for (i = -1; i < 2; i++) { 506 memcpy(&tmtmp, tp, sizeof(struct tm)); 507 tmtmp.tm_mday = dayp; 508 tmtmp.tm_mon = month = monthp - 1; 509 do { 510 v2 = 0; 511 switch (interval) { 512 case WEEKLY: 513 tmtmp.tm_mday += 7 * i; 514 break; 515 case MONTHLY: 516 month += i; 517 tmtmp.tm_mon = month; 518 switch(tmtmp.tm_mon) { 519 case -1: 520 tmtmp.tm_mon = month = 11; 521 tmtmp.tm_year--; 522 break; 523 case 12: 524 tmtmp.tm_mon = month = 0; 525 tmtmp.tm_year++; 526 break; 527 } 528 if (vwd) { 529 v1 = vwd; 530 variable_weekday(&v1, tmtmp.tm_mon + 1, 531 tmtmp.tm_year + TM_YEAR_BASE); 532 tmtmp.tm_mday = v1; 533 } else 534 tmtmp.tm_mday = dayp; 535 break; 536 case YEARLY: 537 default: 538 tmtmp.tm_year += i; 539 if (flags & F_SPECIAL) { 540 tmtmp.tm_mon = 0; /* Gee, mktime() is nice */ 541 tmtmp.tm_mday = spev[v1].getev(tmtmp.tm_year + 542 TM_YEAR_BASE) + vwd; 543 } else if (vwd) { 544 v1 = vwd; 545 variable_weekday(&v1, tmtmp.tm_mon + 1, 546 tmtmp.tm_year + TM_YEAR_BASE); 547 tmtmp.tm_mday = v1; 548 } else { 549 /* Need the following to keep Feb 29 from 550 * becoming Mar 1 */ 551 tmtmp.tm_mday = dayp; 552 tmtmp.tm_mon = monthp - 1; 553 } 554 break; 555 } 556 /* How many days apart are we */ 557 if ((ttmp = mktime(&tmtmp)) == -1) 558 warnx("time out of range: %s", endp); 559 else { 560 tdiff = difftime(ttmp, f_time)/ SECSPERDAY; 561 if (tdiff <= offset + f_dayAfter || 562 (bodun && tdiff == -1)) { 563 if (tdiff >= 0 || 564 (bodun && tdiff == -1)) { 565 if ((tmp = malloc(sizeof(struct match))) == NULL) 566 err(1, NULL); 567 tmp->when = ttmp; 568 if (strftime(tmp->print_date, 569 sizeof(tmp->print_date), 570 /* "%a %b %d", &tm); Skip weekdays */ 571 "%b %d", &tmtmp) == 0) 572 tmp->print_date[sizeof(tmp->print_date) - 1] = '\0'; 573 tmp->bodun = bodun && tdiff == -1; 574 tmp->var = varp; 575 tmp->next = NULL; 576 if (tmp2) 577 tmp2->next = tmp; 578 else 579 matches = tmp; 580 tmp2 = tmp; 581 v2 = (i == 1) ? 1 : 0; 582 } 583 } else 584 i = 2; /* No point checking in the future */ 585 } 586 } while (v2 != 0); 587 } 588 return (matches); 589 } 590 591 592 int 593 getmonth(char *s) 594 { 595 char **p; 596 struct fixs *n; 597 598 for (n = fnmonths; n->name; ++n) 599 if (!strncasecmp(s, n->name, n->len)) 600 return ((n - fnmonths) + 1); 601 for (n = nmonths; n->name; ++n) 602 if (!strncasecmp(s, n->name, n->len)) 603 return ((n - nmonths) + 1); 604 for (p = months; *p; ++p) 605 if (!strncasecmp(s, *p, 3)) 606 return ((p - months) + 1); 607 return (0); 608 } 609 610 611 int 612 getday(char *s) 613 { 614 char **p; 615 struct fixs *n; 616 617 for (n = fndays; n->name; ++n) 618 if (!strncasecmp(s, n->name, n->len)) 619 return ((n - fndays) + 1); 620 for (n = ndays; n->name; ++n) 621 if (!strncasecmp(s, n->name, n->len)) 622 return ((n - ndays) + 1); 623 for (p = days; *p; ++p) 624 if (!strncasecmp(s, *p, 3)) 625 return ((p - days) + 1); 626 return (0); 627 } 628 629 /* return offset for variable weekdays 630 * -1 -> last weekday in month 631 * +1 -> first weekday in month 632 * ... etc ... 633 */ 634 int 635 getdayvar(char *s) 636 { 637 int offset; 638 639 640 offset = strlen(s); 641 642 /* Sun+1 or Wednesday-2 643 * ^ ^ */ 644 645 /* printf ("x: %s %s %d\n", s, s + offset - 2, offset); */ 646 switch(*(s + offset - 2)) { 647 case '-': 648 case '+': 649 return(atoi(s + offset - 2)); 650 break; 651 } 652 653 /* 654 * some aliases: last, first, second, third, fourth 655 */ 656 657 /* last */ 658 if (offset > 4 && !strcasecmp(s + offset - 4, "last")) 659 return(-1); 660 else if (offset > 5 && !strcasecmp(s + offset - 5, "first")) 661 return(+1); 662 else if (offset > 6 && !strcasecmp(s + offset - 6, "second")) 663 return(+2); 664 else if (offset > 5 && !strcasecmp(s + offset - 5, "third")) 665 return(+3); 666 else if (offset > 6 && !strcasecmp(s + offset - 6, "fourth")) 667 return(+4); 668 669 /* no offset detected */ 670 return(0); 671 } 672 673 674 int 675 foy(int year) 676 { 677 /* 0-6; what weekday Jan 1 is */ 678 year--; 679 return ((1 - year/100 + year/400 + (int)(365.25 * year)) % 7); 680 } 681 682 683 684 void 685 variable_weekday(int *day, int month, int year) 686 { 687 int v1, v2; 688 int *cumdays; 689 int day1; 690 691 if (isleap(year)) 692 cumdays = daytab[1]; 693 else 694 cumdays = daytab[0]; 695 day1 = foy(year); 696 /* negative offset; last, -4 .. -1 */ 697 if (*day < 0) { 698 v1 = *day/10 - 1; /* offset -4 ... -1 */ 699 *day = 10 + (*day % 10); /* day 1 ... 7 */ 700 701 /* which weekday the end of the month is (1-7) */ 702 v2 = (cumdays[month + 1] + day1) % 7 + 1; 703 704 /* and subtract enough days */ 705 *day = cumdays[month + 1] - cumdays[month] + 706 (v1 + 1) * 7 - (v2 - *day + 7) % 7; 707 #if DEBUG 708 fprintf(stderr, "\nMonth %d ends on weekday %d\n", month, v2); 709 #endif 710 } 711 712 /* first, second ... +1 ... +5 */ 713 else { 714 v1 = *day/10; /* offset */ 715 *day = *day % 10; 716 717 /* which weekday the first of the month is (1-7) */ 718 v2 = (cumdays[month] + 1 + day1) % 7 + 1; 719 720 /* and add enough days */ 721 *day = 1 + (v1 - 1) * 7 + (*day - v2 + 7) % 7; 722 #if DEBUG 723 fprintf(stderr, "\nMonth %d starts on weekday %d\n", month, v2); 724 #endif 725 } 726 } 727