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