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