2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2007, 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
34 #define _XOPEN_SOURCE 500
43 #include "prototypes.h"
45 static int parse_schedule_line(char *, time_t[], int, time_t, time_t *,
47 static int time_ok(time_t, char *, int);
48 static char *parse_time(time_t *, char *, time_t *);
49 static char *parse_every(time_t *, char *);
50 static char *parse_until(time_t *, char *, time_t *);
51 static char *parse_skip(time_t *, char *, time_t *);
52 static int insert_update(time_t, time_t[], int, time_t);
53 static int delete_update(time_t, time_t[], int);
56 * Read update schedule from file FNAME.
57 * Put the first N-1 updates after T0 into SCHED[] in ascending order,
58 * terminated with a zero.
59 * Use ANCHOR as initial anchor for anchor-relative times.
60 * Return 0 on success, -1 on failure.
63 read_schedule(char *fname, time_t sched[], int n, time_t t0, time_t anchor)
71 fp = fopen(fname, "r");
73 logerror("Can't open %s for reading (%s)\n",
74 fname, strerror(errno));
83 while (fgets(buf, sizeof(buf), fp) != NULL) {
85 endp = strchr(buf, '#');
88 if (parse_schedule_line(buf, sched, n, t0, &anchor, fname, lno))
97 * Parse an update schedule directive from LINE.
98 * Update SCHED[] and ANCHOR accordingly.
99 * SCHED[] holds the first N-1 updates after T0 in ascending order.
100 * FNAME and LNO file name and line number for reporting errors.
103 parse_schedule_line(char *line, time_t sched[], int n,
104 time_t t0, time_t *anchor,
105 char *fname, int lno)
111 if ((endp = parse_time(&t, line, anchor))) {
112 if (!time_ok(t, fname, lno))
115 insert_update(t, sched, n, t0);
116 } else if ((endp = parse_every(&delta, line))) {
117 if ((p = parse_until(&u, endp, anchor))) {
119 if (!time_ok(u, fname, lno))
126 } while ((u == (time_t)-1 || t <= u)
127 && insert_update(t, sched, n, t0) < n - 1);
128 } else if ((endp = parse_skip(&t, line, anchor))) {
129 if (!time_ok(t, fname, lno))
131 delete_update(t, sched, n);
136 while (isspace(*endp)) endp++;
139 logerror("%s:%d: unintelligible\n", fname, lno);
141 logerror("%s:%d: trailing junk\n", fname, lno);
149 * Complain and return zero when T is bad, else return non-zero.
150 * FNAME and LNO file name and line number.
153 time_ok(time_t t, char *fname, int lno)
155 if (t == (time_t)-1) {
156 logerror("%s:%d: time weird\n", fname, lno);
163 * Parse a time from S into *T.
164 * *ANCHOR is the base for anchor-relative time.
165 * Return pointer to first character not parsed on success,
166 * null pointer on failure.
169 parse_time(time_t *t, char *s, time_t *anchor)
171 static char *fmt[] = {
172 "%Y-%m-%d %H:%M ", /* ISO 8601 */
173 "%b %d %H:%M %Y ", /* like ctime(): Dec 22 15:35 2006 */
174 "%d %b %Y %H:%M ", /* 22 Dec 2006 15:35 */
175 "next %a %H:%M ", /* next Fri 15:35 */
176 "next %a ", /* next Fri */
181 struct tm tm, nexttm;
183 for (p = s; isspace(*(unsigned char *)p); ++p) ;
188 memset(&tm, 0, sizeof(tm));
190 endp = strptime(p, fmt[i], &tm);
195 if (tm.tm_mday == 0) {
196 /* relative to anchor */
197 nexttm = *localtime(anchor);
198 if (tm.tm_hour >= 0) {
199 /* got hour and minute */
200 nexttm.tm_hour = tm.tm_hour;
201 nexttm.tm_min = tm.tm_min;
204 nexttm.tm_mday += tm.tm_wday - nexttm.tm_wday;
205 if (tm.tm_wday <= nexttm.tm_wday)
216 * Parse an every clause from S into *SECS.
217 * Return pointer to first character not parsed on success,
218 * null pointer on failure.
221 parse_every(time_t *secs, char *s)
226 sscanf(s, " every %u hours%n", &delta, &nch);
230 sscanf(s, " every %u minutes%n", &delta, &nch);
232 return NULL; *secs = 60 * delta;
237 * Parse an until clause from S into *T.
238 * *ANCHOR is the base for anchor-relative time.
239 * Return pointer to first character not parsed on success,
240 * null pointer on failure.
243 parse_until(time_t *t, char *s, time_t *anchor)
248 sscanf(s, " until%n", &nch);
251 return parse_time(t, s + nch, anchor);
255 * Parse an skip clause from S into *T.
256 * *ANCHOR is the base for anchor-relative time.
257 * Return pointer to first character not parsed on success,
258 * null pointer on failure.
261 parse_skip(time_t *t, char *s, time_t *anchor)
266 sscanf(s, " skip%n", &nch);
269 return parse_time(t, s + nch, anchor);
273 * Return the index of the first update at or after T in SCHED[].
276 find_update(time_t t, time_t sched[])
280 /* Could use binary search here, but it's hardly worth it */
281 for (i = 0; sched[i] && t > sched[i]; i++) ;
286 * Insert update at T into SCHED[].
287 * SCHED[] holds the first N-1 updates after T0 in ascending order.
288 * If T is before T0 or outside game_days/game_hours, return -1.
289 * If there's no space for T in SCHED[], return N-1.
290 * Else insert T into SCHED[] and return its index in SCHED[].
293 insert_update(time_t t, time_t sched[], int n, time_t t0)
297 if (t <= t0 || !gamehours(t))
300 i = find_update(t, sched);
301 memmove(sched + i + 1, sched + i, (n - 1 - i) * sizeof(*sched));
308 * Delete update T from SCHED[].
309 * SCHED[] holds N-1 updates in ascending order.
310 * Return the index of the first update after T in SCHED[].
313 delete_update(time_t t, time_t sched[], int n)
315 int i = find_update(t, sched);
317 memmove(sched + i, sched + i + 1,
318 (n - 1 - i) * sizeof(*sched));