2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * See files README, COPYING and CREDITS in the root of the source
23 * tree for related information and legal notices. It is expected
24 * that future projects/authors will amend these files as needed.
28 * rdsched.c: Read update schedule
30 * Known contributors to this file:
31 * Markus Armbruster, 2007-2008
34 #define _XOPEN_SOURCE 500
44 #include "prototypes.h"
46 static int parse_schedule_line(char *, time_t[], int, time_t, time_t *,
48 static int time_ok(time_t, char *, int);
49 static char *parse_time(time_t *, char *, time_t *);
50 static char *parse_every(time_t *, char *);
51 static char *parse_until(time_t *, char *, time_t *);
52 static char *parse_skip(time_t *, char *, time_t *);
53 static int insert_update(time_t, time_t[], int, time_t);
54 static int delete_update(time_t, time_t[], int);
57 * Read update schedule from file FNAME.
58 * Put the first N-1 updates after T0 into SCHED[] in ascending order,
59 * terminated with a zero.
60 * Use ANCHOR as initial anchor for anchor-relative times.
61 * Return 0 on success, -1 on failure.
64 read_schedule(char *fname, time_t sched[], int n, time_t t0, time_t anchor)
72 fp = fopen(fname, "r");
74 logerror("Can't open %s for reading (%s)\n",
75 fname, strerror(errno));
84 while (fgets(buf, sizeof(buf), fp) != NULL) {
86 endp = strchr(buf, '#');
89 if (parse_schedule_line(buf, sched, n, t0, &anchor, fname, lno))
98 * Parse an update schedule directive from LINE.
99 * Update SCHED[] and ANCHOR accordingly.
100 * SCHED[] holds the first N-1 updates after T0 in ascending order.
101 * FNAME and LNO file name and line number for reporting errors.
104 parse_schedule_line(char *line, time_t sched[], int n,
105 time_t t0, time_t *anchor,
106 char *fname, int lno)
112 if ((endp = parse_time(&t, line, anchor))) {
113 if (!time_ok(t, fname, lno))
116 insert_update(t, sched, n, t0);
117 } else if ((endp = parse_every(&delta, line))) {
118 if ((p = parse_until(&u, endp, anchor))) {
120 if (!time_ok(u, fname, lno))
127 } while ((u == (time_t)-1 || t <= u)
128 && insert_update(t, sched, n, t0) < n - 1);
129 } else if ((endp = parse_skip(&t, line, anchor))) {
130 if (!time_ok(t, fname, lno))
132 delete_update(t, sched, n);
137 while (isspace(*endp)) endp++;
140 logerror("%s:%d: unintelligible\n", fname, lno);
142 logerror("%s:%d: trailing junk\n", fname, lno);
150 * Complain and return zero when T is bad, else return non-zero.
151 * FNAME and LNO file name and line number.
154 time_ok(time_t t, char *fname, int lno)
156 if (t == (time_t)-1) {
157 logerror("%s:%d: time weird\n", fname, lno);
164 * Parse a time from S into *T.
165 * *ANCHOR is the base for anchor-relative time.
166 * Return pointer to first character not parsed on success,
167 * null pointer on failure.
170 parse_time(time_t *t, char *s, time_t *anchor)
172 static char *fmt[] = {
174 * Absolute time formats
175 * Must set tm_year, tm_mon, tm_mday, tm_hour, tm_min.
177 "%Y-%m-%d %H:%M ", /* ISO 8601 */
178 "%b %d %H:%M %Y ", /* like ctime(): Dec 22 15:35 2006 */
179 "%d %b %Y %H:%M ", /* like RFC 2822: 22 Dec 2006 15:35 */
181 * Relative to anchor formats
182 * Must set tm_wday, may set tm_hour and tm_min
184 "next %a %H:%M ", /* next Fri 15:35 */
185 "next %a ", /* next Fri */
190 struct tm tm, nexttm;
192 for (p = s; isspace(*(unsigned char *)p); ++p) ;
198 * Carefully initialize tm so we can tell what strptime()
201 * Beware: some losing implementations of strptime() happily
202 * succeed when they fully consumed the first argument,
203 * regardless of whether they matched the full second argument
204 * or not. Observed on FreeBSD 6.2.
206 memset(&tm, 0, sizeof(tm));
207 tm.tm_hour = -1; /* to recognize anchor-relat. w/o time */
208 tm.tm_year = INT_MIN; /* strptime() lossage work-around */
209 tm.tm_mon = tm.tm_mday = tm.tm_min = tm.tm_wday = -1; /* ditto */
210 endp = strptime(p, fmt[i], &tm);
212 /* strptime() lossage work-around: */
213 && ((tm.tm_year != INT_MIN && tm.tm_mon != -1
214 && tm.tm_mday != -1 && tm.tm_hour != -1 && tm.tm_min != -1)
216 && (tm.tm_hour == -1 || tm.tm_min != -1))))
220 if (tm.tm_mday == -1) {
221 /* relative to anchor */
222 nexttm = *localtime(anchor);
223 if (tm.tm_hour >= 0) {
224 /* got hour and minute */
225 nexttm.tm_hour = tm.tm_hour;
226 nexttm.tm_min = tm.tm_min;
229 nexttm.tm_mday += tm.tm_wday - nexttm.tm_wday;
230 if (tm.tm_wday <= nexttm.tm_wday)
241 * Parse an every clause from S into *SECS.
242 * Return pointer to first character not parsed on success,
243 * null pointer on failure.
246 parse_every(time_t *secs, char *s)
251 sscanf(s, " every %u hours%n", &delta, &nch);
255 sscanf(s, " every %u minutes%n", &delta, &nch);
257 return NULL; *secs = 60 * delta;
262 * Parse an until clause from S into *T.
263 * *ANCHOR is the base for anchor-relative time.
264 * Return pointer to first character not parsed on success,
265 * null pointer on failure.
268 parse_until(time_t *t, char *s, time_t *anchor)
273 sscanf(s, " until%n", &nch);
276 return parse_time(t, s + nch, anchor);
280 * Parse an skip clause from S into *T.
281 * *ANCHOR is the base for anchor-relative time.
282 * Return pointer to first character not parsed on success,
283 * null pointer on failure.
286 parse_skip(time_t *t, char *s, time_t *anchor)
291 sscanf(s, " skip%n", &nch);
294 return parse_time(t, s + nch, anchor);
298 * Return the index of the first update at or after T in SCHED[].
301 find_update(time_t t, time_t sched[])
305 /* Could use binary search here, but it's hardly worth it */
306 for (i = 0; sched[i] && t > sched[i]; i++) ;
311 * Insert update at T into SCHED[].
312 * SCHED[] holds the first N-1 updates after T0 in ascending order.
313 * If T is before T0 or outside game_days/game_hours, return -1.
314 * If there's no space for T in SCHED[], return N-1.
315 * Else insert T into SCHED[] and return its index in SCHED[].
318 insert_update(time_t t, time_t sched[], int n, time_t t0)
322 if (t <= t0 || !gamehours(t))
325 i = find_update(t, sched);
326 memmove(sched + i + 1, sched + i, (n - 1 - i) * sizeof(*sched));
333 * Delete update T from SCHED[].
334 * SCHED[] holds N-1 updates in ascending order.
335 * Return the index of the first update after T in SCHED[].
338 delete_update(time_t t, time_t sched[], int n)
340 int i = find_update(t, sched);
342 memmove(sched + i, sched + i + 1,
343 (n - 1 - i) * sizeof(*sched));