src/lib/common/rdsched.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  rdsched.c: Read update schedule
00029  * 
00030  *  Known contributors to this file:
00031  *     Markus Armbruster, 2007
00032  */
00033 
00034 #define _XOPEN_SOURCE 500
00035 
00036 #include <config.h>
00037 
00038 #include <ctype.h>
00039 #include <errno.h>
00040 #include <stdio.h>
00041 #include <string.h>
00042 #include <time.h>
00043 #include "prototypes.h"
00044 
00045 static int parse_schedule_line(char *, time_t[], int, time_t, time_t *,
00046                                char *, int);
00047 static int time_ok(time_t, char *, int);
00048 static char *parse_time(time_t *, char *, time_t *);
00049 static char *parse_every(time_t *, char *);
00050 static char *parse_until(time_t *, char *, time_t *);
00051 static char *parse_skip(time_t *, char *, time_t *);
00052 static int insert_update(time_t, time_t[], int, time_t);
00053 static int delete_update(time_t, time_t[], int);
00054 
00055 /*
00056  * Read update schedule from file FNAME.
00057  * Put the first N-1 updates after T0 into SCHED[] in ascending order,
00058  * terminated with a zero.
00059  * Use ANCHOR as initial anchor for anchor-relative times. 
00060  * Return 0 on success, -1 on failure.
00061  */
00062 int
00063 read_schedule(char *fname, time_t sched[], int n, time_t t0, time_t anchor)
00064 {
00065     FILE *fp;
00066     int lno = 0;
00067     char buf[1024];
00068     char *endp;
00069 
00070     if (fname) {
00071         fp = fopen(fname, "r");
00072         if (!fp) {
00073             logerror("Can't open %s for reading (%s)\n",
00074                      fname, strerror(errno));
00075             return -1;
00076         }
00077     } else {
00078         fp = stdin;
00079         fname = "<stdin>";
00080     }
00081 
00082     sched[0] = 0;
00083     while (fgets(buf, sizeof(buf), fp) != NULL) {
00084         ++lno;
00085         endp = strchr(buf, '#');
00086         if (endp)
00087             *endp = 0;
00088         if (parse_schedule_line(buf, sched, n, t0, &anchor, fname, lno))
00089             return -1;
00090     }
00091 
00092     fclose(fp);
00093     return 0;
00094 }
00095 
00096 /*
00097  * Parse an update schedule directive from LINE.
00098  * Update SCHED[] and ANCHOR accordingly.
00099  * SCHED[] holds the first N-1 updates after T0 in ascending order.
00100  * FNAME and LNO file name and line number for reporting errors.
00101  */
00102 static int
00103 parse_schedule_line(char *line, time_t sched[], int n,
00104                     time_t t0, time_t *anchor,
00105                     char *fname, int lno)
00106 {
00107     char *endp, *p;
00108     int bol;
00109     time_t t, delta, u;
00110 
00111     if ((endp = parse_time(&t, line, anchor))) {
00112         if (!time_ok(t, fname, lno))
00113             return -1;
00114         *anchor = t;
00115         insert_update(t, sched, n, t0);
00116     } else if ((endp = parse_every(&delta, line))) {
00117         if ((p = parse_until(&u, endp, anchor))) {
00118             endp = p;
00119             if (!time_ok(u, fname, lno))
00120                 return -1;
00121         } else
00122             u = (time_t)-1;
00123         t = *anchor;
00124         do {
00125             t += delta;
00126         } while ((u == (time_t)-1 || t <= u)
00127                  && insert_update(t, sched, n, t0) < n - 1);
00128     } else if ((endp = parse_skip(&t, line, anchor))) {
00129         if (!time_ok(t, fname, lno))
00130             return -1;
00131         delete_update(t, sched, n);
00132     } else
00133         endp = line;
00134 
00135     bol = endp == line;
00136     while (isspace(*endp)) endp++;
00137     if (*endp) {
00138         if (bol)
00139             logerror("%s:%d: unintelligible\n", fname, lno);
00140         else
00141             logerror("%s:%d: trailing junk\n", fname, lno);
00142         return -1;
00143     }
00144 
00145     return 0;
00146 }
00147 
00148 /*
00149  * Complain and return zero when T is bad, else return non-zero.
00150  * FNAME and LNO file name and line number.
00151  */
00152 static int
00153 time_ok(time_t t, char *fname, int lno)
00154 {
00155     if (t == (time_t)-1) {
00156         logerror("%s:%d: time weird\n", fname, lno);
00157         return 0;
00158     }
00159     return 1;
00160 }
00161 
00162 /*
00163  * Parse a time from S into *T.
00164  * *ANCHOR is the base for anchor-relative time.
00165  * Return pointer to first character not parsed on success,
00166  * null pointer on failure.
00167  */
00168 static char *
00169 parse_time(time_t *t, char *s, time_t *anchor)
00170 {
00171     static char *fmt[] = {
00172         "%Y-%m-%d %H:%M ",      /* ISO 8601 */
00173         "%b %d %H:%M %Y ",      /* like ctime(): Dec 22 15:35 2006 */
00174         "%d %b %Y %H:%M ",      /* 22 Dec 2006 15:35 */
00175         "next %a %H:%M ",       /* next Fri 15:35 */
00176         "next %a ",             /* next Fri */
00177         NULL
00178     };
00179     char *p, *endp;
00180     int i;
00181     struct tm tm, nexttm;
00182 
00183     for (p = s; isspace(*(unsigned char *)p); ++p) ;
00184 
00185     for (i = 0; ; i++) {
00186         if (!fmt[i])
00187             return NULL;
00188         memset(&tm, 0, sizeof(tm));
00189         tm.tm_hour = -1;
00190         endp = strptime(p, fmt[i], &tm);
00191         if (endp)
00192             break;
00193     }
00194 
00195     if (tm.tm_mday == 0) {
00196         /* relative to anchor */
00197         nexttm = *localtime(anchor);
00198         if (tm.tm_hour >= 0) {
00199             /* got hour and minute */
00200             nexttm.tm_hour = tm.tm_hour;
00201             nexttm.tm_min = tm.tm_min;
00202             nexttm.tm_sec = 0;
00203         }
00204         nexttm.tm_mday += tm.tm_wday - nexttm.tm_wday;
00205         if (tm.tm_wday <= nexttm.tm_wday)
00206             nexttm.tm_mday += 7;
00207         tm = nexttm;
00208     }
00209 
00210     tm.tm_isdst = -1;
00211     *t = mktime(&tm);
00212     return endp;
00213 }
00214 
00215 /*
00216  * Parse an every clause from S into *SECS.
00217  * Return pointer to first character not parsed on success,
00218  * null pointer on failure.
00219  */
00220 static char *
00221 parse_every(time_t *secs, char *s)
00222 {
00223     int nch, delta;
00224 
00225     nch = -1;
00226     sscanf(s, " every %u hours%n", &delta, &nch);
00227     if (nch >= 0)
00228         delta *= 60;
00229     else
00230         sscanf(s, " every %u minutes%n", &delta, &nch);
00231     if (nch < 0)
00232         return NULL;    *secs = 60 * delta;
00233     return s + nch;
00234 }
00235 
00236 /*
00237  * Parse an until clause from S into *T.
00238  * *ANCHOR is the base for anchor-relative time.
00239  * Return pointer to first character not parsed on success,
00240  * null pointer on failure.
00241  */
00242 static char *
00243 parse_until(time_t *t, char *s, time_t *anchor)
00244 {
00245     int nch;
00246 
00247     nch = -1;
00248     sscanf(s, " until%n", &nch);
00249     if (nch < 0)
00250         return NULL;
00251     return parse_time(t, s + nch, anchor);
00252 }
00253 
00254 /*
00255  * Parse an skip clause from S into *T.
00256  * *ANCHOR is the base for anchor-relative time.
00257  * Return pointer to first character not parsed on success,
00258  * null pointer on failure.
00259  */
00260 static char *
00261 parse_skip(time_t *t, char *s, time_t *anchor)
00262 {
00263     int nch;
00264 
00265     nch = -1;
00266     sscanf(s, " skip%n", &nch);
00267     if (nch < 0)
00268         return NULL;
00269     return parse_time(t, s + nch, anchor);
00270 }
00271 
00272 /*
00273  * Return the index of the first update at or after T in SCHED[].
00274  */
00275 static int
00276 find_update(time_t t, time_t sched[])
00277 {
00278     int i;
00279 
00280     /* Could use binary search here, but it's hardly worth it */
00281     for (i = 0; sched[i] && t > sched[i]; i++) ;
00282     return i;
00283 }
00284 
00285 /*
00286  * Insert update at T into SCHED[].
00287  * SCHED[] holds the first N-1 updates after T0 in ascending order.
00288  * If T is before T0 or outside game_days/game_hours, return -1.
00289  * If there's no space for T in SCHED[], return N-1.
00290  * Else insert T into SCHED[] and return its index in SCHED[].
00291  */
00292 static int
00293 insert_update(time_t t, time_t sched[], int n, time_t t0)
00294 {
00295     int i;
00296 
00297     if (t <= t0 || !gamehours(t))
00298         return -1;
00299 
00300     i = find_update(t, sched);
00301     memmove(sched + i + 1, sched + i, (n - 1 - i) * sizeof(*sched));
00302     sched[i] = t;
00303     sched[n - 1] = 0;
00304     return i;
00305 }
00306 
00307 /*
00308  * Delete update T from SCHED[].
00309  * SCHED[] holds N-1 updates in ascending order.
00310  * Return the index of the first update after T in SCHED[].
00311  */
00312 static int
00313 delete_update(time_t t, time_t sched[], int n)
00314 {
00315     int i = find_update(t, sched);
00316     if (t == sched[i])
00317         memmove(sched + i, sched + i + 1,
00318                 (n - 1 - i) * sizeof(*sched));
00319     return i;
00320 }

Generated on Fri Mar 28 11:01:14 2008 for empserver by  doxygen 1.5.2