]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
Update copyright notice.
[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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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 "prototypes.h"
48 #include "file.h"
49 #include "nsc.h"
50 #include "match.h"
51
52 #define MAX_NUM_COLUMNS 256
53
54 static char *fname = "";
55 static int lineno = 0;
56 static int human;
57
58 /*
59  * TODO
60  * This structure could be replaced with struct valstr.
61  */
62 enum enum_value {
63     VAL_NOTUSED,
64     VAL_STRING,
65     VAL_SYMBOL,
66     VAL_SYMBOL_SET,
67     VAL_DOUBLE
68 };
69
70 struct value {
71     enum enum_value v_type;
72     union {
73         char *v_string;
74         double v_double;
75     } v_field;
76 };
77
78 static int gripe(char *fmt, ...) ATTRIBUTE((format (printf, 1, 2)));
79
80 static int
81 gripe(char *fmt, ...)
82 {
83     va_list ap;
84
85     fprintf(stderr, "%s:%d: ", fname, lineno);
86     va_start(ap, fmt);
87     vfprintf(stderr, fmt, ap);
88     va_end(ap);
89     putc('\n', stderr);
90
91     return -1;
92 }
93
94 static int
95 skipfs(FILE *fp)
96 {
97     int ch;
98
99     do {
100         ch = getc(fp);
101     } while (ch == ' ' || ch == '\t');
102
103     if (ch == '#') {
104         do {
105             ch = getc(fp);
106         } while (ch != EOF && ch != '\n');
107     }
108
109     return ch;
110 }
111
112 static int
113 getid(FILE *fp, char *buf)
114 {
115     int n;
116     if (fscanf(fp, "%1023[^#() \t\n]%n", buf, &n) != 1 || !isalpha(buf[0]))
117         return -1;
118     return n;
119 }
120
121 static char *
122 xuesc(char *buf)
123 {
124     char *src, *dst;
125     int octal_chr, n;
126
127     dst = buf;
128     src = buf;
129     while (*src) {
130         if (*src == '\\') {
131             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
132                 return NULL;
133             *dst++ = (char)octal_chr;
134             src += 3;
135         } else
136             *dst++ = *src++;
137     }
138     *dst = '\0';
139     return buf;
140 }
141
142 static int
143 xuflds(FILE *fp, struct value values[])
144 {
145     int i, ch;
146     char buf[1024];
147     char *p;
148     int len, l1;
149
150     for (i = 0; ; i++) {
151         values[i].v_type = VAL_NOTUSED;
152         if (i >= MAX_NUM_COLUMNS)
153             return gripe("Too many columns");
154
155         ch = skipfs(fp);
156         switch (ch) {
157         case EOF:
158             return gripe("Unexpected EOF");
159         case '\n':
160             return i;
161         case '+': case '-': case '.':
162         case '0': case '1': case '2': case '3': case '4':
163         case '5': case '6': case '7': case '8': case '9':
164             ungetc(ch, fp);
165             if (fscanf(fp, "%lg", &values[i].v_field.v_double) != 1)
166                 return gripe("Malformed number in field %d", i + 1);
167             values[i].v_type = VAL_DOUBLE;
168             break;
169         case '"':
170             ch = getc(fp);
171             if (ch == '"')
172                 buf[0] = 0;
173             else {
174                 ungetc(ch, fp);
175                 if (fscanf(fp, "%1023[^\"\n]", buf) != 1 || getc(fp) != '"')
176                     return gripe("Malformed string in field %d", i + 1);
177                 if (!xuesc(buf))
178                     return gripe("Invalid escape sequence in field %d",
179                                  i + 1);
180             }
181             values[i].v_type = VAL_STRING;
182             values[i].v_field.v_string = strdup(buf);
183             break;
184         case '(':
185             p = strdup("");
186             len = 0;
187             for (;;) {
188                 ch = skipfs(fp);
189                 if (ch == EOF || ch == '\n')
190                     return gripe("Unmatched '(' in field %d", i + 1);
191                 if (ch == ')')
192                     break;
193                 ungetc(ch, fp);
194                 l1 = getid(fp, buf);
195                 if (l1 < 0)
196                     return gripe("Junk in field %d", i + 1);
197                 p = realloc(p, len + l1 + 2);
198                 strcpy(p + len, buf);
199                 strcpy(p + len + l1, " ");
200                 len += l1 + 1;
201             }
202             if (len)
203                 p[len - 1] = 0;
204             values[i].v_type = VAL_SYMBOL_SET;
205             values[i].v_field.v_string = p;
206             break;
207         default:
208             ungetc(ch, fp);
209             if (getid(fp, buf) < 0) {
210                 return gripe("Junk in field %d", i + 1);
211             }
212             if (!strcmp(buf, "nil")) {
213                 values[i].v_type = VAL_STRING;
214                 values[i].v_field.v_string = NULL;
215             }
216             else {
217                 values[i].v_type = VAL_SYMBOL;
218                 values[i].v_field.v_string = strdup(buf);
219             }
220         }
221         ch = getc(fp);
222         if (ch == '\n')
223             ungetc(ch, fp);
224         else if (ch != ' ' && ch != '\t')
225             return gripe("Bad field separator after field %d", i + 1);
226     }
227 }
228
229 static void
230 freeflds(struct value values[])
231 {
232     struct value *vp;
233
234     for (vp = values; vp->v_type != VAL_NOTUSED; vp++) {
235         if (vp->v_type != VAL_DOUBLE)
236             free(vp->v_field.v_string);
237     }
238 }
239
240 static int
241 xunsymbol1(char *id, struct symbol *symtab, struct castr *ca, int n)
242 {
243     int i = stmtch(id, symtab, offsetof(struct symbol, name),
244                    sizeof(struct symbol));
245     if (i < 0)
246         return gripe("%s %s symbol `%s' in field %d",
247                      i == M_NOTUNIQUE ? "Ambiguous" : "Unknown",
248                      ca->ca_name, id, n);
249     return i;
250 }
251
252 static int
253 xunsymbol(struct castr *ca, struct value *vp, int n)
254 {
255     struct symbol *symtab = (struct symbol *)empfile[ca->ca_table].cache;
256     char *buf = vp->v_field.v_string;
257     int i;
258     int value;
259     char *token;
260
261     if (vp->v_type == VAL_SYMBOL_SET) {
262         if (!(ca->ca_flags & NSC_BITS) || ca->ca_table == EF_BAD)
263             return gripe("%s doesn't take a symbol set in field %d",
264                          ca->ca_name, n);
265         value = 0;
266         for (token = strtok(buf, " "); token; token = strtok(NULL, " ")) {
267             i = xunsymbol1(token, symtab, ca, n);
268             if (i < 0)
269                 return -1;
270             value |= symtab[i].value;
271         }
272     } else if (vp->v_type == VAL_SYMBOL) {
273         if ((ca->ca_flags & NSC_BITS) || ca->ca_table == EF_BAD)
274             return gripe("%s doesn't take a symbol in field %d",
275                          ca->ca_name, n);
276         i = xunsymbol1(buf, symtab, ca, n);
277         if (i < 0)
278             return -1;
279         value = symtab[i].value;
280     } else
281         return 0;
282
283     vp->v_type = VAL_DOUBLE;
284     vp->v_field.v_double = value;
285     return 0;
286 }
287
288 static int
289 has_const(struct castr ca[])
290 {
291     int i;
292
293     for (i = 0; ca[i].ca_name; i++) {
294         if (ca[i].ca_flags & NSC_CONST)
295             return 1;
296     }
297     return 0;
298 }
299
300 static void
301 xuinitrow(int type, int row)
302 {
303     struct empfile *ep = &empfile[type];
304     char *ptr = ep->cache + ep->size * row;
305
306     memset(ptr, 0, ep->size);
307
308     if (ep->init)
309         ep->init(row, ptr);
310 }
311
312 static int
313 xuloadrow(int type, int row, struct value values[])
314 {
315     int i,j,k;
316     struct empfile *ep = &empfile[type];
317     char *ptr = ep->cache + ep->size * row;
318     struct castr *ca = ep->cadef;
319     void *row_ref;
320
321     i = 0;
322     j = 0;
323     while (ca[i].ca_type != NSC_NOTYPE &&
324            values[j].v_type != VAL_NOTUSED) {
325         if (ca[i].ca_flags & NSC_EXTRA) {
326             i++;
327             continue;
328         }
329         row_ref = (char *)ptr + ca[i].ca_off;
330         k = 0;
331         do {
332             /*
333              * TODO
334              * factor out NSC_CONST comparsion
335              */
336             switch (values[j].v_type) {
337             case VAL_SYMBOL:
338             case VAL_SYMBOL_SET:
339                 if (xunsymbol(&ca[i], &values[j], j) < 0)
340                     return -1;
341                 /* fall through */
342             case VAL_DOUBLE:
343                 switch (ca[i].ca_type) {
344                 case NSC_INT:
345                     if (ca[i].ca_flags & NSC_CONST) {
346                         if (((int *)row_ref)[k] != (int)
347                              values[j].v_field.v_double)
348                             gripe("Field %s must be same, "
349                                 "read %d != expected %d",
350                                 ca[i].ca_name,
351                                 ((int *)row_ref)[k],
352                                 (int)values[j].v_field.v_double);
353
354                     } else
355                         ((int *)row_ref)[k] =
356                             (int)values[j].v_field.v_double;
357                     break;
358                 case NSC_LONG:
359                     if (ca[i].ca_flags & NSC_CONST) {
360                         if (((long *)row_ref)[k] != (long)
361                              values[j].v_field.v_double)
362                             gripe("Field %s must be same, "
363                                 "read %ld != expected %ld",
364                                 ca[i].ca_name,
365                                 ((long *)row_ref)[k],
366                                 (long)values[j].v_field.v_double);
367                     } else
368                         ((long *)row_ref)[k] = (long)
369                             values[j].v_field.v_double;
370                     break;
371                 case NSC_USHORT:
372                     if (ca[i].ca_flags & NSC_CONST) {
373                         if (((unsigned short *)row_ref)[k] !=
374                              (unsigned short)values[j].v_field.v_double)
375                             gripe("Field %s must be same, "
376                                 "read %d != expected %d",
377                                 ca[i].ca_name,
378                                 ((unsigned short *)row_ref)[k],
379                                 (unsigned short)values[j].v_field.v_double);
380                     } else
381                         ((unsigned short *)row_ref)[k] = (unsigned short)
382                             values[j].v_field.v_double;
383                     break;
384                 case NSC_UCHAR:
385                     if (ca[i].ca_flags & NSC_CONST) {
386                         if (((unsigned char *)row_ref)[k] != (unsigned char)
387                              values[j].v_field.v_double)
388                             gripe("Field %s must be same, "
389                                 "read %d != expected %d",
390                                 ca[i].ca_name,
391                                 ((unsigned char *)row_ref)[k],
392                                 (unsigned char)values[j].v_field.v_double);
393                     } else
394                         ((unsigned char *)row_ref)[k] = (unsigned char)
395                             values[j].v_field.v_double;
396                     break;
397                 case NSC_FLOAT:
398                     if (ca[i].ca_flags & NSC_CONST) {
399                         if (((float *)row_ref)[k] != (float)
400                              values[j].v_field.v_double)
401                             gripe("Field %s must be same, "
402                                 "read %g != expected %g",
403                                 ca[i].ca_name,
404                                 ((float *)row_ref)[k],
405                                 (float)values[j].v_field.v_double);
406                     } else
407                         ((float *)row_ref)[k] = (float)
408                             values[j].v_field.v_double;
409                     break;
410                 case NSC_STRING:
411                     return gripe("Field %s is a string type, "
412                         "but %lg was read which is a number",
413                         ca[i].ca_name, values[j].v_field.v_double);
414                 default:
415                     return gripe("Field %s's type %d is not supported",
416                         ca[i].ca_name, ca[i].ca_type);
417                 }
418                 break;
419             case VAL_STRING:
420                 switch(ca[i].ca_type) {
421                 case NSC_STRING:
422                     if (ca[i].ca_flags & NSC_CONST) {
423                         if (strcmp(((char **)row_ref)[k],
424                                    values[j].v_field.v_string) != 0)
425                             gripe("Field %s must be same, "
426                                 "read %s != expected %s",
427                                 ca[i].ca_name,
428                                 ((char **)row_ref)[k],
429                                 values[j].v_field.v_string);
430                     } else
431                         ((char **)row_ref)[k]
432                             = strdup(values[j].v_field.v_string);
433                     break;
434                 case NSC_INT:
435                 case NSC_LONG:
436                 case NSC_USHORT:
437                 case NSC_UCHAR:
438                 case NSC_FLOAT:
439                     return gripe("Field %s is a number type %d, "
440                         "but %s was read which is a string",
441                         ca[i].ca_name, ca[i].ca_type,
442                         values[j].v_field.v_string);
443                 default:
444                     return gripe("Field %s's type %d is not supported",
445                             ca[i].ca_name, ca[i].ca_type);
446                 }
447                 break;
448             case VAL_NOTUSED:
449                 return gripe("Missing column %s in file", ca[i].ca_name);
450             default:
451                 return gripe("Unknown value type %d", values[j].v_type);
452             }
453             k++;
454             j++;
455         } while (k < ca[i].ca_len);
456         i++;
457     }
458     if (ca[i].ca_type != NSC_NOTYPE)
459         return gripe("Missing column %s in file", ca[i].ca_name);
460     switch  (values[j].v_type) {
461     case VAL_NOTUSED:
462         break;
463     case VAL_STRING:
464     case VAL_SYMBOL:
465     case VAL_SYMBOL_SET:
466         return gripe("Extra junk after the last column, read %s",
467             values[j].v_field.v_string);
468     case VAL_DOUBLE:
469         return gripe("Extra junk after the last column, read %lg",
470             values[j].v_field.v_double);
471     default:
472         return gripe("Extra junk after the last column, "
473             "unknown value type %d", values[j].v_type);
474     }
475     return 0;
476 }
477
478 static int
479 xuheader(FILE *fp, int expected_table, struct value values[])
480 {
481     char name[64];
482     struct empfile *ep;
483     int res, ch;
484     int type;
485
486     while ((ch = skipfs(fp)) == '\n')
487         lineno++;
488     if (ch == EOF && expected_table == EF_BAD)
489         return -1;
490     ungetc(ch, fp);
491
492     human = ch == 'c';
493     res = -1;
494     if ((human
495          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
496          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
497                   name, &res) != 1) || res < 0)
498         return gripe("Expected xdump header");
499
500     if (skipfs(fp) != '\n')
501         return gripe("Junk after xdump header");
502
503     type = ef_byname(name);
504     if (type < 0)
505         return gripe("Unknown table `%s'", name);
506     ep = &empfile[type];
507     if (CANT_HAPPEN(!(ep->flags & EFF_MEM)))
508         return -1;              /* not implemented */
509
510     if (expected_table != EF_BAD && expected_table != type)
511         return gripe("Expected table `%s', not `%s'",
512                      ef_nameof(expected_table), name);
513     return type;
514 }
515
516 static int
517 xucolumnheader(FILE *fp, int type)
518 {
519     char ch;
520
521     if (!human)
522         return 0;
523
524     while ((ch = skipfs(fp)) == '\n');
525     ungetc(ch, fp);
526
527     /* FIXME parse column header */
528     if (fscanf(fp, "%*[^\n]\n") == -1)
529         return gripe("Invalid Column Header for table %s",
530             ef_nameof(type));
531     lineno++;
532
533     return 0;
534 }
535
536 static int
537 xutrailer(FILE *fp, int type, int row)
538 {
539     int rows, ch, res;
540
541     res = -1;
542     if (human) {
543         if (fscanf(fp, "config%n",  &res) != 0) {
544             return gripe("Malformed table footer");
545         }
546     }
547     ch = skipfs(fp);
548     if (!isdigit(ch)) {
549         if (ch != '\n' || !human)
550             return gripe("Malformed table footer");
551     } else {
552         ungetc(ch, fp);
553         if (fscanf(fp, "%d", &rows) != 1)
554             return gripe("Malformed table footer");
555         if (row != rows)
556             return gripe("Read %d rows, which doesn't match footer "
557                          "%d rows", row, rows);
558     }
559     if (skipfs(fp) != '\n')
560         return gripe("Junk after table footer");
561
562     while ((ch = skipfs(fp)) == '\n') ;
563     ungetc(ch, fp);
564
565     return 0;
566 }
567
568 int
569 xundump(FILE *fp, char *file, int expected_table)
570 {
571     int row, res, ch;
572     struct empfile *ep;
573     int need_sentinel;
574     struct value values[MAX_NUM_COLUMNS + 1];
575     int type;
576     int fixed_rows;
577
578     if (strcmp(fname, file) != 0) {
579         fname = file;
580         lineno = 1;
581     } else
582         lineno++;
583
584     if ((type = xuheader(fp, expected_table, values)) == -1)
585         return -1;
586     
587     ep = &empfile[type];
588     fixed_rows = has_const(ef_cadef(type));
589     need_sentinel = !fixed_rows; /* FIXME only approximation */
590
591     if (xucolumnheader(fp, type) == -1)
592         return -1;
593
594     row = 0;
595     for (;;) {
596         lineno++;
597         ch = skipfs(fp);
598         if (ch == '/')
599             break;
600         ungetc(ch, fp);
601         /*
602          * TODO
603          * Add column count check to the return value of xuflds()
604          */
605         res = xuflds(fp, values);
606         if (res > 0 && row >= ep->csize - 1) {
607             /* TODO grow cache unless EFF_STATIC */
608             gripe("Too many rows for table %s", ef_nameof(type));
609             res = -1;
610         }
611         if (res > 0) {
612             if (!fixed_rows)
613                 xuinitrow(type, row);
614             res = xuloadrow(type, row, values);
615             row++;
616         }
617         freeflds(values);
618         if (res < 0)
619             return -1;
620     }
621     if (fixed_rows && row != ep->csize -1)
622         return gripe("Table %s requires %d rows, got %d",
623                      ef_nameof(type), ep->csize - 1, row);
624
625     if (need_sentinel)
626         xuinitrow(type, row);
627
628     if (xutrailer(fp, type, row) == -1)
629         return -1;
630     
631     ep->fids = ep->cids = row;
632     return type;
633 }