]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
COPYING duplicates information from README. Remove. Move GPL from
[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 "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 (ca->ca_table == EF_BAD || ef_cadef(ca->ca_table) != symbol_ca)
262         return gripe("%s doesn't take a symbol or symbol set in field %d",
263                      ca->ca_name, n);
264
265     if (vp->v_type == VAL_SYMBOL_SET) {
266         if (!(ca->ca_flags & NSC_BITS))
267             return gripe("%s doesn't take a symbol set in field %d",
268                          ca->ca_name, n);
269         value = 0;
270         for (token = strtok(buf, " "); token; token = strtok(NULL, " ")) {
271             i = xunsymbol1(token, symtab, ca, n);
272             if (i < 0)
273                 return -1;
274             value |= symtab[i].value;
275         }
276     } else if (vp->v_type == VAL_SYMBOL) {
277         if (ca->ca_flags & NSC_BITS)
278             return gripe("%s doesn't take a symbol in field %d",
279                          ca->ca_name, n);
280         i = xunsymbol1(buf, symtab, ca, n);
281         if (i < 0)
282             return -1;
283         value = symtab[i].value;
284     } else
285         return 0;
286
287     vp->v_type = VAL_DOUBLE;
288     vp->v_field.v_double = value;
289     return 0;
290 }
291
292 static int
293 has_const(struct castr ca[])
294 {
295     int i;
296
297     for (i = 0; ca[i].ca_name; i++) {
298         if (ca[i].ca_flags & NSC_CONST)
299             return 1;
300     }
301     return 0;
302 }
303
304 static void
305 xuinitrow(int type, int row)
306 {
307     struct empfile *ep = &empfile[type];
308     char *ptr = ep->cache + ep->size * row;
309
310     memset(ptr, 0, ep->size);
311
312     if (ep->init)
313         ep->init(row, ptr);
314 }
315
316 static int
317 xuloadrow(int type, int row, struct value values[])
318 {
319     int i,j,k;
320     struct empfile *ep = &empfile[type];
321     char *ptr = ep->cache + ep->size * row;
322     struct castr *ca = ep->cadef;
323     void *row_ref;
324
325     i = 0;
326     j = 0;
327     while (ca[i].ca_type != NSC_NOTYPE &&
328            values[j].v_type != VAL_NOTUSED) {
329         if (ca[i].ca_flags & NSC_EXTRA) {
330             i++;
331             continue;
332         }
333         row_ref = (char *)ptr + ca[i].ca_off;
334         k = 0;
335         do {
336             /*
337              * TODO
338              * factor out NSC_CONST comparsion
339              */
340             switch (values[j].v_type) {
341             case VAL_SYMBOL:
342             case VAL_SYMBOL_SET:
343                 if (xunsymbol(&ca[i], &values[j], j) < 0)
344                     return -1;
345                 /* fall through */
346             case VAL_DOUBLE:
347                 switch (ca[i].ca_type) {
348                 case NSC_INT:
349                     if (ca[i].ca_flags & NSC_CONST) {
350                         if (((int *)row_ref)[k] != (int)
351                              values[j].v_field.v_double)
352                             gripe("Field %s must be same, "
353                                 "read %d != expected %d",
354                                 ca[i].ca_name,
355                                 ((int *)row_ref)[k],
356                                 (int)values[j].v_field.v_double);
357
358                     } else
359                         ((int *)row_ref)[k] =
360                             (int)values[j].v_field.v_double;
361                     break;
362                 case NSC_LONG:
363                     if (ca[i].ca_flags & NSC_CONST) {
364                         if (((long *)row_ref)[k] != (long)
365                              values[j].v_field.v_double)
366                             gripe("Field %s must be same, "
367                                 "read %ld != expected %ld",
368                                 ca[i].ca_name,
369                                 ((long *)row_ref)[k],
370                                 (long)values[j].v_field.v_double);
371                     } else
372                         ((long *)row_ref)[k] = (long)
373                             values[j].v_field.v_double;
374                     break;
375                 case NSC_SHORT:
376                     if (ca[i].ca_flags & NSC_CONST) {
377                         if (((short *)row_ref)[k] !=
378                              (short)values[j].v_field.v_double)
379                             gripe("Field %s must be same, "
380                                 "read %d != expected %d",
381                                 ca[i].ca_name,
382                                 ((short *)row_ref)[k],
383                                 (short)values[j].v_field.v_double);
384                     } else
385                         ((short *)row_ref)[k] = (short)
386                             values[j].v_field.v_double;
387                     break;
388                 case NSC_USHORT:
389                     if (ca[i].ca_flags & NSC_CONST) {
390                         if (((unsigned short *)row_ref)[k] !=
391                              (unsigned short)values[j].v_field.v_double)
392                             gripe("Field %s must be same, "
393                                 "read %d != expected %d",
394                                 ca[i].ca_name,
395                                 ((unsigned short *)row_ref)[k],
396                                 (unsigned short)values[j].v_field.v_double);
397                     } else
398                         ((unsigned short *)row_ref)[k] = (unsigned short)
399                             values[j].v_field.v_double;
400                     break;
401                 case NSC_UCHAR:
402                     if (ca[i].ca_flags & NSC_CONST) {
403                         if (((unsigned char *)row_ref)[k] != (unsigned char)
404                              values[j].v_field.v_double)
405                             gripe("Field %s must be same, "
406                                 "read %d != expected %d",
407                                 ca[i].ca_name,
408                                 ((unsigned char *)row_ref)[k],
409                                 (unsigned char)values[j].v_field.v_double);
410                     } else
411                         ((unsigned char *)row_ref)[k] = (unsigned char)
412                             values[j].v_field.v_double;
413                     break;
414                 case NSC_FLOAT:
415                     if (ca[i].ca_flags & NSC_CONST) {
416                         if (((float *)row_ref)[k] != (float)
417                              values[j].v_field.v_double)
418                             gripe("Field %s must be same, "
419                                 "read %g != expected %g",
420                                 ca[i].ca_name,
421                                 ((float *)row_ref)[k],
422                                 (float)values[j].v_field.v_double);
423                     } else
424                         ((float *)row_ref)[k] = (float)
425                             values[j].v_field.v_double;
426                     break;
427                 case NSC_STRING:
428                     return gripe("Field %s is a string type, "
429                         "but %lg was read which is a number",
430                         ca[i].ca_name, values[j].v_field.v_double);
431                 default:
432                     return gripe("Field %s's type %d is not supported",
433                         ca[i].ca_name, ca[i].ca_type);
434                 }
435                 break;
436             case VAL_STRING:
437                 switch(ca[i].ca_type) {
438                 case NSC_STRING:
439                     if (ca[i].ca_flags & NSC_CONST) {
440                         if (strcmp(((char **)row_ref)[k],
441                                    values[j].v_field.v_string) != 0)
442                             gripe("Field %s must be same, "
443                                 "read %s != expected %s",
444                                 ca[i].ca_name,
445                                 ((char **)row_ref)[k],
446                                 values[j].v_field.v_string);
447                     } else
448                         ((char **)row_ref)[k]
449                             = strdup(values[j].v_field.v_string);
450                     break;
451                 case NSC_INT:
452                 case NSC_LONG:
453                 case NSC_SHORT:
454                 case NSC_USHORT:
455                 case NSC_UCHAR:
456                 case NSC_FLOAT:
457                     return gripe("Field %s is a number type %d, "
458                         "but %s was read which is a string",
459                         ca[i].ca_name, ca[i].ca_type,
460                         values[j].v_field.v_string);
461                 default:
462                     return gripe("Field %s's type %d is not supported",
463                             ca[i].ca_name, ca[i].ca_type);
464                 }
465                 break;
466             case VAL_NOTUSED:
467                 return gripe("Missing column %s in file", ca[i].ca_name);
468             default:
469                 return gripe("Unknown value type %d", values[j].v_type);
470             }
471             k++;
472             j++;
473         } while (k < ca[i].ca_len);
474         i++;
475     }
476     if (ca[i].ca_type != NSC_NOTYPE)
477         return gripe("Missing column %s in file", ca[i].ca_name);
478     switch  (values[j].v_type) {
479     case VAL_NOTUSED:
480         break;
481     case VAL_STRING:
482     case VAL_SYMBOL:
483     case VAL_SYMBOL_SET:
484         return gripe("Extra junk after the last column, read %s",
485             values[j].v_field.v_string);
486     case VAL_DOUBLE:
487         return gripe("Extra junk after the last column, read %lg",
488             values[j].v_field.v_double);
489     default:
490         return gripe("Extra junk after the last column, "
491             "unknown value type %d", values[j].v_type);
492     }
493     return 0;
494 }
495
496 static int
497 xuheader(FILE *fp, int expected_table, struct value values[])
498 {
499     char name[64];
500     struct empfile *ep;
501     int res, ch;
502     int type;
503
504     while ((ch = skipfs(fp)) == '\n')
505         lineno++;
506     if (ch == EOF && expected_table == EF_BAD)
507         return -1;
508     ungetc(ch, fp);
509
510     human = ch == 'c';
511     res = -1;
512     if ((human
513          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
514          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
515                   name, &res) != 1) || res < 0)
516         return gripe("Expected xdump header");
517
518     if (skipfs(fp) != '\n')
519         return gripe("Junk after xdump header");
520
521     type = ef_byname(name);
522     if (type < 0)
523         return gripe("Unknown table `%s'", name);
524     ep = &empfile[type];
525     if (CANT_HAPPEN(!(ep->flags & EFF_MEM)))
526         return -1;              /* not implemented */
527
528     if (expected_table != EF_BAD && expected_table != type)
529         return gripe("Expected table `%s', not `%s'",
530                      ef_nameof(expected_table), name);
531     return type;
532 }
533
534 static int
535 xucolumnheader(FILE *fp, int type)
536 {
537     char ch;
538
539     if (!human)
540         return 0;
541
542     while ((ch = skipfs(fp)) == '\n');
543     ungetc(ch, fp);
544
545     /* FIXME parse column header */
546     if (fscanf(fp, "%*[^\n]\n") == -1)
547         return gripe("Invalid Column Header for table %s",
548             ef_nameof(type));
549     lineno++;
550
551     return 0;
552 }
553
554 static int
555 xutrailer(FILE *fp, int type, int row)
556 {
557     int rows, ch, res;
558
559     res = -1;
560     if (human) {
561         if (fscanf(fp, "config%n",  &res) != 0) {
562             return gripe("Malformed table footer");
563         }
564     }
565     ch = skipfs(fp);
566     if (!isdigit(ch)) {
567         if (ch != '\n' || !human)
568             return gripe("Malformed table footer");
569     } else {
570         ungetc(ch, fp);
571         if (fscanf(fp, "%d", &rows) != 1)
572             return gripe("Malformed table footer");
573         if (row != rows)
574             return gripe("Read %d rows, which doesn't match footer "
575                          "%d rows", row, rows);
576     }
577     if (skipfs(fp) != '\n')
578         return gripe("Junk after table footer");
579
580     while ((ch = skipfs(fp)) == '\n') ;
581     ungetc(ch, fp);
582
583     return 0;
584 }
585
586 int
587 xundump(FILE *fp, char *file, int expected_table)
588 {
589     int row, res, ch;
590     struct empfile *ep;
591     int need_sentinel;
592     struct value values[MAX_NUM_COLUMNS + 1];
593     int type;
594     int fixed_rows;
595
596     if (strcmp(fname, file) != 0) {
597         fname = file;
598         lineno = 1;
599     } else
600         lineno++;
601
602     if ((type = xuheader(fp, expected_table, values)) == -1)
603         return -1;
604     
605     ep = &empfile[type];
606     fixed_rows = has_const(ef_cadef(type));
607     need_sentinel = !fixed_rows; /* FIXME only approximation */
608
609     if (xucolumnheader(fp, type) == -1)
610         return -1;
611
612     row = 0;
613     for (;;) {
614         lineno++;
615         ch = skipfs(fp);
616         if (ch == '/')
617             break;
618         ungetc(ch, fp);
619         /*
620          * TODO
621          * Add column count check to the return value of xuflds()
622          */
623         res = xuflds(fp, values);
624         if (res > 0 && row >= ep->csize - 1) {
625             /* TODO grow cache unless EFF_STATIC */
626             gripe("Too many rows for table %s", ef_nameof(type));
627             res = -1;
628         }
629         if (res > 0) {
630             if (!fixed_rows)
631                 xuinitrow(type, row);
632             res = xuloadrow(type, row, values);
633             row++;
634         }
635         freeflds(values);
636         if (res < 0)
637             return -1;
638     }
639     if (fixed_rows && row != ep->csize -1)
640         return gripe("Table %s requires %d rows, got %d",
641                      ef_nameof(type), ep->csize - 1, row);
642
643     if (need_sentinel)
644         xuinitrow(type, row);
645
646     if (xutrailer(fp, type, row) == -1)
647         return -1;
648     
649     ep->fids = ep->cids = row;
650     return type;
651 }