12f8d7c56SGarance A Drosehn /*- 22f8d7c56SGarance A Drosehn * ------+---------+---------+---------+---------+---------+---------+---------* 32f8d7c56SGarance A Drosehn * Initial version of parse8601 was originally added to newsyslog.c in 42f8d7c56SGarance A Drosehn * FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>. 52f8d7c56SGarance A Drosehn * Initial version of parseDWM was originally added to newsyslog.c in 62f8d7c56SGarance A Drosehn * FreeBSD on Apr 4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>. 72f8d7c56SGarance A Drosehn * 82f8d7c56SGarance A Drosehn * Copyright (c) 2003 - Garance Alistair Drosehn <gad@FreeBSD.org>. 92f8d7c56SGarance A Drosehn * All rights reserved. 102f8d7c56SGarance A Drosehn * 112f8d7c56SGarance A Drosehn * Redistribution and use in source and binary forms, with or without 122f8d7c56SGarance A Drosehn * modification, are permitted provided that the following conditions 132f8d7c56SGarance A Drosehn * are met: 142f8d7c56SGarance A Drosehn * 1. Redistributions of source code must retain the above copyright 152f8d7c56SGarance A Drosehn * notice, this list of conditions and the following disclaimer. 162f8d7c56SGarance A Drosehn * 2. Redistributions in binary form must reproduce the above copyright 172f8d7c56SGarance A Drosehn * notice, this list of conditions and the following disclaimer in the 182f8d7c56SGarance A Drosehn * documentation and/or other materials provided with the distribution. 192f8d7c56SGarance A Drosehn * 202f8d7c56SGarance A Drosehn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 212f8d7c56SGarance A Drosehn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 222f8d7c56SGarance A Drosehn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 232f8d7c56SGarance A Drosehn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 242f8d7c56SGarance A Drosehn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 252f8d7c56SGarance A Drosehn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 262f8d7c56SGarance A Drosehn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 272f8d7c56SGarance A Drosehn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 282f8d7c56SGarance A Drosehn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 292f8d7c56SGarance A Drosehn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 302f8d7c56SGarance A Drosehn * SUCH DAMAGE. 312f8d7c56SGarance A Drosehn * 322f8d7c56SGarance A Drosehn * The views and conclusions contained in the software and documentation 332f8d7c56SGarance A Drosehn * are those of the authors and should not be interpreted as representing 342f8d7c56SGarance A Drosehn * official policies, either expressed or implied, of the FreeBSD Project. 352f8d7c56SGarance A Drosehn * 362f8d7c56SGarance A Drosehn * ------+---------+---------+---------+---------+---------+---------+---------* 376a1485e2SGarance A Drosehn * This is intended to be a set of general-purpose routines to process times. 386a1485e2SGarance A Drosehn * Right now it probably still has a number of assumptions in it, such that 396a1485e2SGarance A Drosehn * it works fine for newsyslog but might not work for other uses. 406a1485e2SGarance A Drosehn * ------+---------+---------+---------+---------+---------+---------+---------* 412f8d7c56SGarance A Drosehn */ 422f8d7c56SGarance A Drosehn 432f8d7c56SGarance A Drosehn #include <sys/cdefs.h> 442f8d7c56SGarance A Drosehn __FBSDID("$FreeBSD$"); 452f8d7c56SGarance A Drosehn 462f8d7c56SGarance A Drosehn #include <ctype.h> 472f8d7c56SGarance A Drosehn #include <limits.h> 482f8d7c56SGarance A Drosehn #include <stdio.h> 492f8d7c56SGarance A Drosehn #include <stdlib.h> 506a1485e2SGarance A Drosehn #include <string.h> 512f8d7c56SGarance A Drosehn #include <time.h> 522f8d7c56SGarance A Drosehn 532f8d7c56SGarance A Drosehn #include "extern.h" 542f8d7c56SGarance A Drosehn 556a1485e2SGarance A Drosehn #define SECS_PER_HOUR 3600 566a1485e2SGarance A Drosehn 576a1485e2SGarance A Drosehn /* 586a1485e2SGarance A Drosehn * Bit-values which indicate which components of time were specified 596a1485e2SGarance A Drosehn * by the string given to parse8601 or parseDWM. These are needed to 606a1485e2SGarance A Drosehn * calculate what time-in-the-future will match that string. 616a1485e2SGarance A Drosehn */ 626a1485e2SGarance A Drosehn #define TSPEC_YEAR 0x0001 636a1485e2SGarance A Drosehn #define TSPEC_MONTHOFYEAR 0x0002 646a1485e2SGarance A Drosehn #define TSPEC_LDAYOFMONTH 0x0004 656a1485e2SGarance A Drosehn #define TSPEC_DAYOFMONTH 0x0008 666a1485e2SGarance A Drosehn #define TSPEC_DAYOFWEEK 0x0010 676a1485e2SGarance A Drosehn #define TSPEC_HOUROFDAY 0x0020 686a1485e2SGarance A Drosehn 696a1485e2SGarance A Drosehn #define TNYET_ADJ4DST -10 /* DST has "not yet" been adjusted */ 706a1485e2SGarance A Drosehn 716a1485e2SGarance A Drosehn struct ptime_data { 726a1485e2SGarance A Drosehn time_t basesecs; /* Base point for relative times */ 736a1485e2SGarance A Drosehn time_t tsecs; /* Time in seconds */ 746a1485e2SGarance A Drosehn struct tm basetm; /* Base Time expanded into fields */ 756a1485e2SGarance A Drosehn struct tm tm; /* Time expanded into fields */ 766a1485e2SGarance A Drosehn int did_adj4dst; /* Track calls to ptime_adjust4dst */ 776a1485e2SGarance A Drosehn int parseopts; /* Options given for parsing */ 786a1485e2SGarance A Drosehn int tmspec; /* Indicates which time fields had 796a1485e2SGarance A Drosehn * been specified by the user */ 806a1485e2SGarance A Drosehn }; 816a1485e2SGarance A Drosehn 82526d55a4SGarance A Drosehn static int days_pmonth(int month, int year); 836a1485e2SGarance A Drosehn static int parse8601(struct ptime_data *ptime, const char *str); 846a1485e2SGarance A Drosehn static int parseDWM(struct ptime_data *ptime, const char *str); 85526d55a4SGarance A Drosehn 86526d55a4SGarance A Drosehn /* 87526d55a4SGarance A Drosehn * Simple routine to calculate the number of days in a given month. 88526d55a4SGarance A Drosehn */ 89526d55a4SGarance A Drosehn static int 90526d55a4SGarance A Drosehn days_pmonth(int month, int year) 91526d55a4SGarance A Drosehn { 92526d55a4SGarance A Drosehn static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 93526d55a4SGarance A Drosehn 30, 31, 30, 31}; 94526d55a4SGarance A Drosehn int ndays; 95526d55a4SGarance A Drosehn 96526d55a4SGarance A Drosehn ndays = mtab[month]; 97526d55a4SGarance A Drosehn 98526d55a4SGarance A Drosehn if (month == 1) { 99526d55a4SGarance A Drosehn /* 100526d55a4SGarance A Drosehn * We are usually called with a 'tm-year' value 101526d55a4SGarance A Drosehn * (ie, the value = the number of years past 1900). 102526d55a4SGarance A Drosehn */ 103526d55a4SGarance A Drosehn if (year < 1900) 104526d55a4SGarance A Drosehn year += 1900; 105526d55a4SGarance A Drosehn if (year % 4 == 0) { 106526d55a4SGarance A Drosehn /* 107526d55a4SGarance A Drosehn * This is a leap year, as long as it is not a 108526d55a4SGarance A Drosehn * multiple of 100, or if it is a multiple of 109526d55a4SGarance A Drosehn * both 100 and 400. 110526d55a4SGarance A Drosehn */ 111526d55a4SGarance A Drosehn if (year % 100 != 0) 112526d55a4SGarance A Drosehn ndays++; /* not multiple of 100 */ 113526d55a4SGarance A Drosehn else if (year % 400 == 0) 114526d55a4SGarance A Drosehn ndays++; /* is multiple of 100 and 400 */ 115526d55a4SGarance A Drosehn } 116526d55a4SGarance A Drosehn } 117526d55a4SGarance A Drosehn return (ndays); 118526d55a4SGarance A Drosehn } 119526d55a4SGarance A Drosehn 1202f8d7c56SGarance A Drosehn /*- 1212f8d7c56SGarance A Drosehn * Parse a limited subset of ISO 8601. The specific format is as follows: 1222f8d7c56SGarance A Drosehn * 1232f8d7c56SGarance A Drosehn * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter) 1242f8d7c56SGarance A Drosehn * 1252f8d7c56SGarance A Drosehn * We don't accept a timezone specification; missing fields (including timezone) 1262f8d7c56SGarance A Drosehn * are defaulted to the current date but time zero. 1272f8d7c56SGarance A Drosehn */ 1286a1485e2SGarance A Drosehn static int 1296a1485e2SGarance A Drosehn parse8601(struct ptime_data *ptime, const char *s) 1302f8d7c56SGarance A Drosehn { 1312f8d7c56SGarance A Drosehn char *t; 1322f8d7c56SGarance A Drosehn long l; 1336a1485e2SGarance A Drosehn struct tm tm; 1342f8d7c56SGarance A Drosehn 1352f8d7c56SGarance A Drosehn l = strtol(s, &t, 10); 1362f8d7c56SGarance A Drosehn if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T')) 1372f8d7c56SGarance A Drosehn return (-1); 1382f8d7c56SGarance A Drosehn 1392f8d7c56SGarance A Drosehn /* 1402f8d7c56SGarance A Drosehn * Now t points either to the end of the string (if no time was 1412f8d7c56SGarance A Drosehn * provided) or to the letter `T' which separates date and time in 1422f8d7c56SGarance A Drosehn * ISO 8601. The pointer arithmetic is the same for either case. 1432f8d7c56SGarance A Drosehn */ 1446a1485e2SGarance A Drosehn tm = ptime->tm; 1456a1485e2SGarance A Drosehn ptime->tmspec = TSPEC_HOUROFDAY; 1462f8d7c56SGarance A Drosehn switch (t - s) { 1472f8d7c56SGarance A Drosehn case 8: 1482f8d7c56SGarance A Drosehn tm.tm_year = ((l / 1000000) - 19) * 100; 1492f8d7c56SGarance A Drosehn l = l % 1000000; 1504c8f6471SMark Johnston /* FALLTHROUGH */ 1512f8d7c56SGarance A Drosehn case 6: 1526a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_YEAR; 1532f8d7c56SGarance A Drosehn tm.tm_year -= tm.tm_year % 100; 1542f8d7c56SGarance A Drosehn tm.tm_year += l / 10000; 1552f8d7c56SGarance A Drosehn l = l % 10000; 1564c8f6471SMark Johnston /* FALLTHROUGH */ 1572f8d7c56SGarance A Drosehn case 4: 1586a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_MONTHOFYEAR; 1592f8d7c56SGarance A Drosehn tm.tm_mon = (l / 100) - 1; 1602f8d7c56SGarance A Drosehn l = l % 100; 1614c8f6471SMark Johnston /* FALLTHROUGH */ 1622f8d7c56SGarance A Drosehn case 2: 1636a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_DAYOFMONTH; 1642f8d7c56SGarance A Drosehn tm.tm_mday = l; 1654c8f6471SMark Johnston /* FALLTHROUGH */ 1662f8d7c56SGarance A Drosehn case 0: 1672f8d7c56SGarance A Drosehn break; 1682f8d7c56SGarance A Drosehn default: 1692f8d7c56SGarance A Drosehn return (-1); 1702f8d7c56SGarance A Drosehn } 1712f8d7c56SGarance A Drosehn 1722f8d7c56SGarance A Drosehn /* sanity check */ 1732f8d7c56SGarance A Drosehn if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12 1742f8d7c56SGarance A Drosehn || tm.tm_mday < 1 || tm.tm_mday > 31) 1752f8d7c56SGarance A Drosehn return (-1); 1762f8d7c56SGarance A Drosehn 1772f8d7c56SGarance A Drosehn if (*t != '\0') { 1782f8d7c56SGarance A Drosehn s = ++t; 1792f8d7c56SGarance A Drosehn l = strtol(s, &t, 10); 1802f8d7c56SGarance A Drosehn if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t))) 1812f8d7c56SGarance A Drosehn return (-1); 1822f8d7c56SGarance A Drosehn 1832f8d7c56SGarance A Drosehn switch (t - s) { 1842f8d7c56SGarance A Drosehn case 6: 1852f8d7c56SGarance A Drosehn tm.tm_sec = l % 100; 1862f8d7c56SGarance A Drosehn l /= 100; 1874c8f6471SMark Johnston /* FALLTHROUGH */ 1882f8d7c56SGarance A Drosehn case 4: 1892f8d7c56SGarance A Drosehn tm.tm_min = l % 100; 1902f8d7c56SGarance A Drosehn l /= 100; 1914c8f6471SMark Johnston /* FALLTHROUGH */ 1922f8d7c56SGarance A Drosehn case 2: 1936a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_HOUROFDAY; 1942f8d7c56SGarance A Drosehn tm.tm_hour = l; 1954c8f6471SMark Johnston /* FALLTHROUGH */ 1962f8d7c56SGarance A Drosehn case 0: 1972f8d7c56SGarance A Drosehn break; 1982f8d7c56SGarance A Drosehn default: 1992f8d7c56SGarance A Drosehn return (-1); 2002f8d7c56SGarance A Drosehn } 2012f8d7c56SGarance A Drosehn 2022f8d7c56SGarance A Drosehn /* sanity check */ 2032f8d7c56SGarance A Drosehn if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0 2042f8d7c56SGarance A Drosehn || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23) 2052f8d7c56SGarance A Drosehn return (-1); 2062f8d7c56SGarance A Drosehn } 2072f8d7c56SGarance A Drosehn 2086a1485e2SGarance A Drosehn ptime->tm = tm; 2096a1485e2SGarance A Drosehn return (0); 2102f8d7c56SGarance A Drosehn } 2112f8d7c56SGarance A Drosehn 2122f8d7c56SGarance A Drosehn /*- 2132f8d7c56SGarance A Drosehn * Parse a cyclic time specification, the format is as follows: 2142f8d7c56SGarance A Drosehn * 2152f8d7c56SGarance A Drosehn * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]] 2162f8d7c56SGarance A Drosehn * 2172f8d7c56SGarance A Drosehn * to rotate a logfile cyclic at 2182f8d7c56SGarance A Drosehn * 2192f8d7c56SGarance A Drosehn * - every day (D) within a specific hour (hh) (hh = 0...23) 2202f8d7c56SGarance A Drosehn * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday) 2212f8d7c56SGarance A Drosehn * - once a month (M) at a specific day (d) (d = 1..31,l|L) 2222f8d7c56SGarance A Drosehn * 2232f8d7c56SGarance A Drosehn * We don't accept a timezone specification; missing fields 2242f8d7c56SGarance A Drosehn * are defaulted to the current date but time zero. 2252f8d7c56SGarance A Drosehn */ 2266a1485e2SGarance A Drosehn static int 2276a1485e2SGarance A Drosehn parseDWM(struct ptime_data *ptime, const char *s) 2282f8d7c56SGarance A Drosehn { 2296a1485e2SGarance A Drosehn int daysmon, Dseen, WMseen; 230c3033287SGarance A Drosehn const char *endval; 231c3033287SGarance A Drosehn char *tmp; 2322f8d7c56SGarance A Drosehn long l; 2336a1485e2SGarance A Drosehn struct tm tm; 2342f8d7c56SGarance A Drosehn 235526d55a4SGarance A Drosehn /* Save away the number of days in this month */ 2366a1485e2SGarance A Drosehn tm = ptime->tm; 237526d55a4SGarance A Drosehn daysmon = days_pmonth(tm.tm_mon, tm.tm_year); 2382f8d7c56SGarance A Drosehn 2396a1485e2SGarance A Drosehn WMseen = Dseen = 0; 2406a1485e2SGarance A Drosehn ptime->tmspec = TSPEC_HOUROFDAY; 2412f8d7c56SGarance A Drosehn for (;;) { 242c3033287SGarance A Drosehn endval = NULL; 2432f8d7c56SGarance A Drosehn switch (*s) { 2442f8d7c56SGarance A Drosehn case 'D': 2452f8d7c56SGarance A Drosehn if (Dseen) 2462f8d7c56SGarance A Drosehn return (-1); 2472f8d7c56SGarance A Drosehn Dseen++; 2486a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_HOUROFDAY; 2492f8d7c56SGarance A Drosehn s++; 250c3033287SGarance A Drosehn l = strtol(s, &tmp, 10); 2512f8d7c56SGarance A Drosehn if (l < 0 || l > 23) 2522f8d7c56SGarance A Drosehn return (-1); 253c3033287SGarance A Drosehn endval = tmp; 2542f8d7c56SGarance A Drosehn tm.tm_hour = l; 2552f8d7c56SGarance A Drosehn break; 2562f8d7c56SGarance A Drosehn 2572f8d7c56SGarance A Drosehn case 'W': 2582f8d7c56SGarance A Drosehn if (WMseen) 2592f8d7c56SGarance A Drosehn return (-1); 2602f8d7c56SGarance A Drosehn WMseen++; 2616a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_DAYOFWEEK; 2622f8d7c56SGarance A Drosehn s++; 263c3033287SGarance A Drosehn l = strtol(s, &tmp, 10); 2642f8d7c56SGarance A Drosehn if (l < 0 || l > 6) 2652f8d7c56SGarance A Drosehn return (-1); 266c3033287SGarance A Drosehn endval = tmp; 2672f8d7c56SGarance A Drosehn if (l != tm.tm_wday) { 2682f8d7c56SGarance A Drosehn int save; 2692f8d7c56SGarance A Drosehn 2702f8d7c56SGarance A Drosehn if (l < tm.tm_wday) { 2712f8d7c56SGarance A Drosehn save = 6 - tm.tm_wday; 2722f8d7c56SGarance A Drosehn save += (l + 1); 2732f8d7c56SGarance A Drosehn } else { 2742f8d7c56SGarance A Drosehn save = l - tm.tm_wday; 2752f8d7c56SGarance A Drosehn } 2762f8d7c56SGarance A Drosehn 2772f8d7c56SGarance A Drosehn tm.tm_mday += save; 2782f8d7c56SGarance A Drosehn 279526d55a4SGarance A Drosehn if (tm.tm_mday > daysmon) { 2802f8d7c56SGarance A Drosehn tm.tm_mon++; 281526d55a4SGarance A Drosehn tm.tm_mday = tm.tm_mday - daysmon; 282*b7b447fdSGarance A Drosehn if (tm.tm_mon >= 12) { 283*b7b447fdSGarance A Drosehn tm.tm_mon = 0; 284*b7b447fdSGarance A Drosehn tm.tm_year++; 285*b7b447fdSGarance A Drosehn } 2862f8d7c56SGarance A Drosehn } 2872f8d7c56SGarance A Drosehn } 2882f8d7c56SGarance A Drosehn break; 2892f8d7c56SGarance A Drosehn 2902f8d7c56SGarance A Drosehn case 'M': 2912f8d7c56SGarance A Drosehn if (WMseen) 2922f8d7c56SGarance A Drosehn return (-1); 2932f8d7c56SGarance A Drosehn WMseen++; 2946a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_DAYOFMONTH; 2952f8d7c56SGarance A Drosehn s++; 2962f8d7c56SGarance A Drosehn if (tolower(*s) == 'l') { 2976a1485e2SGarance A Drosehn /* User wants the last day of the month. */ 2986a1485e2SGarance A Drosehn ptime->tmspec |= TSPEC_LDAYOFMONTH; 299526d55a4SGarance A Drosehn tm.tm_mday = daysmon; 300c3033287SGarance A Drosehn endval = s + 1; 3012f8d7c56SGarance A Drosehn } else { 302c3033287SGarance A Drosehn l = strtol(s, &tmp, 10); 3032f8d7c56SGarance A Drosehn if (l < 1 || l > 31) 3042f8d7c56SGarance A Drosehn return (-1); 3052f8d7c56SGarance A Drosehn 306526d55a4SGarance A Drosehn if (l > daysmon) 3072f8d7c56SGarance A Drosehn return (-1); 308c3033287SGarance A Drosehn endval = tmp; 3092f8d7c56SGarance A Drosehn tm.tm_mday = l; 3102f8d7c56SGarance A Drosehn } 3112f8d7c56SGarance A Drosehn break; 3122f8d7c56SGarance A Drosehn 3132f8d7c56SGarance A Drosehn default: 3142f8d7c56SGarance A Drosehn return (-1); 3152f8d7c56SGarance A Drosehn break; 3162f8d7c56SGarance A Drosehn } 3172f8d7c56SGarance A Drosehn 318c3033287SGarance A Drosehn if (endval == NULL) 319c3033287SGarance A Drosehn return (-1); 320c3033287SGarance A Drosehn else if (*endval == '\0' || isspace(*endval)) 3212f8d7c56SGarance A Drosehn break; 3222f8d7c56SGarance A Drosehn else 323c3033287SGarance A Drosehn s = endval; 3242f8d7c56SGarance A Drosehn } 3252f8d7c56SGarance A Drosehn 3266a1485e2SGarance A Drosehn ptime->tm = tm; 3276a1485e2SGarance A Drosehn return (0); 3286a1485e2SGarance A Drosehn } 3296a1485e2SGarance A Drosehn 3302f8d7c56SGarance A Drosehn /* 3316a1485e2SGarance A Drosehn * Initialize a new ptime-related data area. 3322f8d7c56SGarance A Drosehn */ 3336a1485e2SGarance A Drosehn struct ptime_data * 3346a1485e2SGarance A Drosehn ptime_init(const struct ptime_data *optsrc) 3356a1485e2SGarance A Drosehn { 3366a1485e2SGarance A Drosehn struct ptime_data *newdata; 3376a1485e2SGarance A Drosehn 3386a1485e2SGarance A Drosehn newdata = malloc(sizeof(struct ptime_data)); 3396a1485e2SGarance A Drosehn if (optsrc != NULL) { 3406a1485e2SGarance A Drosehn memcpy(newdata, optsrc, sizeof(struct ptime_data)); 3416a1485e2SGarance A Drosehn } else { 3426a1485e2SGarance A Drosehn memset(newdata, '\0', sizeof(struct ptime_data)); 3436a1485e2SGarance A Drosehn newdata->did_adj4dst = TNYET_ADJ4DST; 3446a1485e2SGarance A Drosehn } 3456a1485e2SGarance A Drosehn 3466a1485e2SGarance A Drosehn return (newdata); 3476a1485e2SGarance A Drosehn } 3486a1485e2SGarance A Drosehn 3496a1485e2SGarance A Drosehn /* 3506a1485e2SGarance A Drosehn * Adjust a given time if that time is in a different timezone than 3516a1485e2SGarance A Drosehn * some other time. 3526a1485e2SGarance A Drosehn */ 3536a1485e2SGarance A Drosehn int 3546a1485e2SGarance A Drosehn ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc) 3556a1485e2SGarance A Drosehn { 3566a1485e2SGarance A Drosehn struct ptime_data adjtime; 3576a1485e2SGarance A Drosehn 3586a1485e2SGarance A Drosehn if (ptime == NULL) 3596a1485e2SGarance A Drosehn return (-1); 3606a1485e2SGarance A Drosehn 3616a1485e2SGarance A Drosehn /* 3626a1485e2SGarance A Drosehn * Changes are not made to the given time until after all 3636a1485e2SGarance A Drosehn * of the calculations have been successful. 3646a1485e2SGarance A Drosehn */ 3656a1485e2SGarance A Drosehn adjtime = *ptime; 3666a1485e2SGarance A Drosehn 3676a1485e2SGarance A Drosehn /* Check to see if this adjustment was already made */ 3686a1485e2SGarance A Drosehn if ((adjtime.did_adj4dst != TNYET_ADJ4DST) && 3696a1485e2SGarance A Drosehn (adjtime.did_adj4dst == dstsrc->tm.tm_isdst)) 3706a1485e2SGarance A Drosehn return (0); /* yes, so don't make it twice */ 3716a1485e2SGarance A Drosehn 3726a1485e2SGarance A Drosehn /* See if daylight-saving has changed between the two times. */ 3736a1485e2SGarance A Drosehn if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) { 3746a1485e2SGarance A Drosehn if (adjtime.tm.tm_isdst == 1) 3756a1485e2SGarance A Drosehn adjtime.tsecs -= SECS_PER_HOUR; 3766a1485e2SGarance A Drosehn else if (adjtime.tm.tm_isdst == 0) 3776a1485e2SGarance A Drosehn adjtime.tsecs += SECS_PER_HOUR; 3786a1485e2SGarance A Drosehn adjtime.tm = *(localtime(&adjtime.tsecs)); 3796a1485e2SGarance A Drosehn /* Remember that this adjustment has been made */ 3806a1485e2SGarance A Drosehn adjtime.did_adj4dst = dstsrc->tm.tm_isdst; 3816a1485e2SGarance A Drosehn /* 3826a1485e2SGarance A Drosehn * XXX - Should probably check to see if changing the 3836a1485e2SGarance A Drosehn * hour also changed the value of is_dst. What 3846a1485e2SGarance A Drosehn * should we do in that case? 3856a1485e2SGarance A Drosehn */ 3866a1485e2SGarance A Drosehn } 3876a1485e2SGarance A Drosehn 3886a1485e2SGarance A Drosehn *ptime = adjtime; 3896a1485e2SGarance A Drosehn return (0); 3906a1485e2SGarance A Drosehn } 3916a1485e2SGarance A Drosehn 3926a1485e2SGarance A Drosehn int 3936a1485e2SGarance A Drosehn ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime, 3946a1485e2SGarance A Drosehn const char *str) 3956a1485e2SGarance A Drosehn { 3966a1485e2SGarance A Drosehn int dpm, pres; 3976a1485e2SGarance A Drosehn struct tm temp_tm; 3986a1485e2SGarance A Drosehn 3996a1485e2SGarance A Drosehn ptime->parseopts = parseopts; 4006a1485e2SGarance A Drosehn ptime->basesecs = basetime; 4016a1485e2SGarance A Drosehn ptime->basetm = *(localtime(&ptime->basesecs)); 4026a1485e2SGarance A Drosehn ptime->tm = ptime->basetm; 4036a1485e2SGarance A Drosehn ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0; 4046a1485e2SGarance A Drosehn 4056a1485e2SGarance A Drosehn /* 4066a1485e2SGarance A Drosehn * Call a routine which sets ptime.tm and ptime.tspecs based 4076a1485e2SGarance A Drosehn * on the given string and parsing-options. Note that the 4086a1485e2SGarance A Drosehn * routine should not call mktime to set ptime.tsecs. 4096a1485e2SGarance A Drosehn */ 4106a1485e2SGarance A Drosehn if (parseopts & PTM_PARSE_DWM) 4116a1485e2SGarance A Drosehn pres = parseDWM(ptime, str); 4126a1485e2SGarance A Drosehn else 4136a1485e2SGarance A Drosehn pres = parse8601(ptime, str); 4146a1485e2SGarance A Drosehn if (pres < 0) { 4156a1485e2SGarance A Drosehn ptime->tsecs = (time_t)pres; 4166a1485e2SGarance A Drosehn return (pres); 4176a1485e2SGarance A Drosehn } 4186a1485e2SGarance A Drosehn 4196a1485e2SGarance A Drosehn /* 4206a1485e2SGarance A Drosehn * Before calling mktime, check to see if we ended up with a 4216a1485e2SGarance A Drosehn * "day-of-month" that does not exist in the selected month. 4226a1485e2SGarance A Drosehn * If we did call mktime with that info, then mktime will 4236a1485e2SGarance A Drosehn * make it look like the user specifically requested a day 4246a1485e2SGarance A Drosehn * in the following month (eg: Feb 31 turns into Mar 3rd). 4256a1485e2SGarance A Drosehn */ 4266a1485e2SGarance A Drosehn dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); 4276a1485e2SGarance A Drosehn if ((parseopts & PTM_PARSE_MATCHDOM) && 4286a1485e2SGarance A Drosehn (ptime->tmspec & TSPEC_DAYOFMONTH) && 4296a1485e2SGarance A Drosehn (ptime->tm.tm_mday> dpm)) { 4306a1485e2SGarance A Drosehn /* 4316a1485e2SGarance A Drosehn * ptime_nxtime() will want a ptime->tsecs value, 4326a1485e2SGarance A Drosehn * but we need to avoid mktime resetting all the 4336a1485e2SGarance A Drosehn * ptime->tm values. 4346a1485e2SGarance A Drosehn */ 4356a1485e2SGarance A Drosehn if (verbose && dbg_at_times > 1) 4366a1485e2SGarance A Drosehn fprintf(stderr, 4376a1485e2SGarance A Drosehn "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)", 4386a1485e2SGarance A Drosehn ptime->tm.tm_year, ptime->tm.tm_mon, 4396a1485e2SGarance A Drosehn ptime->tm.tm_mday, ptime->tm.tm_hour, 4406a1485e2SGarance A Drosehn ptime->tm.tm_min, dpm); 4416a1485e2SGarance A Drosehn temp_tm = ptime->tm; 4426a1485e2SGarance A Drosehn ptime->tsecs = mktime(&temp_tm); 4436a1485e2SGarance A Drosehn if (ptime->tsecs > (time_t)-1) 4446a1485e2SGarance A Drosehn ptimeset_nxtime(ptime); 4456a1485e2SGarance A Drosehn if (verbose && dbg_at_times > 1) 4466a1485e2SGarance A Drosehn fprintf(stderr, 4476a1485e2SGarance A Drosehn " to: %4d/%02d/%02d %02d:%02d\n", 4486a1485e2SGarance A Drosehn ptime->tm.tm_year, ptime->tm.tm_mon, 4496a1485e2SGarance A Drosehn ptime->tm.tm_mday, ptime->tm.tm_hour, 4506a1485e2SGarance A Drosehn ptime->tm.tm_min); 4516a1485e2SGarance A Drosehn } 4526a1485e2SGarance A Drosehn 4536a1485e2SGarance A Drosehn /* 4546a1485e2SGarance A Drosehn * Convert the ptime.tm into standard time_t seconds. Check 4556a1485e2SGarance A Drosehn * for invalid times, which includes things like the hour lost 4566a1485e2SGarance A Drosehn * when switching from "standard time" to "daylight saving". 4576a1485e2SGarance A Drosehn */ 4586a1485e2SGarance A Drosehn ptime->tsecs = mktime(&ptime->tm); 4596a1485e2SGarance A Drosehn if (ptime->tsecs == (time_t)-1) { 4606a1485e2SGarance A Drosehn ptime->tsecs = (time_t)-2; 4616a1485e2SGarance A Drosehn return (-2); 4626a1485e2SGarance A Drosehn } 4636a1485e2SGarance A Drosehn 4646a1485e2SGarance A Drosehn return (0); 4656a1485e2SGarance A Drosehn } 4666a1485e2SGarance A Drosehn 4676a1485e2SGarance A Drosehn int 4686a1485e2SGarance A Drosehn ptime_free(struct ptime_data *ptime) 4696a1485e2SGarance A Drosehn { 4706a1485e2SGarance A Drosehn 4716a1485e2SGarance A Drosehn if (ptime == NULL) 4726a1485e2SGarance A Drosehn return (-1); 4736a1485e2SGarance A Drosehn 4746a1485e2SGarance A Drosehn free(ptime); 4756a1485e2SGarance A Drosehn return (0); 4766a1485e2SGarance A Drosehn } 4776a1485e2SGarance A Drosehn 4786a1485e2SGarance A Drosehn /* 4796a1485e2SGarance A Drosehn * Some trivial routines so ptime_data can remain a completely 4806a1485e2SGarance A Drosehn * opaque type. 4816a1485e2SGarance A Drosehn */ 4826a1485e2SGarance A Drosehn const char * 4836a1485e2SGarance A Drosehn ptimeget_ctime(const struct ptime_data *ptime) 4846a1485e2SGarance A Drosehn { 4856a1485e2SGarance A Drosehn 4866a1485e2SGarance A Drosehn if (ptime == NULL) 4876a1485e2SGarance A Drosehn return ("Null time in ptimeget_ctime()\n"); 4886a1485e2SGarance A Drosehn 4896a1485e2SGarance A Drosehn return (ctime(&ptime->tsecs)); 4906a1485e2SGarance A Drosehn } 4916a1485e2SGarance A Drosehn 492b326fec4SDavid Bright /* 493b326fec4SDavid Bright * Generate a time of day string in an RFC5424 compatible format. Return a 494b326fec4SDavid Bright * pointer to the buffer with the timestamp string or NULL if an error. If the 495b326fec4SDavid Bright * time is not supplied, cannot be converted to local time, or the resulting 496b326fec4SDavid Bright * string would overflow the buffer, the returned string will be the RFC5424 497b326fec4SDavid Bright * NILVALUE. 498b326fec4SDavid Bright */ 499b326fec4SDavid Bright char * 500b326fec4SDavid Bright ptimeget_ctime_rfc5424(const struct ptime_data *ptime, 501b326fec4SDavid Bright char *timebuf, size_t bufsize) 502b326fec4SDavid Bright { 503b326fec4SDavid Bright static const char NILVALUE[] = {"-"}; /* RFC5424 specified NILVALUE */ 504b326fec4SDavid Bright int chars; 505b326fec4SDavid Bright struct tm tm; 506b326fec4SDavid Bright int tz_hours; 507b326fec4SDavid Bright int tz_mins; 508b326fec4SDavid Bright long tz_offset; 509b326fec4SDavid Bright char tz_sign; 510b326fec4SDavid Bright 511b326fec4SDavid Bright if (timebuf == NULL) { 512b326fec4SDavid Bright return (NULL); 513b326fec4SDavid Bright } 514b326fec4SDavid Bright 515b326fec4SDavid Bright if (bufsize < sizeof(NILVALUE)) { 516b326fec4SDavid Bright return (NULL); 517b326fec4SDavid Bright } 518b326fec4SDavid Bright 519b326fec4SDavid Bright /* 520b326fec4SDavid Bright * Convert to localtime. RFC5424 mandates the use of the NILVALUE if 521b326fec4SDavid Bright * the time cannot be obtained, so use that if there is an error in the 522b326fec4SDavid Bright * conversion. 523b326fec4SDavid Bright */ 524b326fec4SDavid Bright if (ptime == NULL || localtime_r(&(ptime->tsecs), &tm) == NULL) { 525b326fec4SDavid Bright strlcpy(timebuf, NILVALUE, bufsize); 526b326fec4SDavid Bright return (timebuf); 527b326fec4SDavid Bright } 528b326fec4SDavid Bright 529b326fec4SDavid Bright /* 530b326fec4SDavid Bright * Convert the time to a string in RFC5424 format. The conversion 531b326fec4SDavid Bright * cannot be done with strftime() because it cannot produce the correct 532b326fec4SDavid Bright * timezone offset format. 533b326fec4SDavid Bright */ 534b326fec4SDavid Bright if (tm.tm_gmtoff < 0) { 535b326fec4SDavid Bright tz_sign = '-'; 536b326fec4SDavid Bright tz_offset = -tm.tm_gmtoff; 537b326fec4SDavid Bright } else { 538b326fec4SDavid Bright tz_sign = '+'; 539b326fec4SDavid Bright tz_offset = tm.tm_gmtoff; 540b326fec4SDavid Bright } 541b326fec4SDavid Bright 542b326fec4SDavid Bright tz_hours = tz_offset / 3600; 543b326fec4SDavid Bright tz_mins = (tz_offset % 3600) / 60; 544b326fec4SDavid Bright 545b326fec4SDavid Bright chars = snprintf(timebuf, bufsize, 546b326fec4SDavid Bright "%04d-%02d-%02d" /* date */ 547b326fec4SDavid Bright "T%02d:%02d:%02d" /* time */ 548b326fec4SDavid Bright "%c%02d:%02d", /* time zone offset */ 549b326fec4SDavid Bright tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 550b326fec4SDavid Bright tm.tm_hour, tm.tm_min, tm.tm_sec, 551b326fec4SDavid Bright tz_sign, tz_hours, tz_mins); 552b326fec4SDavid Bright 553b326fec4SDavid Bright /* If the timestamp is too big for timebuf, return the NILVALUE. */ 554b326fec4SDavid Bright if (chars >= (int)bufsize) { 555b326fec4SDavid Bright strlcpy(timebuf, NILVALUE, bufsize); 556b326fec4SDavid Bright } 557b326fec4SDavid Bright 558b326fec4SDavid Bright return (timebuf); 559b326fec4SDavid Bright } 560b326fec4SDavid Bright 5616a1485e2SGarance A Drosehn double 5626a1485e2SGarance A Drosehn ptimeget_diff(const struct ptime_data *minuend, const struct 5636a1485e2SGarance A Drosehn ptime_data *subtrahend) 5646a1485e2SGarance A Drosehn { 5656a1485e2SGarance A Drosehn 5666a1485e2SGarance A Drosehn /* Just like difftime(), we have no good error-return */ 5676a1485e2SGarance A Drosehn if (minuend == NULL || subtrahend == NULL) 5686a1485e2SGarance A Drosehn return (0.0); 5696a1485e2SGarance A Drosehn 5706a1485e2SGarance A Drosehn return (difftime(minuend->tsecs, subtrahend->tsecs)); 5716a1485e2SGarance A Drosehn } 5726a1485e2SGarance A Drosehn 5736a1485e2SGarance A Drosehn time_t 5746a1485e2SGarance A Drosehn ptimeget_secs(const struct ptime_data *ptime) 5756a1485e2SGarance A Drosehn { 5766a1485e2SGarance A Drosehn 5776a1485e2SGarance A Drosehn if (ptime == NULL) 5786a1485e2SGarance A Drosehn return (-1); 5796a1485e2SGarance A Drosehn 5806a1485e2SGarance A Drosehn return (ptime->tsecs); 5816a1485e2SGarance A Drosehn } 5826a1485e2SGarance A Drosehn 5836a1485e2SGarance A Drosehn /* 5846a1485e2SGarance A Drosehn * Generate an approximate timestamp for the next event, based on 5856a1485e2SGarance A Drosehn * what parts of time were specified by the original parameter to 5866a1485e2SGarance A Drosehn * ptime_relparse(). The result may be -1 if there is no obvious 5876a1485e2SGarance A Drosehn * "next time" which will work. 5886a1485e2SGarance A Drosehn */ 5896a1485e2SGarance A Drosehn int 5906a1485e2SGarance A Drosehn ptimeset_nxtime(struct ptime_data *ptime) 5916a1485e2SGarance A Drosehn { 5926a1485e2SGarance A Drosehn int moredays, tdpm, tmon, tyear; 5936a1485e2SGarance A Drosehn struct ptime_data nextmatch; 5946a1485e2SGarance A Drosehn 5956a1485e2SGarance A Drosehn if (ptime == NULL) 5966a1485e2SGarance A Drosehn return (-1); 5976a1485e2SGarance A Drosehn 5986a1485e2SGarance A Drosehn /* 5996a1485e2SGarance A Drosehn * Changes are not made to the given time until after all 6006a1485e2SGarance A Drosehn * of the calculations have been successful. 6016a1485e2SGarance A Drosehn */ 6026a1485e2SGarance A Drosehn nextmatch = *ptime; 6036a1485e2SGarance A Drosehn /* 6046a1485e2SGarance A Drosehn * If the user specified a year and we're already past that 6056a1485e2SGarance A Drosehn * time, then there will never be another one! 6066a1485e2SGarance A Drosehn */ 6076a1485e2SGarance A Drosehn if (ptime->tmspec & TSPEC_YEAR) 6086a1485e2SGarance A Drosehn return (-1); 6096a1485e2SGarance A Drosehn 6106a1485e2SGarance A Drosehn /* 6116a1485e2SGarance A Drosehn * The caller gave us a time in the past. Calculate how much 6126a1485e2SGarance A Drosehn * time is needed to go from that valid rotate time to the 6136a1485e2SGarance A Drosehn * next valid rotate time. We only need to get to the nearest 6146a1485e2SGarance A Drosehn * hour, because newsyslog is only run once per hour. 6156a1485e2SGarance A Drosehn */ 6166a1485e2SGarance A Drosehn moredays = 0; 6176a1485e2SGarance A Drosehn if (ptime->tmspec & TSPEC_MONTHOFYEAR) { 6186a1485e2SGarance A Drosehn /* Special case: Feb 29th does not happen every year. */ 6196a1485e2SGarance A Drosehn if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) { 6206a1485e2SGarance A Drosehn nextmatch.tm.tm_year += 4; 6216a1485e2SGarance A Drosehn if (days_pmonth(1, nextmatch.tm.tm_year) < 29) 6226a1485e2SGarance A Drosehn nextmatch.tm.tm_year += 4; 6236a1485e2SGarance A Drosehn } else { 6246a1485e2SGarance A Drosehn nextmatch.tm.tm_year += 1; 6256a1485e2SGarance A Drosehn } 6266a1485e2SGarance A Drosehn nextmatch.tm.tm_isdst = -1; 6276a1485e2SGarance A Drosehn nextmatch.tsecs = mktime(&nextmatch.tm); 6286a1485e2SGarance A Drosehn 6296a1485e2SGarance A Drosehn } else if (ptime->tmspec & TSPEC_LDAYOFMONTH) { 6306a1485e2SGarance A Drosehn /* 6316a1485e2SGarance A Drosehn * Need to get to the last day of next month. Origtm is 6326a1485e2SGarance A Drosehn * already at the last day of this month, so just add to 6336a1485e2SGarance A Drosehn * it number of days in the next month. 6346a1485e2SGarance A Drosehn */ 6356a1485e2SGarance A Drosehn if (ptime->tm.tm_mon < 11) 6366a1485e2SGarance A Drosehn moredays = days_pmonth(ptime->tm.tm_mon + 1, 6376a1485e2SGarance A Drosehn ptime->tm.tm_year); 6386a1485e2SGarance A Drosehn else 6396a1485e2SGarance A Drosehn moredays = days_pmonth(0, ptime->tm.tm_year + 1); 6406a1485e2SGarance A Drosehn 6416a1485e2SGarance A Drosehn } else if (ptime->tmspec & TSPEC_DAYOFMONTH) { 6426a1485e2SGarance A Drosehn /* Jump to the same day in the next month */ 6436a1485e2SGarance A Drosehn moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); 6446a1485e2SGarance A Drosehn /* 6456a1485e2SGarance A Drosehn * In some cases, the next month may not *have* the 6466a1485e2SGarance A Drosehn * desired day-of-the-month. If that happens, then 6476a1485e2SGarance A Drosehn * move to the next month that does have enough days. 6486a1485e2SGarance A Drosehn */ 6496a1485e2SGarance A Drosehn tmon = ptime->tm.tm_mon; 6506a1485e2SGarance A Drosehn tyear = ptime->tm.tm_year; 6516a1485e2SGarance A Drosehn for (;;) { 6526a1485e2SGarance A Drosehn if (tmon < 11) 6536a1485e2SGarance A Drosehn tmon += 1; 6546a1485e2SGarance A Drosehn else { 6556a1485e2SGarance A Drosehn tmon = 0; 6566a1485e2SGarance A Drosehn tyear += 1; 6576a1485e2SGarance A Drosehn } 6586a1485e2SGarance A Drosehn tdpm = days_pmonth(tmon, tyear); 6596a1485e2SGarance A Drosehn if (tdpm >= ptime->tm.tm_mday) 6606a1485e2SGarance A Drosehn break; 6616a1485e2SGarance A Drosehn moredays += tdpm; 6626a1485e2SGarance A Drosehn } 6636a1485e2SGarance A Drosehn 6646a1485e2SGarance A Drosehn } else if (ptime->tmspec & TSPEC_DAYOFWEEK) { 6656a1485e2SGarance A Drosehn moredays = 7; 6666a1485e2SGarance A Drosehn } else if (ptime->tmspec & TSPEC_HOUROFDAY) { 6676a1485e2SGarance A Drosehn moredays = 1; 6686a1485e2SGarance A Drosehn } 6696a1485e2SGarance A Drosehn 6706a1485e2SGarance A Drosehn if (moredays != 0) { 6716a1485e2SGarance A Drosehn nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays; 6726a1485e2SGarance A Drosehn nextmatch.tm = *(localtime(&nextmatch.tsecs)); 6736a1485e2SGarance A Drosehn } 6746a1485e2SGarance A Drosehn 6756a1485e2SGarance A Drosehn /* 6766a1485e2SGarance A Drosehn * The new time will need to be adjusted if the setting of 6776a1485e2SGarance A Drosehn * daylight-saving has changed between the two times. 6786a1485e2SGarance A Drosehn */ 6796a1485e2SGarance A Drosehn ptime_adjust4dst(&nextmatch, ptime); 6806a1485e2SGarance A Drosehn 6816a1485e2SGarance A Drosehn /* Everything worked. Update the given time and return. */ 6826a1485e2SGarance A Drosehn *ptime = nextmatch; 6836a1485e2SGarance A Drosehn return (0); 6846a1485e2SGarance A Drosehn } 6856a1485e2SGarance A Drosehn 6866a1485e2SGarance A Drosehn int 6876a1485e2SGarance A Drosehn ptimeset_time(struct ptime_data *ptime, time_t secs) 6886a1485e2SGarance A Drosehn { 6896a1485e2SGarance A Drosehn 6906a1485e2SGarance A Drosehn if (ptime == NULL) 6916a1485e2SGarance A Drosehn return (-1); 6926a1485e2SGarance A Drosehn 6936a1485e2SGarance A Drosehn ptime->tsecs = secs; 6946a1485e2SGarance A Drosehn ptime->tm = *(localtime(&ptime->tsecs)); 6956a1485e2SGarance A Drosehn ptime->parseopts = 0; 6966a1485e2SGarance A Drosehn /* ptime->tmspec = ? */ 6976a1485e2SGarance A Drosehn return (0); 6982f8d7c56SGarance A Drosehn } 699