2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2011, 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-2008
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));
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[] = {
173 * Absolute time formats
174 * Must set tm_year, tm_mon, tm_mday, tm_hour, tm_min.
176 "%Y-%m-%d %H:%M ", /* ISO 8601 */
177 "%b %d %H:%M %Y ", /* like ctime(): Dec 22 15:35 2006 */
178 "%d %b %Y %H:%M ", /* like RFC 2822: 22 Dec 2006 15:35 */
180 * Relative to anchor formats
181 * Must set tm_wday, may set tm_hour and tm_min
183 "next %a %H:%M ", /* next Fri 15:35 */
184 "next %a ", /* next Fri */
189 struct tm tm, nexttm;
191 for (p = s; isspace(*(unsigned char *)p); ++p) ;
197 * Carefully initialize tm so we can tell what strptime()
200 * Beware: some losing implementations of strptime() happily
201 * succeed when they fully consumed the first argument,
202 * regardless of whether they matched the full second argument
203 * or not. Observed on FreeBSD 6.2.
205 memset(&tm, 0, sizeof(tm));
206 tm.tm_hour = -1; /* to recognize anchor-relat. w/o time */
207 tm.tm_year = INT_MIN; /* strptime() lossage work-around */
208 tm.tm_mon = tm.tm_mday = tm.tm_min = tm.tm_wday = -1; /* ditto */
209 endp = strptime(p, fmt[i], &tm);
211 /* strptime() lossage work-around: */
212 && ((tm.tm_year != INT_MIN && tm.tm_mon != -1
213 && tm.tm_mday != -1 && tm.tm_hour != -1 && tm.tm_min != -1)
215 && (tm.tm_hour == -1 || tm.tm_min != -1))))
219 if (tm.tm_mday == -1) {
220 /* relative to anchor */
221 nexttm = *localtime(anchor);
222 if (tm.tm_hour >= 0) {
223 /* got hour and minute */
224 nexttm.tm_hour = tm.tm_hour;
225 nexttm.tm_min = tm.tm_min;
228 nexttm.tm_mday += tm.tm_wday - nexttm.tm_wday;
229 if (tm.tm_wday <= nexttm.tm_wday)
240 * Parse an every clause from S into *SECS.
241 * Return pointer to first character not parsed on success,
242 * null pointer on failure.
245 parse_every(time_t *secs, char *s)
250 sscanf(s, " every %u hours%n", &delta, &nch);
254 sscanf(s, " every %u minutes%n", &delta, &nch);
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, (n - 1 - i) * sizeof(*sched));