]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
(xundump): Fix parsing of XDUMP header.
[empserver] / src / lib / common / xundump.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2005, 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 <stdio.h>
38 #include <stdlib.h>
39
40 #include <ctype.h>
41 #include <string.h>
42 #include <stdarg.h>
43 #include <time.h>
44
45 #include "prototypes.h"
46 #include "file.h"
47 #include "nsc.h"
48 #include "match.h"
49
50 #define MAX_NUM_COLUMNS 256
51
52 static char *fname = "";
53 static int lineno = 0;
54
55 /*
56  * TODO
57  * This structure could be replaced with struct valstr.
58  */
59 enum enum_value {
60     VAL_NOTUSED,
61     VAL_STRING,
62     VAL_SYMBOL,
63     VAL_DOUBLE
64 };
65
66 struct value {
67     enum enum_value v_type;
68     union {
69         char *v_string;
70         double v_double;
71     } v_field;
72 };
73
74 static int
75 gripe(char *fmt, ...)
76 {
77     va_list ap;
78
79     fprintf(stderr, "%s:%d: ", fname, lineno);
80     va_start(ap, fmt);
81     vfprintf(stderr, fmt, ap);
82     va_end(ap);
83     putc('\n', stderr);
84
85     return -1;
86 }
87
88 static char *
89 xuesc(char *buf)
90 {
91     char *src, *dst;
92     int octal_chr, n;
93
94     dst = buf;
95     src = buf;
96     while (*src) {
97         if (*src == '\\') {
98             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
99                 return NULL;
100             *dst++ = (char)octal_chr;
101             src += 3;
102         } else
103             *dst++ = *src++;
104     }
105     *dst = '\0';
106     return buf;
107 }
108
109 static int
110 xuflds(FILE *fp, struct value values[])
111 {
112     int i, ch;
113     char sep;
114     char buf[1024];
115
116     for (i = 0; ; i++) {
117         values[i].v_type = VAL_NOTUSED;
118         if (i >= MAX_NUM_COLUMNS)
119             return gripe("Too many columns");
120
121         ch = getc(fp);
122         ungetc(ch, fp);
123
124         switch (ch) {
125         case EOF:
126             return gripe("Unexpected EOF");
127         case '+': case '-': case '.':
128         case '0': case '1': case '2': case '3': case '4':
129         case '5': case '6': case '7': case '8': case '9':
130             if (fscanf(fp, "%lg%c", &values[i].v_field.v_double, &sep) != 2)
131                 return gripe("Malformed number in field %d", i + 1);
132             values[i].v_type = VAL_DOUBLE;
133             break;
134         case '"':
135             if (fscanf(fp, "\"%1023[^ \n]%c", buf, &sep) != 2
136                 || buf[strlen(buf)-1] != '"')
137                 return gripe("Malformed string in field %d", i + 1);
138             buf[strlen(buf)-1] = '\0';
139             if (!xuesc(buf))
140                 return gripe("Invalid escape sequence in field %d",
141                     i + 1);
142             values[i].v_type = VAL_STRING;
143             values[i].v_field.v_string = strdup(buf);
144             break;
145         default:
146             if (fscanf(fp, "%1023[^ \n]%c", buf, &sep) != 2) {
147                 return gripe("Junk in field %d", i + 1);
148             }
149             if (!strcmp(buf, "nil")) {
150                 values[i].v_type = VAL_STRING;
151                 values[i].v_field.v_string = NULL;
152             }
153             else {
154                 values[i].v_type = VAL_SYMBOL;
155                 values[i].v_field.v_string = strdup(buf);
156             }
157         }
158         if (sep == '\n')
159             break;
160         if (sep != ' ')
161             return gripe(
162                 "Expected space or newline as field separator found %c",
163                 sep);
164     }
165     values[++i].v_type = VAL_NOTUSED;
166     return i;
167 }
168
169 static void
170 freeflds(struct value values[])
171 {
172     struct value *vp;
173
174     for (vp = values; vp->v_type != VAL_NOTUSED; vp++) {
175         if (vp->v_type != VAL_DOUBLE)
176             free(vp->v_field.v_string);
177     }
178 }
179
180 static int
181 xunsymbol(struct castr *ca, char *buf)
182 {
183     struct symbol *symbol = (struct symbol *)empfile[ca->ca_table].cache;
184     int i;
185     int value = 0;
186     char *token;
187
188     if (ca->ca_flags & NSC_BITS)
189         token = strtok( buf, "|");
190     else
191         token = buf;
192
193     if (!token || token[0] == '\0')
194         return gripe("Empty symbol value for field %s", ca->ca_name);
195
196     while (token) {
197         if ((i = stmtch(token, symbol, offsetof(struct symbol, name),
198                         sizeof(struct symbol))) != M_NOTFOUND) {
199             if (!(ca->ca_flags & NSC_BITS))
200                 return(symbol[i].value);
201             value |= symbol[i].value;
202         }
203         else
204             return gripe("Symbol %s was not found for field %s", token,
205                 ca->ca_name);
206         token = strtok(NULL, "|");
207     }
208     return(value);
209 }
210
211 static int
212 has_const(struct castr ca[])
213 {
214     int i;
215
216     for (i = 0; ca[i].ca_name; i++) {
217         if (ca[i].ca_flags & NSC_CONST)
218             return 1;
219     }
220     return 0;
221 }
222
223 static void
224 xuinitrow(int type, int row)
225 {
226     struct empfile *ep = &empfile[type];
227     char *ptr = ep->cache + ep->size * row;
228
229     memset(ptr, 0, ep->size);
230
231     if (ep->init)
232         ep->init(row, ptr);
233 }
234
235 static int
236 xuloadrow(int type, int row, struct value values[])
237 {
238     int i,j,k;
239     struct empfile *ep = &empfile[type];
240     char *ptr = ep->cache + ep->size * row;
241     struct castr *ca = ep->cadef;
242     void *row_ref;
243
244     i = 0;
245     j = 0;
246     while (ca[i].ca_type != NSC_NOTYPE &&
247            values[j].v_type != VAL_NOTUSED) {
248         row_ref = (char *)ptr + ca[i].ca_off;
249         k = 0;
250         do {
251             /*
252              * TODO
253              * factor out NSC_CONST comparsion
254              */
255             switch (values[j].v_type) {
256             case VAL_SYMBOL:
257                 if (ca[i].ca_table == EF_BAD)
258                     return(gripe("Found symbol string %s, but column %s "
259                         "is not symbol or symbol sets",
260                         values[j].v_field.v_string, ca[i].ca_name));
261                 values[j].v_field.v_double =
262                     (double)xunsymbol(&ca[i], values[j].v_field.v_string);
263                 if (values[j].v_field.v_double < 0.0)
264                     return -1;
265                 /*
266                  * fall through
267                  */
268             case VAL_DOUBLE:
269                 switch (ca[i].ca_type) {
270                 case NSC_INT:
271                     if (ca[i].ca_flags & NSC_CONST) {
272                         if (((int *)row_ref)[k] != (int)
273                              values[j].v_field.v_double)
274                             gripe("Field %s must be same, "
275                                 "read %d != expected %d",
276                                 ca[i].ca_name,
277                                 ((int *)row_ref)[k],
278                                 (int)values[j].v_field.v_double);
279
280                     } else
281                         ((int *)row_ref)[k] =
282                             (int)values[j].v_field.v_double;
283                     break;
284                 case NSC_LONG:
285                     if (ca[i].ca_flags & NSC_CONST) {
286                         if (((long *)row_ref)[k] != (long)
287                              values[j].v_field.v_double)
288                             gripe("Field %s must be same, "
289                                 "read %ld != expected %ld",
290                                 ca[i].ca_name,
291                                 ((long *)row_ref)[k],
292                                 (long)values[j].v_field.v_double);
293                     } else
294                         ((long *)row_ref)[k] = (long)
295                             values[j].v_field.v_double;
296                     break;
297                 case NSC_USHORT:
298                     if (ca[i].ca_flags & NSC_CONST) {
299                         if (((unsigned short *)row_ref)[k] !=
300                              (unsigned short)values[j].v_field.v_double)
301                             gripe("Field %s must be same, "
302                                 "read %d != expected %d",
303                                 ca[i].ca_name,
304                                 ((unsigned short *)row_ref)[k],
305                                 (unsigned short)values[j].v_field.v_double);
306                     } else
307                         ((unsigned short *)row_ref)[k] = (unsigned short)
308                             values[j].v_field.v_double;
309                     break;
310                 case NSC_UCHAR:
311                     if (ca[i].ca_flags & NSC_CONST) {
312                         if (((unsigned char *)row_ref)[k] != (unsigned char)
313                              values[j].v_field.v_double)
314                             gripe("Field %s must be same, "
315                                 "read %d != expected %d",
316                                 ca[i].ca_name,
317                                 ((unsigned char *)row_ref)[k],
318                                 (unsigned char)values[j].v_field.v_double);
319                     } else
320                         ((unsigned char *)row_ref)[k] = (unsigned char)
321                             values[j].v_field.v_double;
322                     break;
323                 case NSC_FLOAT:
324                     if (ca[i].ca_flags & NSC_CONST) {
325                         if (((float *)row_ref)[k] != (float)
326                              values[j].v_field.v_double)
327                             gripe("Field %s must be same, "
328                                 "read %g != expected %g",
329                                 ca[i].ca_name,
330                                 ((float *)row_ref)[k],
331                                 (float)values[j].v_field.v_double);
332                     } else
333                         ((float *)row_ref)[k] = (float)
334                             values[j].v_field.v_double;
335                     break;
336                 case NSC_STRING:
337                     return gripe("Field %s is a string type, "
338                         "but %lg was read which is a number",
339                         ca[i].ca_name, values[j].v_field.v_double);
340                 default:
341                     return gripe("Field %s's type %d is not supported",
342                         ca[i].ca_name, ca[i].ca_type);
343                 }
344                 break;
345             case VAL_STRING:
346                 switch(ca[i].ca_type) {
347                 case NSC_STRINGY:
348                     return gripe("Field %s is of NSC_STRINGY type "
349                         "which is not supported", ca[i].ca_name);
350                 case NSC_STRING:
351                     if (ca[i].ca_flags & NSC_CONST) {
352                         if (strcmp(((char **)row_ref)[k],
353                                    values[j].v_field.v_string) != 0)
354                             gripe("Field %s must be same, "
355                                 "read %s != expected %s",
356                                 ca[i].ca_name,
357                                 *((char **)row_ref)[k],
358                                 *values[j].v_field.v_string);
359                     } else
360                         ((char **)row_ref)[k] = values[j].v_field.v_string;
361                     break;
362                 case NSC_INT:
363                 case NSC_LONG:
364                 case NSC_USHORT:
365                 case NSC_UCHAR:
366                 case NSC_FLOAT:
367                     return gripe("Field %s is a number type %d, "
368                         "but %s was read which is a string",
369                         ca[i].ca_name, ca[i].ca_type,
370                         values[j].v_field.v_string);
371                 default:
372                     return gripe("Field %s's type %d is not supported",
373                             ca[i].ca_name, ca[i].ca_type);
374                 }
375                 break;
376             case VAL_NOTUSED:
377                 return gripe("Missing column %s in file", ca[i].ca_name);
378             default:
379                 return gripe("Unknown value type %d", values[j].v_type);
380             }
381             k++;
382             j++;
383         } while (k < ca[i].ca_len);
384         i++;
385     }
386     if (ca[i].ca_type != NSC_NOTYPE)
387         return gripe("Missing column %s in file", ca[i].ca_name);
388     switch  (values[j].v_type) {
389     case VAL_NOTUSED:
390         break;
391     case VAL_STRING:
392     case VAL_SYMBOL:
393         return gripe("Extra junk after the last column, read %s",
394             values[j].v_field.v_string);
395     case VAL_DOUBLE:
396         return gripe("Extra junk after the last column, read %lg",
397             values[j].v_field.v_double);
398     default:
399         return gripe("Extra junk after the last column, "
400             "unknown value type %d", values[j].v_type);
401     }
402     return 0;
403 }
404
405 int
406 xundump(FILE *fp, char *file, int expected_table)
407 {
408     char name[64];
409     char sep;
410     int row, res, rows, ch;
411     struct value values[MAX_NUM_COLUMNS + 1];
412     int type;
413     int fixed_rows;
414
415     if (strcmp(fname, file) != 0) {
416         fname = file;
417         lineno = 1;
418     } else
419         lineno++;
420
421     if (fscanf(fp, "XDUMP%*1[ ]%63s%*1[ ]%*d%c", name, &sep) != 2)
422         return gripe("Expected XDUMP header");
423     if (sep != '\n')
424         return gripe("Junk after XDUMP header");
425
426     type = ef_byname(name);
427     if (type < 0)
428         return gripe("Unknown table `%s'", name);
429
430     if (expected_table != EF_BAD && expected_table != type)
431         return gripe("Expected table `%s', not `%s'",
432                      ef_nameof(expected_table), name);
433
434     fixed_rows = has_const(ef_cadef(type));
435
436     for (row = 0; ; row++) {
437         lineno++;
438         ch = getc(fp);
439         ungetc(ch, fp);
440         if (ch == '/')
441             break;
442         /*
443          * TODO
444          * Add column count check to the return value of xuflds()
445          */
446         res = xuflds(fp, values);
447         if (res > 0 && row >= empfile[type].csize - 1) {
448             gripe("Too many rows for table %s", name);
449             res = -1;
450         }
451         if (res > 0) {
452             empfile[type].fids = row + 1;
453             if (!fixed_rows)
454                 xuinitrow(type, row);
455             res = xuloadrow(type, row, values);
456         }
457         freeflds(values);
458         if (res < 0)
459             return -1;
460     }
461
462     if (fscanf(fp, "/%d%c", &rows, &sep) != 2)
463         return gripe("Expected table footer");
464     if (row != rows)
465         return gripe("Read %d rows, which doesn't match footer",
466                      row);
467     if (fixed_rows && row != empfile[type].csize -1)
468         return gripe("Table %s requires %d rows, got %d",
469                      name, empfile[type].csize - 1, row);
470     if (sep != '\n')
471         return gripe("Junk after table footer");
472
473     if (!fixed_rows)
474         xuinitrow(type, row);
475
476     return 0;
477 }