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
This commit is contained in:
Garance A Drosehn 2003-09-23 00:00:26 +00:00
parent da87d7e10d
commit 6a1485e2fe
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=120361
3 changed files with 482 additions and 87 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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);
/*
* 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);
}
/*
* 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));
}
/*
* The new time will need to be adjusted if the setting of
* daylight-saving has changed between the two times.
*/
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);
}