2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2012, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure, Markus Armbruster
6 * Empire 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 3 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, see <http://www.gnu.org/licenses/>.
21 * See files README, COPYING and CREDITS in the root of the source
22 * tree for related information and legal notices. It is expected
23 * that future projects/authors will amend these files as needed.
27 * rdsched.c: Read update schedule
29 * Known contributors to this file:
30 * Markus Armbruster, 2007-2011
33 #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));
82 while (fgets(buf, sizeof(buf), fp) != NULL) {
84 endp = strchr(buf, '#');
87 if (parse_schedule_line(buf, sched, n, t0, &anchor,
88 fname ? fname : "<stdin>", lno)) {
100 * Parse an update schedule directive from LINE.
101 * Update SCHED[] and ANCHOR accordingly.
102 * SCHED[] holds the first N-1 updates after T0 in ascending order.
103 * FNAME and LNO file name and line number for reporting errors.
106 parse_schedule_line(char *line, time_t sched[], int n,
107 time_t t0, time_t *anchor,
108 char *fname, int lno)
114 if ((endp = parse_time(&t, line, anchor))) {
115 if (!time_ok(t, fname, lno))
118 insert_update(t, sched, n, t0);
119 } else if ((endp = parse_every(&delta, line))) {
120 if ((p = parse_until(&u, endp, anchor))) {
122 if (!time_ok(u, fname, lno))
129 } while ((u == (time_t)-1 || t <= u)
130 && insert_update(t, sched, n, t0) < n - 1);
131 } else if ((endp = parse_skip(&t, line, anchor))) {
132 if (!time_ok(t, fname, lno))
134 delete_update(t, sched, n);
139 while (isspace(*endp)) endp++;
142 logerror("%s:%d: unintelligible\n", fname, lno);
144 logerror("%s:%d: trailing junk\n", fname, lno);
152 * Complain and return zero when T is bad, else return non-zero.
153 * FNAME and LNO file name and line number.
156 time_ok(time_t t, char *fname, int lno)
158 if (t == (time_t)-1) {
159 logerror("%s:%d: time weird\n", fname, lno);
166 * Parse a time from S into *T.
167 * *ANCHOR is the base for anchor-relative time.
168 * Return pointer to first character not parsed on success,
169 * null pointer on failure.
172 parse_time(time_t *t, char *s, time_t *anchor)
174 static char *fmt[] = {
176 * Absolute time formats
177 * Must set tm_year, tm_mon, tm_mday, tm_hour, tm_min.
179 "%Y-%m-%d %H:%M ", /* ISO 8601 */
180 "%b %d %H:%M %Y ", /* like ctime(): Dec 22 15:35 2006 */
181 "%d %b %Y %H:%M ", /* like RFC 2822: 22 Dec 2006 15:35 */
183 * Relative to anchor formats
184 * Must set tm_wday, may set tm_hour and tm_min
186 "next %a %H:%M ", /* next Fri 15:35 */
187 "next %a ", /* next Fri */
192 struct tm tm, nexttm;
194 for (p = s; isspace(*(unsigned char *)p); ++p) ;
200 * Carefully initialize tm so we can tell what strptime()
203 * Beware: some losing implementations of strptime() happily
204 * succeed when they fully consumed the first argument,
205 * regardless of whether they matched the full second argument
206 * or not. Observed on FreeBSD 6.2.
208 memset(&tm, 0, sizeof(tm));
209 tm.tm_hour = -1; /* to recognize anchor-relat. w/o time */
210 tm.tm_year = INT_MIN; /* strptime() lossage work-around */
211 tm.tm_mon = tm.tm_mday = tm.tm_min = tm.tm_wday = -1; /* ditto */
212 endp = strptime(p, fmt[i], &tm);
214 /* strptime() lossage work-around: */
215 && ((tm.tm_year != INT_MIN && tm.tm_mon != -1
216 && tm.tm_mday != -1 && tm.tm_hour != -1 && tm.tm_min != -1)
218 && (tm.tm_hour == -1 || tm.tm_min != -1))))
222 if (tm.tm_mday == -1) {
223 /* relative to anchor */
224 nexttm = *localtime(anchor);
225 if (tm.tm_hour >= 0) {
226 /* got hour and minute */
227 nexttm.tm_hour = tm.tm_hour;
228 nexttm.tm_min = tm.tm_min;
231 nexttm.tm_mday += tm.tm_wday - nexttm.tm_wday;
232 if (tm.tm_wday <= nexttm.tm_wday)
243 * Parse an every clause from S into *SECS.
244 * Return pointer to first character not parsed on success,
245 * null pointer on failure.
248 parse_every(time_t *secs, char *s)
253 sscanf(s, " every %u hours%n", &delta, &nch);
257 sscanf(s, " every %u minutes%n", &delta, &nch);
265 * Parse an until clause from S into *T.
266 * *ANCHOR is the base for anchor-relative time.
267 * Return pointer to first character not parsed on success,
268 * null pointer on failure.
271 parse_until(time_t *t, char *s, time_t *anchor)
276 sscanf(s, " until%n", &nch);
279 return parse_time(t, s + nch, anchor);
283 * Parse an skip clause from S into *T.
284 * *ANCHOR is the base for anchor-relative time.
285 * Return pointer to first character not parsed on success,
286 * null pointer on failure.
289 parse_skip(time_t *t, char *s, time_t *anchor)
294 sscanf(s, " skip%n", &nch);
297 return parse_time(t, s + nch, anchor);
301 * Return the index of the first update at or after T in SCHED[].
304 find_update(time_t t, time_t sched[])
308 /* Could use binary search here, but it's hardly worth it */
309 for (i = 0; sched[i] && t > sched[i]; i++) ;
314 * Insert update at T into SCHED[].
315 * SCHED[] holds the first N-1 updates after T0 in ascending order.
316 * If T is before T0 or outside game_days/game_hours, return -1.
317 * If there's no space for T in SCHED[], return N-1.
318 * Else insert T into SCHED[] and return its index in SCHED[].
321 insert_update(time_t t, time_t sched[], int n, time_t t0)
325 if (t <= t0 || !gamehours(t))
328 i = find_update(t, sched);
329 memmove(sched + i + 1, sched + i, (n - 1 - i) * sizeof(*sched));
336 * Delete update T from SCHED[].
337 * SCHED[] holds N-1 updates in ascending order.
338 * Return the index of the first update after T in SCHED[].
341 delete_update(time_t t, time_t sched[], int n)
343 int i = find_update(t, sched);
345 memmove(sched + i, sched + i + 1, (n - 1 - i) * sizeof(*sched));