diff options
author | gad <gad@FreeBSD.org> | 2003-09-23 00:00:26 +0000 |
---|---|---|
committer | gad <gad@FreeBSD.org> | 2003-09-23 00:00:26 +0000 |
commit | 8dafe430a02604e433903c786d2161b805228379 (patch) | |
tree | 5cbce648b655870350dca3b06cc88ab5d10345f8 /usr.sbin/newsyslog | |
parent | f2180e5cf94c12cbf30fcfb34bd27d5818dd5419 (diff) | |
download | FreeBSD-src-8dafe430a02604e433903c786d2161b805228379.zip FreeBSD-src-8dafe430a02604e433903c786d2161b805228379.tar.gz |
Restructure the time processing routines, mainly to fix up the
"will trim at" message printed when the user requests '-v'. The
previous code would often print the wrong time, such as:
On Sept 22, run: newsyslog -nv /var/log/wtmp
And see: will trim at Mon Sep 1 05:00:00 2003
correct msg: will trim at Wed Oct 1 05:00:00 2003
MFC after: 20 days
Diffstat (limited to 'usr.sbin/newsyslog')
-rw-r--r-- | usr.sbin/newsyslog/extern.h | 30 | ||||
-rw-r--r-- | usr.sbin/newsyslog/newsyslog.c | 137 | ||||
-rw-r--r-- | usr.sbin/newsyslog/ptimes.c | 398 |
3 files changed, 480 insertions, 85 deletions
diff --git a/usr.sbin/newsyslog/extern.h b/usr.sbin/newsyslog/extern.h index 05ef92c..c350226 100644 --- a/usr.sbin/newsyslog/extern.h +++ b/usr.sbin/newsyslog/extern.h @@ -36,13 +36,33 @@ #include <sys/cdefs.h> #include <time.h> +#define PTM_PARSE_ISO8601 0x0001 /* Parse ISO-standard format */ +#define PTM_PARSE_DWM 0x0002 /* Parse Day-Week-Month format */ +#define PTM_PARSE_MATCHDOM 0x0004 /* If the user specifies a day-of-month, + * then the result should be a month + * which actually has that day. Eg: + * the user requests "day 31" when + * the present month is February. */ + +struct ptime_data; + +/* Some global variables from newsyslog.c which might be of interest */ +extern int dbg_at_times; /* cmdline debugging option */ extern int noaction; /* command-line option */ extern int verbose; /* command-line option */ - -extern time_t dbg_timenow; -extern time_t timenow; +extern struct ptime_data *dbg_timenow; __BEGIN_DECLS -time_t parse8601(const char *_srcstr, time_t *_next_time); -time_t parseDWM(char *_srcstr, time_t *_next_time); +struct ptime_data *ptime_init(const struct ptime_data *_optsrc); +int ptime_adjust4dst(struct ptime_data *_ptime, const struct + ptime_data *_dstsrc); +int ptime_free(struct ptime_data *_ptime); +int ptime_relparse(struct ptime_data *_ptime, int _parseopts, + time_t _basetime, const char *_str); +const char *ptimeget_ctime(const struct ptime_data *_ptime); +double ptimeget_diff(const struct ptime_data *_minuend, + const struct ptime_data *_subtrahend); +time_t ptimeget_secs(const struct ptime_data *_ptime); +int ptimeset_nxtime(struct ptime_data *_ptime); +int ptimeset_time(struct ptime_data *_ptime, time_t _secs); __END_DECLS diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c index a9d1fd2..39efdb8 100644 --- a/usr.sbin/newsyslog/newsyslog.c +++ b/usr.sbin/newsyslog/newsyslog.c @@ -90,7 +90,7 @@ struct conf_entry { int numlogs; /* Number of logs to keep */ int size; /* Size cutoff to trigger trimming the log */ int hours; /* Hours between log trimming */ - time_t trim_at; /* Specific time to do trimming */ + struct ptime_data *trim_at; /* Specific time to do trimming */ int permissions; /* File permissions on the log */ int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */ int sig; /* Signal to send */ @@ -100,6 +100,8 @@ struct conf_entry { #define DEFAULT_MARKER "<default>" +int dbg_at_times; /* -D Show details of 'trim_at' code */ + int archtodir = 0; /* Archive old logfiles to other directory */ int createlogs; /* Create (non-GLOB) logfiles which do not */ /* already exist. 1=='for entries with */ @@ -116,11 +118,13 @@ int rotatereq = 0; /* -R = Always rotate the file(s) as given */ char *requestor; /* The name given on a -R request */ char *archdirname; /* Directory path to old logfiles archive */ const char *conf; /* Configuration file to use */ -time_t dbg_timenow; /* A "timenow" value set via -D option */ -time_t timenow; + +struct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */ +struct ptime_data *timenow; /* The time to use for checking at-fields */ char hostname[MAXHOSTNAMELEN]; /* hostname */ -char daytime[16]; /* timenow in human readable form */ +char daytime[16]; /* The current time in human readable form, + * used for rotation-tracking messages. */ static struct conf_entry *get_worklist(char **files); static void parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p, @@ -213,7 +217,9 @@ init_entry(const char *fname, struct conf_entry *src_entry) tempwork->numlogs = src_entry->numlogs; tempwork->size = src_entry->size; tempwork->hours = src_entry->hours; - tempwork->trim_at = src_entry->trim_at; + tempwork->trim_at = NULL; + if (src_entry->trim_at != NULL) + tempwork->trim_at = ptime_init(src_entry->trim_at); tempwork->permissions = src_entry->permissions; tempwork->flags = src_entry->flags; tempwork->sig = src_entry->sig; @@ -229,7 +235,7 @@ init_entry(const char *fname, struct conf_entry *src_entry) tempwork->numlogs = 1; tempwork->size = -1; tempwork->hours = -1; - tempwork->trim_at = (time_t)0; + tempwork->trim_at = NULL; tempwork->permissions = 0; tempwork->flags = 0; tempwork->sig = SIGHUP; @@ -264,6 +270,11 @@ free_entry(struct conf_entry *ent) ent->r_reason = NULL; } + if (ent->trim_at != NULL) { + ptime_free(ent->trim_at); + ent->trim_at = NULL; + } + free(ent); } @@ -290,6 +301,7 @@ do_entry(struct conf_entry * ent) { #define REASON_MAX 80 int size, modtime; + double diffsecs; char temp_reason[REASON_MAX]; if (verbose) { @@ -330,11 +342,40 @@ do_entry(struct conf_entry * ent) } } else { if (ent->flags & CE_TRIMAT && !force && !rotatereq) { - if (timenow < ent->trim_at - || difftime(timenow, ent->trim_at) >= 60 * 60) { - if (verbose) + diffsecs = ptimeget_diff(timenow, ent->trim_at); + if (diffsecs < 0.0) { + /* trim_at is some time in the future. */ + if (verbose) { + ptime_adjust4dst(ent->trim_at, + timenow); printf("--> will trim at %s", - ctime(&ent->trim_at)); + ptimeget_ctime(ent->trim_at)); + } + return; + } else if (diffsecs >= 3600.0) { + /* + * trim_at is more than an hour in the past, + * so find the next valid trim_at time, and + * tell the user what that will be. + */ + if (verbose && dbg_at_times) + printf("\n\t--> prev trim at %s\t", + ptimeget_ctime(ent->trim_at)); + if (verbose) { + ptimeset_nxtime(ent->trim_at); + printf("--> will trim at %s", + ptimeget_ctime(ent->trim_at)); + } + return; + } else if (verbose && noaction && dbg_at_times) { + /* + * If we are just debugging at-times, then + * a detailed message is helpful. Also + * skip "doing" any commands, since they + * would all be turned off by no-action. + */ + printf("\n\t--> timematch at %s", + ptimeget_ctime(ent->trim_at)); return; } else if (verbose && ent->hours <= 0) { printf("--> time is up\n"); @@ -496,10 +537,10 @@ parse_args(int argc, char **argv) { int ch; char *p; - char debugtime[32]; - timenow = time(NULL); - (void)strncpy(daytime, ctime(&timenow) + 4, 15); + timenow = ptime_init(NULL); + ptimeset_time(timenow, time(NULL)); + (void)strncpy(daytime, ptimeget_ctime(timenow) + 4, 15); daytime[15] = '\0'; /* Let's get our hostname */ @@ -578,37 +619,49 @@ parse_args(int argc, char **argv) * logfile is rotated, and if a file *is* rotated, * then it will still rotated at the "real now" time. */ + ptime_free(timenow); timenow = dbg_timenow; - strlcpy(debugtime, ctime(&timenow), sizeof(debugtime)); fprintf(stderr, "Debug: Running as if TimeNow is %s", - debugtime); + ptimeget_ctime(dbg_timenow)); } } +/* + * These debugging options are mainly meant for developer use, such + * as writing regression-tests. They would not be needed by users + * during normal operation of newsyslog... + */ static int parse_doption(const char *doption) { const char TN[] = "TN="; + int res; if (strncmp(doption, TN, sizeof(TN) - 1) == 0) { /* - * The "TimeNow" debugging option. This probably will - * be off by an hour when crossing a timezone change. + * The "TimeNow" debugging option. This might be off + * by an hour when crossing a timezone change. */ - dbg_timenow = parse8601(doption + sizeof(TN) - 1, NULL); - if (dbg_timenow == (time_t)-1) { - warnx("Malformed time given on -D %s", doption); - return (0); /* failure */ - } - if (dbg_timenow == (time_t)-2) { + dbg_timenow = ptime_init(NULL); + res = ptime_relparse(dbg_timenow, PTM_PARSE_ISO8601, + time(NULL), doption + sizeof(TN) - 1); + if (res == -2) { warnx("Non-existent time specified on -D %s", doption); return (0); /* failure */ + } else if (res < 0) { + warnx("Malformed time given on -D %s", doption); + return (0); /* failure */ } return (1); /* successfully parsed */ } + if (strcmp(doption, "ats") == 0) { + dbg_at_times++; + return (1); /* successfully parsed */ + } + warnx("Unknown -D (debug) option: %s", doption); return (0); /* failure */ } @@ -889,7 +942,7 @@ parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p, struct conf_entry *lastglob, *lastwork, *working; struct passwd *pwd; struct group *grp; - int eol, special; + int eol, ptm_opts, res, special; /* * XXX - for now, assume that only one config file will be read, @@ -1032,24 +1085,26 @@ parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p, else working->hours = ul; - if (*ep != '\0' && *ep != '@' && *ep != '*' && - *ep != '$') + if (*ep == '\0' || strcmp(ep, "*") == 0) + goto no_trimat; + if (*ep != '@' && *ep != '$') errx(1, "malformed interval/at:\n%s", errline); - if (*ep == '@') { - working->trim_at = parse8601(ep + 1, NULL); - working->flags |= CE_TRIMAT; - } else if (*ep == '$') { - working->trim_at = parseDWM(ep + 1, NULL); - working->flags |= CE_TRIMAT; - } - if (working->flags & CE_TRIMAT) { - if (working->trim_at == (time_t)-1) - errx(1, "malformed at:\n%s", errline); - if (working->trim_at == (time_t)-2) - errx(1, "nonexistent time:\n%s", - errline); - } + + working->flags |= CE_TRIMAT; + working->trim_at = ptime_init(NULL); + ptm_opts = PTM_PARSE_ISO8601; + if (*ep == '$') + ptm_opts = PTM_PARSE_DWM; + ptm_opts |= PTM_PARSE_MATCHDOM; + res = ptime_relparse(working->trim_at, ptm_opts, + ptimeget_secs(timenow), ep + 1); + if (res == -2) + errx(1, "nonexistent time for 'at' value:\n%s", + errline); + else if (res < 0) + errx(1, "malformed 'at' value:\n%s", errline); } +no_trimat: if (eol) q = NULL; @@ -1575,7 +1630,7 @@ age_old_log(char *file) return (-1); } } - return ((int)(timenow - sb.st_mtime + 1800) / 3600); + return ((int)(ptimeget_secs(timenow) - sb.st_mtime + 1800) / 3600); } /* Skip Over Blanks */ diff --git a/usr.sbin/newsyslog/ptimes.c b/usr.sbin/newsyslog/ptimes.c index 46fe1c7..4b63d6b 100644 --- a/usr.sbin/newsyslog/ptimes.c +++ b/usr.sbin/newsyslog/ptimes.c @@ -34,6 +34,10 @@ * official policies, either expressed or implied, of the FreeBSD Project. * * ------+---------+---------+---------+---------+---------+---------+---------* + * This is intended to be a set of general-purpose routines to process times. + * Right now it probably still has a number of assumptions in it, such that + * it works fine for newsyslog but might not work for other uses. + * ------+---------+---------+---------+---------+---------+---------+---------* */ #include <sys/cdefs.h> @@ -44,11 +48,41 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdint.h> #include <stdlib.h> +#include <string.h> #include <time.h> #include "extern.h" +#define SECS_PER_HOUR 3600 + +/* + * Bit-values which indicate which components of time were specified + * by the string given to parse8601 or parseDWM. These are needed to + * calculate what time-in-the-future will match that string. + */ +#define TSPEC_YEAR 0x0001 +#define TSPEC_MONTHOFYEAR 0x0002 +#define TSPEC_LDAYOFMONTH 0x0004 +#define TSPEC_DAYOFMONTH 0x0008 +#define TSPEC_DAYOFWEEK 0x0010 +#define TSPEC_HOUROFDAY 0x0020 + +#define TNYET_ADJ4DST -10 /* DST has "not yet" been adjusted */ + +struct ptime_data { + time_t basesecs; /* Base point for relative times */ + time_t tsecs; /* Time in seconds */ + struct tm basetm; /* Base Time expanded into fields */ + struct tm tm; /* Time expanded into fields */ + int did_adj4dst; /* Track calls to ptime_adjust4dst */ + int parseopts; /* Options given for parsing */ + int tmspec; /* Indicates which time fields had + * been specified by the user */ +}; + static int days_pmonth(int month, int year); +static int parse8601(struct ptime_data *ptime, const char *str); +static int parseDWM(struct ptime_data *ptime, const char *str); /* * Simple routine to calculate the number of days in a given month. @@ -92,20 +126,12 @@ days_pmonth(int month, int year) * We don't accept a timezone specification; missing fields (including timezone) * are defaulted to the current date but time zero. */ -time_t -parse8601(const char *s, time_t *next_time) +static int +parse8601(struct ptime_data *ptime, const char *s) { char *t; - time_t tsecs; - struct tm tm, *tmp; long l; - - tmp = localtime(&timenow); - tm = *tmp; - if (next_time != NULL) - *next_time = (time_t)-1; - - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + struct tm tm; l = strtol(s, &t, 10); if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T')) @@ -116,18 +142,23 @@ parse8601(const char *s, time_t *next_time) * provided) or to the letter `T' which separates date and time in * ISO 8601. The pointer arithmetic is the same for either case. */ + tm = ptime->tm; + ptime->tmspec = TSPEC_HOUROFDAY; switch (t - s) { case 8: tm.tm_year = ((l / 1000000) - 19) * 100; l = l % 1000000; case 6: + ptime->tmspec |= TSPEC_YEAR; tm.tm_year -= tm.tm_year % 100; tm.tm_year += l / 10000; l = l % 10000; case 4: + ptime->tmspec |= TSPEC_MONTHOFYEAR; tm.tm_mon = (l / 100) - 1; l = l % 100; case 2: + ptime->tmspec |= TSPEC_DAYOFMONTH; tm.tm_mday = l; case 0: break; @@ -154,6 +185,7 @@ parse8601(const char *s, time_t *next_time) tm.tm_min = l % 100; l /= 100; case 2: + ptime->tmspec |= TSPEC_HOUROFDAY; tm.tm_hour = l; case 0: break; @@ -167,14 +199,8 @@ parse8601(const char *s, time_t *next_time) return (-1); } - tsecs = mktime(&tm); - /* - * Check for invalid times, including things like the missing - * hour when switching from "standard time" to "daylight saving". - */ - if (tsecs == (time_t)-1) - tsecs = (time_t)-2; - return (tsecs); + ptime->tm = tm; + return (0); } /*- @@ -191,32 +217,27 @@ parse8601(const char *s, time_t *next_time) * We don't accept a timezone specification; missing fields * are defaulted to the current date but time zero. */ -time_t -parseDWM(char *s, time_t *next_time) +static int +parseDWM(struct ptime_data *ptime, const char *s) { - int daysmon; + int daysmon, Dseen, WMseen; char *t; - time_t tsecs; - struct tm tm, *tmp; long l; - int WMseen = 0; - int Dseen = 0; - - tmp = localtime(&timenow); - tm = *tmp; - if (next_time != NULL) - *next_time = (time_t)-1; + struct tm tm; /* Save away the number of days in this month */ + tm = ptime->tm; daysmon = days_pmonth(tm.tm_mon, tm.tm_year); - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + WMseen = Dseen = 0; + ptime->tmspec = TSPEC_HOUROFDAY; for (;;) { switch (*s) { case 'D': if (Dseen) return (-1); Dseen++; + ptime->tmspec |= TSPEC_HOUROFDAY; s++; l = strtol(s, &t, 10); if (l < 0 || l > 23) @@ -228,6 +249,7 @@ parseDWM(char *s, time_t *next_time) if (WMseen) return (-1); WMseen++; + ptime->tmspec |= TSPEC_DAYOFWEEK; s++; l = strtol(s, &t, 10); if (l < 0 || l > 6) @@ -255,11 +277,14 @@ parseDWM(char *s, time_t *next_time) if (WMseen) return (-1); WMseen++; + ptime->tmspec |= TSPEC_DAYOFMONTH; s++; if (tolower(*s) == 'l') { + /* User wants the last day of the month. */ + ptime->tmspec |= TSPEC_LDAYOFMONTH; tm.tm_mday = daysmon; s++; - t = s; + t = __DECONST(char *,s); } else { l = strtol(s, &t, 10); if (l < 1 || l > 31) @@ -282,12 +307,307 @@ parseDWM(char *s, time_t *next_time) s = t; } - tsecs = mktime(&tm); + ptime->tm = tm; + return (0); +} + +/* + * Initialize a new ptime-related data area. + */ +struct ptime_data * +ptime_init(const struct ptime_data *optsrc) +{ + struct ptime_data *newdata; + + newdata = malloc(sizeof(struct ptime_data)); + if (optsrc != NULL) { + memcpy(newdata, optsrc, sizeof(struct ptime_data)); + } else { + memset(newdata, '\0', sizeof(struct ptime_data)); + newdata->did_adj4dst = TNYET_ADJ4DST; + } + + return (newdata); +} + +/* + * Adjust a given time if that time is in a different timezone than + * some other time. + */ +int +ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc) +{ + struct ptime_data adjtime; + + if (ptime == NULL) + return (-1); + + /* + * Changes are not made to the given time until after all + * of the calculations have been successful. + */ + adjtime = *ptime; + + /* Check to see if this adjustment was already made */ + if ((adjtime.did_adj4dst != TNYET_ADJ4DST) && + (adjtime.did_adj4dst == dstsrc->tm.tm_isdst)) + return (0); /* yes, so don't make it twice */ + + /* See if daylight-saving has changed between the two times. */ + if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) { + if (adjtime.tm.tm_isdst == 1) + adjtime.tsecs -= SECS_PER_HOUR; + else if (adjtime.tm.tm_isdst == 0) + adjtime.tsecs += SECS_PER_HOUR; + adjtime.tm = *(localtime(&adjtime.tsecs)); + /* Remember that this adjustment has been made */ + adjtime.did_adj4dst = dstsrc->tm.tm_isdst; + /* + * XXX - Should probably check to see if changing the + * hour also changed the value of is_dst. What + * should we do in that case? + */ + } + + *ptime = adjtime; + return (0); +} + +int +ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime, + const char *str) +{ + int dpm, pres; + struct tm temp_tm; + + ptime->parseopts = parseopts; + ptime->basesecs = basetime; + ptime->basetm = *(localtime(&ptime->basesecs)); + ptime->tm = ptime->basetm; + ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0; + + /* + * Call a routine which sets ptime.tm and ptime.tspecs based + * on the given string and parsing-options. Note that the + * routine should not call mktime to set ptime.tsecs. + */ + if (parseopts & PTM_PARSE_DWM) + pres = parseDWM(ptime, str); + else + pres = parse8601(ptime, str); + if (pres < 0) { + ptime->tsecs = (time_t)pres; + return (pres); + } + + /* + * Before calling mktime, check to see if we ended up with a + * "day-of-month" that does not exist in the selected month. + * If we did call mktime with that info, then mktime will + * make it look like the user specifically requested a day + * in the following month (eg: Feb 31 turns into Mar 3rd). + */ + dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); + if ((parseopts & PTM_PARSE_MATCHDOM) && + (ptime->tmspec & TSPEC_DAYOFMONTH) && + (ptime->tm.tm_mday> dpm)) { + /* + * ptime_nxtime() will want a ptime->tsecs value, + * but we need to avoid mktime resetting all the + * ptime->tm values. + */ + if (verbose && dbg_at_times > 1) + fprintf(stderr, + "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)", + ptime->tm.tm_year, ptime->tm.tm_mon, + ptime->tm.tm_mday, ptime->tm.tm_hour, + ptime->tm.tm_min, dpm); + temp_tm = ptime->tm; + ptime->tsecs = mktime(&temp_tm); + if (ptime->tsecs > (time_t)-1) + ptimeset_nxtime(ptime); + if (verbose && dbg_at_times > 1) + fprintf(stderr, + " to: %4d/%02d/%02d %02d:%02d\n", + ptime->tm.tm_year, ptime->tm.tm_mon, + ptime->tm.tm_mday, ptime->tm.tm_hour, + ptime->tm.tm_min); + } + + /* + * Convert the ptime.tm into standard time_t seconds. Check + * for invalid times, which includes things like the hour lost + * when switching from "standard time" to "daylight saving". + */ + ptime->tsecs = mktime(&ptime->tm); + if (ptime->tsecs == (time_t)-1) { + ptime->tsecs = (time_t)-2; + return (-2); + } + + return (0); +} + +int +ptime_free(struct ptime_data *ptime) +{ + + if (ptime == NULL) + return (-1); + + free(ptime); + return (0); +} + +/* + * Some trivial routines so ptime_data can remain a completely + * opaque type. + */ +const char * +ptimeget_ctime(const struct ptime_data *ptime) +{ + + if (ptime == NULL) + return ("Null time in ptimeget_ctime()\n"); + + return (ctime(&ptime->tsecs)); +} + +double +ptimeget_diff(const struct ptime_data *minuend, const struct + ptime_data *subtrahend) +{ + + /* Just like difftime(), we have no good error-return */ + if (minuend == NULL || subtrahend == NULL) + return (0.0); + + return (difftime(minuend->tsecs, subtrahend->tsecs)); +} + +time_t +ptimeget_secs(const struct ptime_data *ptime) +{ + + if (ptime == NULL) + return (-1); + + return (ptime->tsecs); +} + +/* + * Generate an approximate timestamp for the next event, based on + * what parts of time were specified by the original parameter to + * ptime_relparse(). The result may be -1 if there is no obvious + * "next time" which will work. + */ +int +ptimeset_nxtime(struct ptime_data *ptime) +{ + int moredays, tdpm, tmon, tyear; + struct ptime_data nextmatch; + + if (ptime == NULL) + return (-1); + + /* + * Changes are not made to the given time until after all + * of the calculations have been successful. + */ + nextmatch = *ptime; + /* + * If the user specified a year and we're already past that + * time, then there will never be another one! + */ + if (ptime->tmspec & TSPEC_YEAR) + return (-1); + + /* + * The caller gave us a time in the past. Calculate how much + * time is needed to go from that valid rotate time to the + * next valid rotate time. We only need to get to the nearest + * hour, because newsyslog is only run once per hour. + */ + moredays = 0; + if (ptime->tmspec & TSPEC_MONTHOFYEAR) { + /* Special case: Feb 29th does not happen every year. */ + if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) { + nextmatch.tm.tm_year += 4; + if (days_pmonth(1, nextmatch.tm.tm_year) < 29) + nextmatch.tm.tm_year += 4; + } else { + nextmatch.tm.tm_year += 1; + } + nextmatch.tm.tm_isdst = -1; + nextmatch.tsecs = mktime(&nextmatch.tm); + + } else if (ptime->tmspec & TSPEC_LDAYOFMONTH) { + /* + * Need to get to the last day of next month. Origtm is + * already at the last day of this month, so just add to + * it number of days in the next month. + */ + if (ptime->tm.tm_mon < 11) + moredays = days_pmonth(ptime->tm.tm_mon + 1, + ptime->tm.tm_year); + else + moredays = days_pmonth(0, ptime->tm.tm_year + 1); + + } else if (ptime->tmspec & TSPEC_DAYOFMONTH) { + /* Jump to the same day in the next month */ + moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year); + /* + * In some cases, the next month may not *have* the + * desired day-of-the-month. If that happens, then + * move to the next month that does have enough days. + */ + tmon = ptime->tm.tm_mon; + tyear = ptime->tm.tm_year; + for (;;) { + if (tmon < 11) + tmon += 1; + else { + tmon = 0; + tyear += 1; + } + tdpm = days_pmonth(tmon, tyear); + if (tdpm >= ptime->tm.tm_mday) + break; + moredays += tdpm; + } + + } else if (ptime->tmspec & TSPEC_DAYOFWEEK) { + moredays = 7; + } else if (ptime->tmspec & TSPEC_HOUROFDAY) { + moredays = 1; + } + + if (moredays != 0) { + nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays; + nextmatch.tm = *(localtime(&nextmatch.tsecs)); + } + /* - * Check for invalid times, including things like the missing - * hour when switching from "standard time" to "daylight saving". + * The new time will need to be adjusted if the setting of + * daylight-saving has changed between the two times. */ - if (tsecs == (time_t)-1) - tsecs = (time_t)-2; - return (tsecs); + ptime_adjust4dst(&nextmatch, ptime); + + /* Everything worked. Update the given time and return. */ + *ptime = nextmatch; + return (0); +} + +int +ptimeset_time(struct ptime_data *ptime, time_t secs) +{ + + if (ptime == NULL) + return (-1); + + ptime->tsecs = secs; + ptime->tm = *(localtime(&ptime->tsecs)); + ptime->parseopts = 0; + /* ptime->tmspec = ? */ + return (0); } |