160484Sobrien/*- 2218822Sdim * ------+---------+---------+---------+---------+---------+---------+---------* 3218822Sdim * Initial version of parse8601 was originally added to newsyslog.c in 460484Sobrien * FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>. 560484Sobrien * Initial version of parseDWM was originally added to newsyslog.c in 660484Sobrien * FreeBSD on Apr 4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>. 760484Sobrien * 860484Sobrien * Copyright (c) 2003 - Garance Alistair Drosehn <gad@FreeBSD.org>. 960484Sobrien * All rights reserved. 1060484Sobrien * 1160484Sobrien * Redistribution and use in source and binary forms, with or without 1260484Sobrien * modification, are permitted provided that the following conditions 1360484Sobrien * are met: 1460484Sobrien * 1. Redistributions of source code must retain the above copyright 1560484Sobrien * notice, this list of conditions and the following disclaimer. 1660484Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1760484Sobrien * notice, this list of conditions and the following disclaimer in the 1860484Sobrien * documentation and/or other materials provided with the distribution. 1960484Sobrien * 2060484Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21218822Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22218822Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2360484Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2460484Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2560484Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2660484Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2760484Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2860484Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2960484Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3060484Sobrien * SUCH DAMAGE. 3160484Sobrien * 3260484Sobrien * The views and conclusions contained in the software and documentation 3360484Sobrien * are those of the authors and should not be interpreted as representing 3460484Sobrien * official policies, either expressed or implied, of the FreeBSD Project. 3560484Sobrien * 3660484Sobrien * ------+---------+---------+---------+---------+---------+---------+---------* 3760484Sobrien * This is intended to be a set of general-purpose routines to process times. 3860484Sobrien * Right now it probably still has a number of assumptions in it, such that 3960484Sobrien * it works fine for newsyslog but might not work for other uses. 4060484Sobrien * ------+---------+---------+---------+---------+---------+---------+---------* 4160484Sobrien */ 4260484Sobrien 4360484Sobrien#include <sys/cdefs.h> 4460484Sobrien__FBSDID("$FreeBSD: stable/10/usr.sbin/newsyslog/ptimes.c 321263 2017-07-20 00:44:01Z ngie $"); 4560484Sobrien 4660484Sobrien#include <ctype.h> 4760484Sobrien#include <limits.h> 48130561Sobrien#include <stdio.h> 49130561Sobrien#include <stdlib.h> 5060484Sobrien#include <string.h> 51130561Sobrien#include <time.h> 52130561Sobrien 53130561Sobrien#include "extern.h" 54130561Sobrien 55130561Sobrien#define SECS_PER_HOUR 3600 56130561Sobrien 57130561Sobrien/* 58130561Sobrien * Bit-values which indicate which components of time were specified 59130561Sobrien * by the string given to parse8601 or parseDWM. These are needed to 60130561Sobrien * calculate what time-in-the-future will match that string. 61130561Sobrien */ 62130561Sobrien#define TSPEC_YEAR 0x0001 63130561Sobrien#define TSPEC_MONTHOFYEAR 0x0002 64130561Sobrien#define TSPEC_LDAYOFMONTH 0x0004 6560484Sobrien#define TSPEC_DAYOFMONTH 0x0008 6660484Sobrien#define TSPEC_DAYOFWEEK 0x0010 67130561Sobrien#define TSPEC_HOUROFDAY 0x0020 68130561Sobrien 69218822Sdim#define TNYET_ADJ4DST -10 /* DST has "not yet" been adjusted */ 70218822Sdim 71130561Sobrienstruct ptime_data { 72130561Sobrien time_t basesecs; /* Base point for relative times */ 73130561Sobrien time_t tsecs; /* Time in seconds */ 7460484Sobrien struct tm basetm; /* Base Time expanded into fields */ 7560484Sobrien struct tm tm; /* Time expanded into fields */ 7660484Sobrien int did_adj4dst; /* Track calls to ptime_adjust4dst */ 77130561Sobrien int parseopts; /* Options given for parsing */ 78130561Sobrien int tmspec; /* Indicates which time fields had 79130561Sobrien * been specified by the user */ 8060484Sobrien}; 8160484Sobrien 82130561Sobrienstatic int days_pmonth(int month, int year); 8377298Sobrienstatic int parse8601(struct ptime_data *ptime, const char *str); 84218822Sdimstatic int parseDWM(struct ptime_data *ptime, const char *str); 85218822Sdim 86218822Sdim/* 8760484Sobrien * Simple routine to calculate the number of days in a given month. 88218822Sdim */ 89218822Sdimstatic int 90218822Sdimdays_pmonth(int month, int year) 9160484Sobrien{ 9260484Sobrien static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 9360484Sobrien 30, 31, 30, 31}; 9460484Sobrien int ndays; 9560484Sobrien 9660484Sobrien ndays = mtab[month]; 9760484Sobrien 9860484Sobrien if (month == 1) { 9960484Sobrien /* 100218822Sdim * We are usually called with a 'tm-year' value 101218822Sdim * (ie, the value = the number of years past 1900). 102218822Sdim */ 103218822Sdim if (year < 1900) 104218822Sdim year += 1900; 105218822Sdim if (year % 4 == 0) { 10660484Sobrien /* 10760484Sobrien * This is a leap year, as long as it is not a 10860484Sobrien * multiple of 100, or if it is a multiple of 10960484Sobrien * both 100 and 400. 110218822Sdim */ 111218822Sdim if (year % 100 != 0) 11260484Sobrien ndays++; /* not multiple of 100 */ 11360484Sobrien else if (year % 400 == 0) 11460484Sobrien ndays++; /* is multiple of 100 and 400 */ 115130561Sobrien } 11660484Sobrien } 11760484Sobrien return (ndays); 11860484Sobrien} 11960484Sobrien 12060484Sobrien/*- 121130561Sobrien * Parse a limited subset of ISO 8601. The specific format is as follows: 12260484Sobrien * 12360484Sobrien * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 12460484Sobrien * 12560484Sobrien * We don't accept a timezone specification; missing fields (including timezone) 126218822Sdim * are defaulted to the current date but time zero. 127218822Sdim */ 128218822Sdimstatic int 129218822Sdimparse8601(struct ptime_data *ptime, const char *s) 130218822Sdim{ 131218822Sdim char *t; 132218822Sdim long l; 133218822Sdim struct tm tm; 134218822Sdim 135218822Sdim l = strtol(s, &t, 10); 136218822Sdim if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T')) 13760484Sobrien return (-1); 138218822Sdim 13960484Sobrien /* 14060484Sobrien * Now t points either to the end of the string (if no time was 14160484Sobrien * provided) or to the letter `T' which separates date and time in 14260484Sobrien * ISO 8601. The pointer arithmetic is the same for either case. 14360484Sobrien */ 144218822Sdim tm = ptime->tm; 145218822Sdim ptime->tmspec = TSPEC_HOUROFDAY; 146218822Sdim switch (t - s) { 147218822Sdim case 8: 148218822Sdim tm.tm_year = ((l / 1000000) - 19) * 100; 149218822Sdim l = l % 1000000; 150130561Sobrien case 6: 151130561Sobrien ptime->tmspec |= TSPEC_YEAR; 152130561Sobrien tm.tm_year -= tm.tm_year % 100; 15360484Sobrien tm.tm_year += l / 10000; 15460484Sobrien l = l % 10000; 15560484Sobrien case 4: 15660484Sobrien ptime->tmspec |= TSPEC_MONTHOFYEAR; 15760484Sobrien tm.tm_mon = (l / 100) - 1; 158218822Sdim l = l % 100; 159218822Sdim case 2: 16060484Sobrien ptime->tmspec |= TSPEC_DAYOFMONTH; 161130561Sobrien tm.tm_mday = l; 162130561Sobrien case 0: 163130561Sobrien break; 164130561Sobrien default: 165130561Sobrien return (-1); 16660484Sobrien } 16760484Sobrien 168130561Sobrien /* sanity check */ 169130561Sobrien if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 170130561Sobrien || tm.tm_mday < 1 || tm.tm_mday > 31) 171130561Sobrien return (-1); 172130561Sobrien 17360484Sobrien if (*t != '\0') { 174218822Sdim s = ++t; 175218822Sdim l = strtol(s, &t, 10); 176218822Sdim if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t))) 177218822Sdim return (-1); 178218822Sdim 179218822Sdim switch (t - s) { 18060484Sobrien case 6: 18160484Sobrien tm.tm_sec = l % 100; 18278828Sobrien l /= 100; 18378828Sobrien case 4: 18478828Sobrien tm.tm_min = l % 100; 18578828Sobrien l /= 100; 18678828Sobrien case 2: 187130561Sobrien ptime->tmspec |= TSPEC_HOUROFDAY; 18878828Sobrien tm.tm_hour = l; 189130561Sobrien case 0: 19078828Sobrien break; 19178828Sobrien default: 19278828Sobrien return (-1); 19378828Sobrien } 19478828Sobrien 19578828Sobrien /* sanity check */ 19678828Sobrien if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 197130561Sobrien || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 198218822Sdim return (-1); 199218822Sdim } 200218822Sdim 201218822Sdim ptime->tm = tm; 202218822Sdim return (0); 203218822Sdim} 204218822Sdim 205218822Sdim/*- 206130561Sobrien * Parse a cyclic time specification, the format is as follows: 207130561Sobrien * 208130561Sobrien * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] 209218822Sdim * 210130561Sobrien * to rotate a logfile cyclic at 211218822Sdim * 212130561Sobrien * - every day (D) within a specific hour (hh) (hh = 0...23) 213218822Sdim * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) 214218822Sdim * - once a month (M) at a specific day (d) (d = 1..31,l|L) 215218822Sdim * 216218822Sdim * We don't accept a timezone specification; missing fields 217218822Sdim * are defaulted to the current date but time zero. 218218822Sdim */ 219130561Sobrienstatic int 220130561SobrienparseDWM(struct ptime_data *ptime, const char *s) 221130561Sobrien{ 222130561Sobrien int daysmon, Dseen, WMseen; 223130561Sobrien const char *endval; 224130561Sobrien char *tmp; 225130561Sobrien long l; 226130561Sobrien struct tm tm; 227218822Sdim 228218822Sdim /* Save away the number of days in this month */ 229218822Sdim tm = ptime->tm; 230218822Sdim daysmon = days_pmonth(tm.tm_mon, tm.tm_year); 231218822Sdim 232218822Sdim WMseen = Dseen = 0; 233218822Sdim ptime->tmspec = TSPEC_HOUROFDAY; 234218822Sdim for (;;) { 235218822Sdim endval = NULL; 236218822Sdim switch (*s) { 237218822Sdim case 'D': 238218822Sdim if (Dseen) 239218822Sdim return (-1); 240218822Sdim Dseen++; 241218822Sdim ptime->tmspec |= TSPEC_HOUROFDAY; 242130561Sobrien s++; 243130561Sobrien l = strtol(s, &tmp, 10); 244130561Sobrien if (l < 0 || l > 23) 245130561Sobrien return (-1); 246130561Sobrien endval = tmp; 247130561Sobrien tm.tm_hour = l; 248130561Sobrien break; 249130561Sobrien 250218822Sdim case 'W': 251130561Sobrien if (WMseen) 252130561Sobrien return (-1); 253130561Sobrien WMseen++; 254130561Sobrien ptime->tmspec |= TSPEC_DAYOFWEEK; 255130561Sobrien s++; 256218822Sdim l = strtol(s, &tmp, 10); 257218822Sdim if (l < 0 || l > 6) 258218822Sdim return (-1); 259130561Sobrien endval = tmp; 260130561Sobrien if (l != tm.tm_wday) { 261130561Sobrien int save; 262130561Sobrien 263130561Sobrien if (l < tm.tm_wday) { 264130561Sobrien save = 6 - tm.tm_wday; 265130561Sobrien save += (l + 1); 266130561Sobrien } else { 267130561Sobrien save = l - tm.tm_wday; 268130561Sobrien } 269130561Sobrien 270130561Sobrien tm.tm_mday += save; 271130561Sobrien 272130561Sobrien if (tm.tm_mday > daysmon) { 273130561Sobrien tm.tm_mon++; 274130561Sobrien tm.tm_mday = tm.tm_mday - daysmon; 275218822Sdim } 276218822Sdim } 277218822Sdim break; 278218822Sdim 279218822Sdim case 'M': 280218822Sdim if (WMseen) 281218822Sdim return (-1); 282218822Sdim WMseen++; 283218822Sdim ptime->tmspec |= TSPEC_DAYOFMONTH; 284218822Sdim s++; 285218822Sdim if (tolower(*s) == 'l') { 286218822Sdim /* User wants the last day of the month. */ 287 ptime->tmspec |= TSPEC_LDAYOFMONTH; 288 tm.tm_mday = daysmon; 289 endval = s + 1; 290 } else { 291 l = strtol(s, &tmp, 10); 292 if (l < 1 || l > 31) 293 return (-1); 294 295 if (l > daysmon) 296 return (-1); 297 endval = tmp; 298 tm.tm_mday = l; 299 } 300 break; 301 302 default: 303 return (-1); 304 break; 305 } 306 307 if (endval == NULL) 308 return (-1); 309 else if (*endval == '\0' || isspace(*endval)) 310 break; 311 else 312 s = endval; 313 } 314 315 ptime->tm = tm; 316 return (0); 317} 318 319/* 320 * Initialize a new ptime-related data area. 321 */ 322struct ptime_data * 323ptime_init(const struct ptime_data *optsrc) 324{ 325 struct ptime_data *newdata; 326 327 newdata = malloc(sizeof(struct ptime_data)); 328 if (optsrc != NULL) { 329 memcpy(newdata, optsrc, sizeof(struct ptime_data)); 330 } else { 331 memset(newdata, '\0', sizeof(struct ptime_data)); 332 newdata->did_adj4dst = TNYET_ADJ4DST; 333 } 334 335 return (newdata); 336} 337 338/* 339 * Adjust a given time if that time is in a different timezone than 340 * some other time. 341 */ 342int 343ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc) 344{ 345 struct ptime_data adjtime; 346 347 if (ptime == NULL) 348 return (-1); 349 350 /* 351 * Changes are not made to the given time until after all 352 * of the calculations have been successful. 353 */ 354 adjtime = *ptime; 355 356 /* Check to see if this adjustment was already made */ 357 if ((adjtime.did_adj4dst != TNYET_ADJ4DST) && 358 (adjtime.did_adj4dst == dstsrc->tm.tm_isdst)) 359 return (0); /* yes, so don't make it twice */ 360 361 /* See if daylight-saving has changed between the two times. */ 362 if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) { 363 if (adjtime.tm.tm_isdst == 1) 364 adjtime.tsecs -= SECS_PER_HOUR; 365 else if (adjtime.tm.tm_isdst == 0) 366 adjtime.tsecs += SECS_PER_HOUR; 367 adjtime.tm = *(localtime(&adjtime.tsecs)); 368 /* Remember that this adjustment has been made */ 369 adjtime.did_adj4dst = dstsrc->tm.tm_isdst; 370 /* 371 * XXX - Should probably check to see if changing the 372 * hour also changed the value of is_dst. What 373 * should we do in that case? 374 */ 375 } 376 377 *ptime = adjtime; 378 return (0); 379} 380 381int 382ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime, 383 const char *str) 384{ 385 int dpm, pres; 386 struct tm temp_tm; 387 388 ptime->parseopts = parseopts; 389 ptime->basesecs = basetime; 390 ptime->basetm = *(localtime(&ptime->basesecs)); 391 ptime->tm = ptime->basetm; 392 ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0; 393 394 /* 395 * Call a routine which sets ptime.tm and ptime.tspecs based 396 * on the given string and parsing-options. Note that the 397 * routine should not call mktime to set ptime.tsecs. 398 */ 399 if (parseopts & PTM_PARSE_DWM) 400 pres = parseDWM(ptime, str); 401 else 402 pres = parse8601(ptime, str); 403 if (pres < 0) { 404 ptime->tsecs = (time_t)pres; 405 return (pres); 406 } 407 408 /* 409 * Before calling mktime, check to see if we ended up with a 410 * "day-of-month" that does not exist in the selected month. 411 * If we did call mktime with that info, then mktime will 412 * make it look like the user specifically requested a day 413 * in the following month (eg: Feb 31 turns into Mar 3rd). 414 */ 415 dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); 416 if ((parseopts & PTM_PARSE_MATCHDOM) && 417 (ptime->tmspec & TSPEC_DAYOFMONTH) && 418 (ptime->tm.tm_mday> dpm)) { 419 /* 420 * ptime_nxtime() will want a ptime->tsecs value, 421 * but we need to avoid mktime resetting all the 422 * ptime->tm values. 423 */ 424 if (verbose && dbg_at_times > 1) 425 fprintf(stderr, 426 "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)", 427 ptime->tm.tm_year, ptime->tm.tm_mon, 428 ptime->tm.tm_mday, ptime->tm.tm_hour, 429 ptime->tm.tm_min, dpm); 430 temp_tm = ptime->tm; 431 ptime->tsecs = mktime(&temp_tm); 432 if (ptime->tsecs > (time_t)-1) 433 ptimeset_nxtime(ptime); 434 if (verbose && dbg_at_times > 1) 435 fprintf(stderr, 436 " to: %4d/%02d/%02d %02d:%02d\n", 437 ptime->tm.tm_year, ptime->tm.tm_mon, 438 ptime->tm.tm_mday, ptime->tm.tm_hour, 439 ptime->tm.tm_min); 440 } 441 442 /* 443 * Convert the ptime.tm into standard time_t seconds. Check 444 * for invalid times, which includes things like the hour lost 445 * when switching from "standard time" to "daylight saving". 446 */ 447 ptime->tsecs = mktime(&ptime->tm); 448 if (ptime->tsecs == (time_t)-1) { 449 ptime->tsecs = (time_t)-2; 450 return (-2); 451 } 452 453 return (0); 454} 455 456int 457ptime_free(struct ptime_data *ptime) 458{ 459 460 if (ptime == NULL) 461 return (-1); 462 463 free(ptime); 464 return (0); 465} 466 467/* 468 * Some trivial routines so ptime_data can remain a completely 469 * opaque type. 470 */ 471const char * 472ptimeget_ctime(const struct ptime_data *ptime) 473{ 474 475 if (ptime == NULL) 476 return ("Null time in ptimeget_ctime()\n"); 477 478 return (ctime(&ptime->tsecs)); 479} 480 481/* 482 * Generate a time of day string in an RFC5424 compatible format. Return a 483 * pointer to the buffer with the timestamp string or NULL if an error. If the 484 * time is not supplied, cannot be converted to local time, or the resulting 485 * string would overflow the buffer, the returned string will be the RFC5424 486 * NILVALUE. 487 */ 488char * 489ptimeget_ctime_rfc5424(const struct ptime_data *ptime, 490 char *timebuf, size_t bufsize) 491{ 492 static const char NILVALUE[] = {"-"}; /* RFC5424 specified NILVALUE */ 493 int chars; 494 struct tm tm; 495 int tz_hours; 496 int tz_mins; 497 long tz_offset; 498 char tz_sign; 499 500 if (timebuf == NULL) { 501 return (NULL); 502 } 503 504 if (bufsize < sizeof(NILVALUE)) { 505 return (NULL); 506 } 507 508 /* 509 * Convert to localtime. RFC5424 mandates the use of the NILVALUE if 510 * the time cannot be obtained, so use that if there is an error in the 511 * conversion. 512 */ 513 if (ptime == NULL || localtime_r(&(ptime->tsecs), &tm) == NULL) { 514 strlcpy(timebuf, NILVALUE, bufsize); 515 return (timebuf); 516 } 517 518 /* 519 * Convert the time to a string in RFC5424 format. The conversion 520 * cannot be done with strftime() because it cannot produce the correct 521 * timezone offset format. 522 */ 523 if (tm.tm_gmtoff < 0) { 524 tz_sign = '-'; 525 tz_offset = -tm.tm_gmtoff; 526 } else { 527 tz_sign = '+'; 528 tz_offset = tm.tm_gmtoff; 529 } 530 531 tz_hours = tz_offset / 3600; 532 tz_mins = (tz_offset % 3600) / 60; 533 534 chars = snprintf(timebuf, bufsize, 535 "%04d-%02d-%02d" /* date */ 536 "T%02d:%02d:%02d" /* time */ 537 "%c%02d:%02d", /* time zone offset */ 538 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 539 tm.tm_hour, tm.tm_min, tm.tm_sec, 540 tz_sign, tz_hours, tz_mins); 541 542 /* If the timestamp is too big for timebuf, return the NILVALUE. */ 543 if (chars >= (int)bufsize) { 544 strlcpy(timebuf, NILVALUE, bufsize); 545 } 546 547 return (timebuf); 548} 549 550double 551ptimeget_diff(const struct ptime_data *minuend, const struct 552 ptime_data *subtrahend) 553{ 554 555 /* Just like difftime(), we have no good error-return */ 556 if (minuend == NULL || subtrahend == NULL) 557 return (0.0); 558 559 return (difftime(minuend->tsecs, subtrahend->tsecs)); 560} 561 562time_t 563ptimeget_secs(const struct ptime_data *ptime) 564{ 565 566 if (ptime == NULL) 567 return (-1); 568 569 return (ptime->tsecs); 570} 571 572/* 573 * Generate an approximate timestamp for the next event, based on 574 * what parts of time were specified by the original parameter to 575 * ptime_relparse(). The result may be -1 if there is no obvious 576 * "next time" which will work. 577 */ 578int 579ptimeset_nxtime(struct ptime_data *ptime) 580{ 581 int moredays, tdpm, tmon, tyear; 582 struct ptime_data nextmatch; 583 584 if (ptime == NULL) 585 return (-1); 586 587 /* 588 * Changes are not made to the given time until after all 589 * of the calculations have been successful. 590 */ 591 nextmatch = *ptime; 592 /* 593 * If the user specified a year and we're already past that 594 * time, then there will never be another one! 595 */ 596 if (ptime->tmspec & TSPEC_YEAR) 597 return (-1); 598 599 /* 600 * The caller gave us a time in the past. Calculate how much 601 * time is needed to go from that valid rotate time to the 602 * next valid rotate time. We only need to get to the nearest 603 * hour, because newsyslog is only run once per hour. 604 */ 605 moredays = 0; 606 if (ptime->tmspec & TSPEC_MONTHOFYEAR) { 607 /* Special case: Feb 29th does not happen every year. */ 608 if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) { 609 nextmatch.tm.tm_year += 4; 610 if (days_pmonth(1, nextmatch.tm.tm_year) < 29) 611 nextmatch.tm.tm_year += 4; 612 } else { 613 nextmatch.tm.tm_year += 1; 614 } 615 nextmatch.tm.tm_isdst = -1; 616 nextmatch.tsecs = mktime(&nextmatch.tm); 617 618 } else if (ptime->tmspec & TSPEC_LDAYOFMONTH) { 619 /* 620 * Need to get to the last day of next month. Origtm is 621 * already at the last day of this month, so just add to 622 * it number of days in the next month. 623 */ 624 if (ptime->tm.tm_mon < 11) 625 moredays = days_pmonth(ptime->tm.tm_mon + 1, 626 ptime->tm.tm_year); 627 else 628 moredays = days_pmonth(0, ptime->tm.tm_year + 1); 629 630 } else if (ptime->tmspec & TSPEC_DAYOFMONTH) { 631 /* Jump to the same day in the next month */ 632 moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); 633 /* 634 * In some cases, the next month may not *have* the 635 * desired day-of-the-month. If that happens, then 636 * move to the next month that does have enough days. 637 */ 638 tmon = ptime->tm.tm_mon; 639 tyear = ptime->tm.tm_year; 640 for (;;) { 641 if (tmon < 11) 642 tmon += 1; 643 else { 644 tmon = 0; 645 tyear += 1; 646 } 647 tdpm = days_pmonth(tmon, tyear); 648 if (tdpm >= ptime->tm.tm_mday) 649 break; 650 moredays += tdpm; 651 } 652 653 } else if (ptime->tmspec & TSPEC_DAYOFWEEK) { 654 moredays = 7; 655 } else if (ptime->tmspec & TSPEC_HOUROFDAY) { 656 moredays = 1; 657 } 658 659 if (moredays != 0) { 660 nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays; 661 nextmatch.tm = *(localtime(&nextmatch.tsecs)); 662 } 663 664 /* 665 * The new time will need to be adjusted if the setting of 666 * daylight-saving has changed between the two times. 667 */ 668 ptime_adjust4dst(&nextmatch, ptime); 669 670 /* Everything worked. Update the given time and return. */ 671 *ptime = nextmatch; 672 return (0); 673} 674 675int 676ptimeset_time(struct ptime_data *ptime, time_t secs) 677{ 678 679 if (ptime == NULL) 680 return (-1); 681 682 ptime->tsecs = secs; 683 ptime->tm = *(localtime(&ptime->tsecs)); 684 ptime->parseopts = 0; 685 /* ptime->tmspec = ? */ 686 return (0); 687} 688