]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
The separation between parsing and semantic actions is awkward and
[empserver] / src / lib / common / xundump.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
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.
10  *
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.
15  *
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
19  *
20  *  ---
21  *
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.
25  *
26  *  ---
27  *
28  *  xundump.c: Load back xdump output
29  * 
30  *  Known contributors to this file:
31  *     Ron Koenderink, 2005
32  *     Markus Armbruster, 2005
33  */
34
35 /* FIXME normalize terminology: table/rows/columns or file/records/fields */
36
37 #include <config.h>
38
39 #include <stdio.h>
40 #include <stdlib.h>
41
42 #include <ctype.h>
43 #include <string.h>
44 #include <stdarg.h>
45 #include <time.h>
46
47 #include "file.h"
48 #include "match.h"
49 #include "nsc.h"
50 #include "optlist.h"
51 #include "prototypes.h"
52
53 static char *fname;
54 static int lineno;
55 static int human;
56 static int cur_type;
57 static void *cur_obj;
58 static int nxt_sel, nxt_idx;
59
60 static int gripe(char *, ...) ATTRIBUTE((format (printf, 1, 2)));
61 static int deffld(int, char *, int);
62 static void nxtfld(void);
63 static int setnum(int, double);
64 static int setstr(int, char *);
65 static int xunsymbol1(char *, struct symbol *, struct castr *, int);
66 static int setsym(int, char *);
67 static int mtsymset(int, long *);
68 static int add2symset(int, long *, char *);
69 static struct symbol *get_symtab(struct castr *);
70
71 static int
72 gripe(char *fmt, ...)
73 {
74     va_list ap;
75
76     fprintf(stderr, "%s:%d: ", fname, lineno);
77     va_start(ap, fmt);
78     vfprintf(stderr, fmt, ap);
79     va_end(ap);
80     putc('\n', stderr);
81
82     return -1;
83 }
84
85 static int
86 skipfs(FILE *fp)
87 {
88     int ch;
89
90     do {
91         ch = getc(fp);
92     } while (ch == ' ' || ch == '\t');
93
94     if (ch == '#') {
95         do {
96             ch = getc(fp);
97         } while (ch != EOF && ch != '\n');
98     }
99
100     return ch;
101 }
102
103 static int
104 getid(FILE *fp, char *buf)
105 {
106     int n;
107     if (fscanf(fp, "%1023[^#() \t\n]%n", buf, &n) != 1 || !isalpha(buf[0]))
108         return -1;
109     return n;
110 }
111
112 static char *
113 xuesc(char *buf)
114 {
115     char *src, *dst;
116     int octal_chr, n;
117
118     dst = buf;
119     src = buf;
120     while (*src) {
121         if (*src == '\\') {
122             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
123                 return NULL;
124             *dst++ = (char)octal_chr;
125             src += 3;
126         } else
127             *dst++ = *src++;
128     }
129     *dst = '\0';
130     return buf;
131 }
132
133 static int
134 xufldname(FILE *fp, int i)
135 {
136     int ch, idx;
137     char buf[1024];
138
139     ch = skipfs(fp);
140     switch (ch) {
141     case EOF:
142         return gripe("Unexpected EOF");
143     case '\n':
144         if (nxt_sel >= 0)
145             return gripe("Fields missing");
146         lineno++;
147         return 0;
148     default:
149         ungetc(ch, fp);
150         if (getid(fp, buf) < 0)
151             return gripe("Junk in field %d", i + 1);
152         ch = getc(fp);
153         if (ch != '(') {
154             ungetc(ch, fp);
155             return deffld(i, buf, -1);
156         }
157         ch = getc(fp);
158         ungetc(ch, fp);
159         if (isdigit(ch) || ch == '-' || ch == '+') {
160             if (fscanf(fp, "%d", &idx) != 1) {
161                 return gripe("Malformed number in index field %d", i + 1);
162             }
163         } else {
164             if (getid(fp, buf) < 0)
165                 return gripe("Malformed string in index field %d", i + 1);
166             return gripe("Symbolic index in field %d not yet implemented",
167                          i + 1);
168         }
169         ch = getc(fp);
170         if (ch != ')')
171             return gripe("Malformed index field %d", i + 1);
172         return deffld(i, buf, idx);
173     }
174 }
175
176 static int
177 xufld(FILE *fp, int i)
178 {
179     int ch;
180     char buf[1024];
181     double dbl;
182     long set;
183
184     ch = skipfs(fp);
185     switch (ch) {
186     case EOF:
187         return gripe("Unexpected EOF");
188     case '\n':
189         if (nxt_sel >= 0)
190             return gripe("Fields missing");
191         lineno++;
192         return 0;
193     case '+': case '-': case '.':
194     case '0': case '1': case '2': case '3': case '4':
195     case '5': case '6': case '7': case '8': case '9':
196         ungetc(ch, fp);
197         if (fscanf(fp, "%lg", &dbl) != 1)
198             return gripe("Malformed number in field %d", i + 1);
199         return setnum(i, dbl);
200     case '"':
201         ch = getc(fp);
202         if (ch == '"')
203             buf[0] = 0;
204         else {
205             ungetc(ch, fp);
206             if (fscanf(fp, "%1023[^\"\n]", buf) != 1 || getc(fp) != '"')
207                 return gripe("Malformed string in field %d", i + 1);
208             if (!xuesc(buf))
209                 return gripe("Invalid escape sequence in field %d",
210                              i + 1);
211         }
212         return setstr(i, buf);
213     case '(':
214         if (mtsymset(i, &set) < 0)
215             return -1;
216         for (;;) {
217             ch = skipfs(fp);
218             if (ch == EOF || ch == '\n')
219                 return gripe("Unmatched '(' in field %d", i + 1);
220             if (ch == ')')
221                 break;
222             ungetc(ch, fp);
223             if (getid(fp, buf) < 0)
224                 return gripe("Junk in field %d", i + 1);
225             if (add2symset(i, &set, buf) < 0)
226                 return -1;
227         }
228         return setnum(i, set);
229     default:
230         ungetc(ch, fp);
231         if (getid(fp, buf) < 0)
232             return gripe("Junk in field %d", i + 1);
233         if (!strcmp(buf, "nil"))
234             return setstr(i, NULL);
235         else
236             return setsym(i, buf);
237     }
238 }
239
240 static int
241 xuflds(FILE *fp, int (*parse)(FILE *, int))
242 {
243     int i, ch, res;
244
245     for (i = 0; ; i++) {
246         res = parse(fp, i);
247         if (res < 0)
248             return -1;
249         if (res == 0)
250             return i;
251         ch = getc(fp);
252         if (ch == '\n')
253             ungetc(ch, fp);
254         else if (ch != ' ' && ch != '\t')
255             return gripe("Bad field separator after field %d", i + 1);
256     }
257 }
258
259 static int
260 deffld(int fldidx, char *name, int idx)
261 {
262     struct castr *ca = ef_cadef(cur_type);
263     int res;
264
265     if (nxt_sel < 0)
266         return gripe("Too many fields, expected only %d", fldidx);
267
268     res = stmtch(name, ca, offsetof(struct castr, ca_name),
269                      sizeof(struct castr));
270     if (ca[nxt_sel].ca_type != NSC_STRINGY && ca[nxt_sel].ca_len != 0) {
271         if (res != nxt_sel || idx != nxt_idx)
272             return gripe("Expected %s(%d) in field %d",
273                          ca[nxt_sel].ca_name, nxt_idx, fldidx + 1);
274     } else {
275         if (res != nxt_sel || idx >= 0)
276             return gripe("Expected %s in field %d",
277                          ca[nxt_sel].ca_name, fldidx + 1);
278     }
279
280     nxtfld();
281     return 1;
282 }
283
284 static void
285 nxtfld(void)
286 {
287     struct castr *ca = ef_cadef(cur_type);
288     unsigned len = ca[nxt_sel].ca_type == NSC_STRINGY ? 0 : ca[nxt_sel].ca_len; /* FIXME ugly */
289
290     nxt_idx++;
291     if ((unsigned)nxt_idx >= len) {
292         nxt_idx = 0;
293         for (;;) {
294             nxt_sel++;
295             if (!ca[nxt_sel].ca_name) {
296                 nxt_sel = -1;
297                 break;
298             }
299             if (!(ca[nxt_sel].ca_flags & NSC_EXTRA))
300                 break;
301         }
302     }
303 }
304
305 static struct castr *
306 ca4fld(int fldidx)
307 {
308     struct castr *ca = ef_cadef(cur_type);
309     unsigned len = ca[nxt_sel].ca_type == NSC_STRINGY ? 0 : ca[nxt_sel].ca_len; /* FIXME ugly */
310
311     if (nxt_sel < 0 || CANT_HAPPEN(nxt_idx && (unsigned)nxt_idx >= len)) {
312         gripe("Too many fields, expected only %d", fldidx);
313         return NULL;
314     }
315     return &ca[nxt_sel];
316 }
317
318 static int
319 setnum(int fldidx, double dbl)
320 {
321     struct castr *ca = ca4fld(fldidx);
322     char *memb_ptr;
323     double old;
324
325     if (!ca)
326         return -1;
327
328     memb_ptr = cur_obj;
329     memb_ptr += ca->ca_off;
330     switch (ca->ca_type) {
331     case NSC_CHAR:
332     case NSC_TYPEID:
333         old = ((signed char *)memb_ptr)[nxt_idx];
334         ((signed char *)memb_ptr)[nxt_idx] = (signed char)dbl;
335         break;
336     case NSC_UCHAR:
337         old = ((unsigned char *)memb_ptr)[nxt_idx];
338         ((unsigned char *)memb_ptr)[nxt_idx] = (unsigned char)dbl;
339         break;
340     case NSC_SHORT:
341         old = ((short *)memb_ptr)[nxt_idx];
342         ((short *)memb_ptr)[nxt_idx] = (short)dbl;
343         break;
344     case NSC_USHORT:
345         old = ((unsigned short *)memb_ptr)[nxt_idx];
346         ((unsigned short *)memb_ptr)[nxt_idx] = (unsigned short)dbl;
347         break;
348     case NSC_INT:
349         old = ((int *)memb_ptr)[nxt_idx];
350         ((int *)memb_ptr)[nxt_idx] = (int)dbl;
351         break;
352     case NSC_LONG:
353         old = ((long *)memb_ptr)[nxt_idx];
354         ((long *)memb_ptr)[nxt_idx] = (long)dbl;
355         break;
356     case NSC_XCOORD:
357         old = ((coord *)memb_ptr)[nxt_idx];
358         ((coord *)memb_ptr)[nxt_idx] = XNORM((coord)dbl);
359         break;
360     case NSC_YCOORD:
361         old = ((coord *)memb_ptr)[nxt_idx];
362         ((coord *)memb_ptr)[nxt_idx] = YNORM((coord)dbl);
363         break;
364     case NSC_FLOAT:
365         old = ((float *)memb_ptr)[nxt_idx];
366         ((float *)memb_ptr)[nxt_idx] = (float)dbl;
367         break;
368     case NSC_DOUBLE:
369         old = ((double *)memb_ptr)[nxt_idx];
370         ((double *)memb_ptr)[nxt_idx] = dbl;
371         break;
372     case NSC_TIME:
373         old = ((time_t *)memb_ptr)[nxt_idx];
374         ((time_t *)memb_ptr)[nxt_idx] = (time_t)dbl;
375         break;
376     default:
377         return gripe("Field %d doesn't take numbers", fldidx + 1);
378     }
379
380     if ((ca->ca_flags & NSC_CONST) && old != dbl)
381         return gripe("Value for field %d must be %g", fldidx + 1, old);
382
383     nxtfld();
384     return 1;
385 }
386
387 static int
388 setstr(int fldidx, char *str)
389 {
390     struct castr *ca = ca4fld(fldidx);
391     char *memb_ptr, *old;
392
393     if (!ca)
394         return -1;
395
396     memb_ptr = cur_obj;
397     memb_ptr += ca->ca_off;
398     switch (ca->ca_type) {
399     case NSC_STRING:
400         old = ((char **)memb_ptr)[nxt_idx];
401         if (!(ca->ca_flags & NSC_CONST))
402             ((char **)memb_ptr)[nxt_idx] = str ? strdup(str) : NULL;
403         break;
404     case NSC_STRINGY:
405         CANT_HAPPEN(nxt_idx);
406         if (!str)
407             return gripe("Field doesn't take nil");
408         if (strlen(str) > ca->ca_len)
409             return gripe("Field %d takes at most %d characters",
410                          fldidx + 1, ca->ca_len);
411         old = memb_ptr;
412         if (!(ca->ca_flags & NSC_CONST))
413             strncpy(memb_ptr, str, ca->ca_len);
414         break;
415     default:
416         return gripe("Field %d doesn't take strings", fldidx + 1);
417     }
418
419     if ((ca->ca_flags & NSC_CONST) && strcmp(old, str))
420         return gripe("Value for field %d must be %s", fldidx + 1, old);
421
422     nxtfld();
423     return 1;
424 }
425
426 static int
427 xunsymbol1(char *id, struct symbol *symtab, struct castr *ca, int n)
428 {
429     int i = stmtch(id, symtab, offsetof(struct symbol, name),
430                    sizeof(struct symbol));
431     if (i < 0)
432         return gripe("%s %s symbol `%s' in field %d",
433                      i == M_NOTUNIQUE ? "Ambiguous" : "Unknown",
434                      ca->ca_name, id, n);
435     return i;
436 }
437
438 static int
439 setsym(int fldidx, char *sym)
440 {
441     struct castr *ca = ca4fld(fldidx);
442     struct symbol *symtab;
443     int i;
444
445     if (!ca)
446         return -1;
447
448     symtab = get_symtab(ca);
449     if (!symtab || (ca->ca_flags & NSC_BITS))
450         return gripe("Field %d doesn't take symbols", fldidx + 1);
451
452     i = xunsymbol1(sym, symtab, ca, fldidx);
453     if (i < 0)
454         return -1;
455     return setnum(fldidx, symtab[i].value);
456 }
457
458 static int
459 has_const(struct castr ca[])
460 {
461     int i;
462
463     for (i = 0; ca[i].ca_name; i++) {
464         if (ca[i].ca_flags & NSC_CONST)
465             return 1;
466     }
467     return 0;
468 }
469
470 static int
471 mtsymset(int fldidx, long *set)
472 {
473     struct castr *ca = ca4fld(fldidx);
474     struct symbol *symtab;
475     int i;
476
477     if (!ca)
478         return -1;
479
480     symtab = get_symtab(ca);
481     if (!symtab || !(ca->ca_flags & NSC_BITS)) {
482         return gripe("Field %d doesn't take symbol sets", fldidx + 1);
483     }
484     *set = 0;
485     return 0;
486 }
487
488 static int
489 add2symset(int fldidx, long *set, char *sym)
490 {
491     struct castr *ca = ca4fld(fldidx);
492     struct symbol *symtab;
493     int i;
494
495     if (!ca)
496         return -1;
497
498     symtab = get_symtab(ca);
499     i = xunsymbol1(sym, symtab, ca, fldidx);
500     if (i < 0)
501         return -1;
502     *set |= symtab[i].value;
503     return 0;
504 }
505
506 static struct symbol *
507 get_symtab(struct castr *ca)
508 {
509     int symtype = ca->ca_table;
510     struct symbol *symtab;
511
512     if (symtype == EF_BAD || ef_cadef(symtype) != symbol_ca)
513         return NULL;
514
515     symtab = ef_ptr(symtype, 0);
516     CANT_HAPPEN(!symtab);
517     return symtab;
518 }
519
520 static int
521 xuheader(FILE *fp, int expected_table)
522 {
523     char name[64];
524     int res, ch;
525     int type;
526
527     while ((ch = skipfs(fp)) == '\n')
528         lineno++;
529     if (ch == EOF && expected_table == EF_BAD)
530         return -1;
531     ungetc(ch, fp);
532
533     human = ch == 'c';
534     res = -1;
535     if ((human
536          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
537          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
538                   name, &res) != 1) || res < 0)
539         return gripe("Expected xdump header");
540
541     type = ef_byname(name);
542     if (type < 0)
543         return gripe("Unknown table `%s'", name);
544     if (expected_table != EF_BAD && expected_table != type)
545         return gripe("Expected table `%s', not `%s'",
546                      ef_nameof(expected_table), name);
547
548     if (CANT_HAPPEN(!(ef_flags(type) & EFF_MEM)))
549         return -1;
550
551     if (skipfs(fp) != '\n')
552         return gripe("Junk after xdump header");
553     lineno++;
554
555     return type;
556 }
557
558 static int
559 xutrailer(FILE *fp, int type, int row)
560 {
561     int rows, ch, res;
562
563     res = -1;
564     if (human) {
565         if (fscanf(fp, "config%n",  &res) != 0 || res < 0)
566             return gripe("Malformed table footer");
567     } else {
568         if (fscanf(fp, "%d", &rows) != 1)
569             return gripe("Malformed table footer");
570         if (row != rows)
571             return gripe("Read %d rows, which doesn't match footer "
572                          "%d rows", row, rows);
573     }
574     if (skipfs(fp) != '\n')
575         return gripe("Junk after table footer");
576     lineno++;
577
578     return 0;
579 }
580
581 int
582 xundump(FILE *fp, char *file, int expected_table)
583 {
584     struct empfile *ep;
585     int type, fixed_rows, need_sentinel;
586     int row, res, ch;
587
588     if (fname != file) {
589         fname = file;
590         lineno = 1;
591     }
592
593     if ((type = xuheader(fp, expected_table)) < 0)
594         return -1;
595
596     ep = &empfile[type];
597     fixed_rows = has_const(ef_cadef(type));
598     need_sentinel = !EF_IS_GAME_STATE(type);
599
600     cur_type = type;
601     if (human) {
602         nxt_sel = nxt_idx = 0;
603         if (xuflds(fp, xufldname) < 0)
604             return -1;
605     }
606
607     for (row = 0;; ++row) {
608         ch = skipfs(fp);
609         if (ch == '/')
610             break;
611         ungetc(ch, fp);
612         /* TODO ability to skip records */
613         if (!fixed_rows) {
614             if (row >= ep->csize - !!need_sentinel)
615                 /* TODO grow cache unless EFF_STATIC */
616                 return gripe("Too many rows for table %s", ef_nameof(type));
617             if (row >= ep->cids)
618                 /* TODO grow file */
619                 ep->cids = ep->fids = row + 1;
620         }
621         cur_obj = ef_ptr(type, row);
622         if (!cur_obj)
623             return gripe("Too many rows for table %s", ef_nameof(type));
624         nxt_sel = nxt_idx = 0;
625         res = xuflds(fp, xufld);
626         if (res < 0)
627             return -1;
628     }
629     if (row != ep->fids) {
630         if (fixed_rows)
631             return gripe("Table %s requires %d rows, got %d",
632                          ef_nameof(type), ep->fids, row);
633         else {
634             ep->cids = ep->fids = row;
635             if (EF_IS_GAME_STATE(type) && row != ep->csize)
636                 /* TODO truncate file */
637                 gripe("Warning: should resize table %s from %d to %d, not implemented",
638                       ef_nameof(type), ep->csize, row);
639         }
640     }
641
642     if (need_sentinel) {
643         if (CANT_HAPPEN(row >= ep->csize))
644             return gripe("No space for sentinel");
645         memset(ep->cache + ep->size * row, 0, ep->size);
646     }
647
648     if (xutrailer(fp, type, row) < 0)
649         return -1;
650     
651     return type;
652 }