]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
Clean up xundump's test whether table may be truncated
[empserver] / src / lib / common / xundump.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  xundump.c: Load back xdump output
28  *
29  *  Known contributors to this file:
30  *     Ron Koenderink, 2005
31  *     Markus Armbruster, 2005-2011
32  */
33
34 /*
35  * See doc/xdump!  And keep it up-to-date.
36  *
37  * Parsing of machine-readable xdump is not precise: it recognizes
38  * comments, accepts whitespace in place of single space, and accepts
39  * the full human-readable field syntax instead of its machine-
40  * readable subset.
41  *
42  * FIXME:
43  * - Normalize terminology: table/rows/columns or file/records/fields
44  * - Loading tables with NSC_STRING elements more than once leaks memory
45  * TODO:
46  * - Check each partial table supplies the same rows
47  * - Check EFF_CFG tables are dense
48  * - Symbolic array indexes
49  * - Option to treat missing and unknown fields as warning, not error
50  * TODO, but hardly worth the effort:
51  * - Permit reordering of array elements
52  */
53
54 #include <config.h>
55
56 #include <ctype.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <time.h>
62 #include "file.h"
63 #include "match.h"
64 #include "nsc.h"
65 #include "optlist.h"
66 #include "xdump.h"
67
68 static char *fname;             /* Name of file being read */
69 static int lineno;              /* Current line number */
70
71 static int cur_type;            /* Current table's file type */
72 static void *cur_obj;           /* The object being read into */
73 static int cur_id;              /* and its index in the table */
74 static int cur_obj_is_blank;
75
76 static int human;               /* Reading human-readable syntax? */
77 static int ellipsis;            /* Header ended with ...? */
78 static int is_partial;          /* Is input split into parts? */
79 static int nflds;               /* #fields in input records */
80 static struct castr **fldca;    /* Map field number to selector */
81 static int *fldidx;             /* Map field number to index */
82 static int *caflds;             /* Map selector number to #fields seen */
83 static int *cafldspp;           /* ditto, in previous parts */
84 static int may_trunc;           /* Okay to truncate? */
85
86 static int gripe(char *, ...) ATTRIBUTE((format (printf, 1, 2)));
87 static int deffld(int, char *, int);
88 static int defellipsis(void);
89 static int chkflds(void);
90 static int setnum(int, double);
91 static int setstr(int, char *);
92 static int xunsymbol(char *, struct castr *, int);
93 static int setsym(int, char *);
94 static int mtsymset(int, long *);
95 static int add2symset(int, long *, char *);
96 static int xubody(FILE *);
97 static int xutail(FILE *, struct castr *);
98
99 /*
100  * Gripe about the current line to stderr, return -1.
101  */
102 static int
103 gripe(char *fmt, ...)
104 {
105     va_list ap;
106
107     fprintf(stderr, "%s:%d: ", fname, lineno);
108     va_start(ap, fmt);
109     vfprintf(stderr, fmt, ap);
110     va_end(ap);
111     putc('\n', stderr);
112
113     return -1;
114 }
115
116 /* Make TYPE the current table.  */
117 static void
118 tbl_start(int type)
119 {
120     cur_type = type;
121     cur_id = -1;
122     cur_obj = NULL;
123 }
124
125 /* End the current table.  */
126 static void
127 tbl_end(void)
128 {
129     tbl_start(EF_BAD);
130 }
131
132 /*
133  * Seek to current table's ID-th record.
134  * ID must be acceptable.
135  * Store it in cur_obj, and set cur_id and cur_obj_is_blank accordingly.
136  * Return 0 on success, -1 on failure.
137  */
138 static int
139 tbl_seek(int id)
140 {
141     struct empfile *ep = &empfile[cur_type];
142
143     cur_obj_is_blank = id >= ep->fids;
144
145     if (id >= ef_nelem(cur_type)) {
146         if (!ef_ensure_space(cur_type, id, 1))
147             return gripe("Can't put ID %d into table %s", id, ep->name);
148     }
149
150     cur_obj = ef_ptr(cur_type, id);
151     if (CANT_HAPPEN(!cur_obj))
152         return -1;
153     cur_id = id;
154     return 0;
155 }
156
157 /*
158  * Get the next object.
159  * Must not have a record index.
160  * Store it in cur_obj, and set cur_id and cur_obj_is_blank accordingly.
161  * Return 0 on success, -1 on failure.
162  */
163 static int
164 tbl_next_obj(void)
165 {
166     int max_id = ef_id_limit(cur_type);
167
168     if (cur_id >= max_id)
169         return gripe("Too many rows");
170     return tbl_seek(cur_id + 1);
171 }
172
173 /*
174  * Get the next object, it has record index ID.
175  * Store it in cur_obj, and set cur_id and cur_obj_is_blank accordingly.
176  * Return 0 on success, -1 on failure.
177  */
178 static int
179 tbl_skip_to_obj(int id)
180 {
181     int max_id;
182
183     if (id < 0)
184         return gripe("Field %d must be >= 0", 1);
185     max_id = ef_id_limit(cur_type);
186     if (id > max_id)
187         return gripe("Field %d must be <= %d", 1, max_id);
188
189     if (tbl_seek(id) < 0)
190         return -1;
191     return 0;
192 }
193
194 /*
195  * Finish table part.
196  * If the table has variable length, truncate it.
197  */
198 static void
199 tbl_part_done(void)
200 {
201     cur_id = -1;
202     cur_obj = NULL;
203 }
204
205 /*
206  * Read and ignore field separators from FP.
207  * Return first character that is not a field separator.
208  */
209 static int
210 skipfs(FILE *fp)
211 {
212     int ch;
213
214     do {
215         ch = getc(fp);
216     } while (ch == ' ' || ch == '\t');
217
218     if (ch == '#') {
219         do {
220             ch = getc(fp);
221         } while (ch != EOF && ch != '\n');
222     }
223
224     return ch;
225 }
226
227 /*
228  * Decode escape sequences in BUF.
229  * Return BUF on success, null pointer on failure.
230  */
231 static char *
232 xuesc(char *buf)
233 {
234     char *src, *dst;
235     int octal_chr, n;
236
237     dst = buf;
238     src = buf;
239     while (*src) {
240         if (*src == '\\') {
241             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
242                 return NULL;
243             *dst++ = (char)octal_chr;
244             src += 3;
245         } else
246             *dst++ = *src++;
247     }
248     *dst = '\0';
249     return buf;
250 }
251
252 /*
253  * Read an identifier from FP into BUF.
254  * BUF must have space for 1024 characters.
255  * Return number of characters read on success, -1 on failure.
256  */
257 static int
258 getid(FILE *fp, char *buf)
259 {
260     int n;
261     if (fscanf(fp, "%1023[^\"#()<>= \t\n]%n", buf, &n) != 1
262         || !isalpha(buf[0]))
263         return -1;
264     xuesc(buf);
265     return n;
266 }
267
268 /*
269  * Try to read a field name from FP.
270  * I is the field number, counting from zero.
271  * If a name is read, set fldca[I] and fldidx[I] for it, and update
272  * caflds[].
273  * Return 1 if a name or ... was read, 0 on end of line, -1 on error.
274  */
275 static int
276 xufldname(FILE *fp, int i)
277 {
278     int ch, idx;
279     char buf[1024];
280
281     ch = skipfs(fp);
282     switch (ch) {
283     case EOF:
284         return gripe("Unexpected EOF");
285     case '\n':
286         if (chkflds() < 0)
287             return -1;
288         lineno++;
289         return 0;
290     case '.':
291         if (getc(fp) != '.' || getc(fp) != '.')
292             return gripe("Junk in header field %d", i + 1);
293         if (defellipsis() < 0)
294             return -1;
295         ch = skipfs(fp);
296         if (ch != EOF && ch != '\n')
297             return gripe("Junk after ...");
298         ungetc(ch, fp);
299         return 1;
300     default:
301         ungetc(ch, fp);
302         if (getid(fp, buf) < 0)
303             return gripe("Junk in header field %d", i + 1);
304         ch = getc(fp);
305         if (ch != '(') {
306             ungetc(ch, fp);
307             return deffld(i, buf, -1);
308         }
309         ch = getc(fp);
310         ungetc(ch, fp);
311         if (isdigit(ch) || ch == '-' || ch == '+') {
312             if (fscanf(fp, "%d", &idx) != 1)
313                 return gripe("Malformed number in index of header field %d",
314                              i + 1);
315             if (idx < 0)
316                 return gripe("Index must not be negative in header field %d",
317                              i + 1);
318         } else {
319             if (getid(fp, buf) < 0)
320                 return gripe("Malformed index in header field %d", i + 1);
321             return gripe("Symbolic index in header field %d not yet implemented",
322                          i + 1);
323         }
324         ch = getc(fp);
325         if (ch != ')')
326             return gripe("Malformed index in header field %d", i + 1);
327         return deffld(i, buf, idx);
328     }
329 }
330
331 /*
332  * Try to read a field value from FP.
333  * I is the field number, counting from zero.
334  * Return 1 if a value was read, 0 on end of line, -1 on error.
335  */
336 static int
337 xufld(FILE *fp, int i)
338 {
339     int ch;
340     char buf[1024];
341     double dbl;
342     long set;
343
344     ch = skipfs(fp);
345     switch (ch) {
346     case EOF:
347         return gripe("Unexpected EOF");
348     case '\n':
349         CANT_HAPPEN(i > nflds);
350         if (i < nflds) {
351             if (fldca[i]->ca_type != NSC_STRINGY && fldca[i]->ca_len)
352                 return gripe("Field %s(%d) missing",
353                              fldca[i]->ca_name, fldidx[i]);
354             return gripe("Field %s missing", fldca[i]->ca_name);
355         }
356         lineno++;
357         return 0;
358     case '+': case '-': case '.':
359     case '0': case '1': case '2': case '3': case '4':
360     case '5': case '6': case '7': case '8': case '9':
361         ungetc(ch, fp);
362         if (fscanf(fp, "%lg", &dbl) != 1)
363             return gripe("Malformed number in field %d", i + 1);
364         return setnum(i, dbl);
365     case '"':
366         ch = getc(fp);
367         if (ch == '"')
368             buf[0] = 0;
369         else {
370             ungetc(ch, fp);
371             if (fscanf(fp, "%1023[^\"\n]", buf) != 1 || getc(fp) != '"')
372                 return gripe("Malformed string in field %d", i + 1);
373             if (!xuesc(buf))
374                 return gripe("Invalid escape sequence in field %d",
375                              i + 1);
376         }
377         return setstr(i, buf);
378     case '(':
379         if (mtsymset(i, &set) < 0)
380             return -1;
381         for (;;) {
382             ch = skipfs(fp);
383             if (ch == EOF || ch == '\n')
384                 return gripe("Unmatched '(' in field %d", i + 1);
385             if (ch == ')')
386                 break;
387             ungetc(ch, fp);
388             if (getid(fp, buf) < 0)
389                 return gripe("Junk in field %d", i + 1);
390             if (add2symset(i, &set, buf) < 0)
391                 return -1;
392         }
393         return setnum(i, set);
394     default:
395         ungetc(ch, fp);
396         if (getid(fp, buf) < 0)
397             return gripe("Junk in field %d", i + 1);
398         if (!strcmp(buf, "nil"))
399             return setstr(i, NULL);
400         else
401             return setsym(i, buf);
402     }
403 }
404
405 /*
406  * Read fields from FP.
407  * Use PARSE() to read each field.
408  * Return number of fields read on success, -1 on error.
409  */
410 static int
411 xuflds(FILE *fp, int (*parse)(FILE *, int))
412 {
413     int i, ch, res;
414
415     for (i = 0; ; i++) {
416         res = parse(fp, i);
417         if (res < 0)
418             return -1;
419         if (res == 0)
420             return i;
421         ch = getc(fp);
422         if (ch == '\n')
423             ungetc(ch, fp);
424         else if (ch != ' ' && ch != '\t')
425             return gripe("Bad field separator after field %d", i + 1);
426     }
427 }
428
429 /*
430  * Define the FLDNO-th field.
431  * If IDX is negative, define as selector NAME, else as NAME(IDX).
432  * Set fldca[FLDNO] and fldidx[FLDNO] accordingly.
433  * Update caflds[].
434  * Return 1 on success, -1 on error.
435  */
436 static int
437 deffld(int fldno, char *name, int idx)
438 {
439     struct castr *ca = ef_cadef(cur_type);
440     int res;
441
442     res = stmtch(name, ca, offsetof(struct castr, ca_name),
443                  sizeof(struct castr));
444     if (res < 0)
445         return gripe("Header %s of field %d is %s", name, fldno + 1,
446                      res == M_NOTUNIQUE ? "ambiguous" : "unknown");
447     if ((ca[res].ca_flags & NSC_EXTRA) || CANT_HAPPEN(ca[res].ca_get))
448         return gripe("Extraneous header %s in field %d", name, fldno + 1);
449     if (ca[res].ca_type != NSC_STRINGY && ca[res].ca_len != 0) {
450         if (idx < 0)
451             return gripe("Header %s requires an index in field %d",
452                          ca[res].ca_name, fldno + 1);
453         if (idx >= ca[res].ca_len)
454             return gripe("Header %s(%d) index out of bounds in field %d",
455                          ca[res].ca_name, idx, fldno + 1);
456         if (idx < caflds[res])
457             return gripe("Duplicate header %s(%d) in field %d",
458                          ca[res].ca_name, idx, fldno + 1);
459         if (idx > caflds[res])
460             return gripe("Expected header %s(%d) in field %d",
461                          ca[res].ca_name, caflds[res], fldno + 1);
462     } else {
463         if (idx >= 0)
464             return gripe("Header %s doesn't take an index in field %d",
465                          ca[res].ca_name, fldno + 1);
466         idx = 0;
467         if (caflds[res])
468             return gripe("Duplicate header %s in field %d",
469                          ca[res].ca_name, fldno + 1);
470     }
471     fldca[fldno] = &ca[res];
472     fldidx[fldno] = idx;
473     caflds[res]++;
474     return 1;
475 }
476
477 /*
478  * Record that header ends with ...
479  * Set ellipsis and is_partial.
480  * Return 0 on success, -1 on error.
481  */
482 static int
483 defellipsis(void)
484 {
485     struct castr *ca = ef_cadef(cur_type);
486
487     if (ca[0].ca_table != cur_type || (ca[0].ca_flags & NSC_EXTRA))
488         return gripe("Table %s doesn't support ...", ef_nameof(cur_type));
489     ellipsis = is_partial = 1;
490     return 0;
491 }
492
493 /*
494  * Check fields in xdump are sane.
495  * Return 0 on success, -1 on error.
496  */
497 static int
498 chkflds(void)
499 {
500     struct castr *ca = ef_cadef(cur_type);
501     int i, len, cafldsmax, res = 0;
502
503     /* Record index must come first, to make cur_id work, see setnum() */
504     if (ca[0].ca_table == cur_type && caflds[0] && fldca[0] != &ca[0])
505         res = gripe("Header field %s must come first", ca[0].ca_name);
506
507     if (is_partial) {
508         /* Need a join field, use 0-th selector */
509         if (!caflds[0])
510             res = gripe("Header field %s required in each table part",
511                         ca[0].ca_name);
512     }
513
514     if (ellipsis)
515         return res;             /* table is split, another part expected */
516
517     /* Check for missing fields */
518     for (i = 0; ca[i].ca_name; i++) {
519         cafldsmax = MAX(caflds[i], cafldspp[i]);
520         if (ca[i].ca_flags & NSC_EXTRA)
521             continue;
522         len = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
523         if (!len && !cafldsmax)
524             res = gripe("Header field %s missing", ca[i].ca_name);
525         else if (len && cafldsmax == len - 1)
526             res = gripe("Header field %s(%d) missing",
527                         ca[i].ca_name, len - 1);
528         else if (len && cafldsmax < len - 1)
529             res = gripe("Header fields %s(%d) ... %s(%d) missing",
530                         ca[i].ca_name, cafldsmax, ca[i].ca_name, len - 1);
531     }
532
533     return res;
534 }
535
536 /*
537  * Get selector for field FLDNO.
538  * Assign the field's selector index to *IDX, unless it is null.
539  * Return the selector on success, null pointer on error.
540  */
541 static struct castr *
542 getfld(int fldno, int *idx)
543 {
544     if (fldno >= nflds) {
545         gripe("Too many fields, expected only %d", nflds);
546         return NULL;
547     }
548     if (CANT_HAPPEN(fldno < 0))
549         return NULL;
550     if (idx)
551         *idx = fldidx[fldno];
552     return fldca[fldno];
553 }
554
555 /*
556  * Is a new value for field FLDNO required to match the old one?
557  */
558 static int
559 fldval_must_match(int fldno)
560 {
561     struct castr *ca = ef_cadef(cur_type);
562     int i = fldca[fldno] - ca;
563
564     /*
565      * Value must match if:
566      * it's for a const selector, unless the object is still blank, or
567      * it was already given in a previous part of a split table.
568      */
569     return (!cur_obj_is_blank && (fldca[fldno]->ca_flags & NSC_CONST))
570         || fldidx[fldno] < cafldspp[i];
571 }
572
573 /*
574  * Set value of field FLDNO in current object to DBL.
575  * Return 1 on success, -1 on error.
576  */
577 static int
578 setnum(int fldno, double dbl)
579 {
580     struct castr *ca;
581     int next_id, idx;
582     char *memb_ptr;
583     double old, new;
584
585     ca = getfld(fldno, &idx);
586     if (!ca)
587         return -1;
588
589     if (fldno == 0) {
590         if (ca->ca_table == cur_type) {
591             /* Got record index */
592             next_id = (int)dbl;
593             if (next_id != dbl)
594                 return gripe("Field %d can't hold this value", fldno + 1);
595             if (tbl_skip_to_obj(next_id) < 0)
596                 return -1;
597         } else {
598             if (tbl_next_obj() < 0)
599                 return -1;
600         }
601     }
602     memb_ptr = cur_obj;
603     memb_ptr += ca->ca_off;
604
605     switch (ca->ca_type) {
606     case NSC_CHAR:
607         old = ((signed char *)memb_ptr)[idx];
608         new = ((signed char *)memb_ptr)[idx] = (signed char)dbl;
609         break;
610     case NSC_UCHAR:
611     case NSC_HIDDEN:
612         old = ((unsigned char *)memb_ptr)[idx];
613         new = ((unsigned char *)memb_ptr)[idx] = (unsigned char)dbl;
614         break;
615     case NSC_SHORT:
616         old = ((short *)memb_ptr)[idx];
617         new = ((short *)memb_ptr)[idx] = (short)dbl;
618         break;
619     case NSC_USHORT:
620         old = ((unsigned short *)memb_ptr)[idx];
621         new = ((unsigned short *)memb_ptr)[idx] = (unsigned short)dbl;
622         break;
623     case NSC_INT:
624         old = ((int *)memb_ptr)[idx];
625         new = ((int *)memb_ptr)[idx] = (int)dbl;
626         break;
627     case NSC_LONG:
628         old = ((long *)memb_ptr)[idx];
629         new = ((long *)memb_ptr)[idx] = (long)dbl;
630         break;
631     case NSC_XCOORD:
632         old = ((coord *)memb_ptr)[idx];
633         /* FIXME use variant of xrel() that takes orig instead of nation */
634         if (old >= WORLD_X / 2)
635             old -= WORLD_X;
636         new = ((coord *)memb_ptr)[idx] = XNORM((coord)dbl);
637         if (new >= WORLD_X / 2)
638             new -= WORLD_X;
639         break;
640     case NSC_YCOORD:
641         old = ((coord *)memb_ptr)[idx];
642         /* FIXME use variant of yrel() that takes orig instead of nation */
643         if (old >= WORLD_Y / 2)
644             old -= WORLD_Y;
645         new = ((coord *)memb_ptr)[idx] = YNORM((coord)dbl);
646         if (new >= WORLD_Y / 2)
647             new -= WORLD_Y;
648         break;
649     case NSC_FLOAT:
650         old = ((float *)memb_ptr)[idx];
651         ((float *)memb_ptr)[idx] = (float)dbl;
652         new = dbl;              /* suppress new != dbl check */
653         break;
654     case NSC_DOUBLE:
655         old = ((double *)memb_ptr)[idx];
656         ((double *)memb_ptr)[idx] = dbl;
657         new = dbl;              /* suppress new != dbl check */
658         break;
659     case NSC_TIME:
660         old = ((time_t *)memb_ptr)[idx];
661         new = ((time_t *)memb_ptr)[idx] = (time_t)dbl;
662         break;
663     default:
664         return gripe("Field %d doesn't take numbers", fldno + 1);
665     }
666
667     if (fldval_must_match(fldno) && old != dbl)
668         return gripe("Value for field %d must be %g", fldno + 1, old);
669     if (new != dbl)
670         return gripe("Field %d can't hold this value", fldno + 1);
671
672     return 1;
673 }
674
675 /*
676  * Set value of field FLDNO in current object to STR.
677  * Return 1 on success, -1 on error.
678  */
679 static int
680 setstr(int fldno, char *str)
681 {
682     struct castr *ca;
683     int must_match, idx;
684     size_t len;
685     char *memb_ptr, *old;
686
687     ca = getfld(fldno, &idx);
688     if (!ca)
689         return -1;
690
691     if (fldno == 0) {
692         if (tbl_next_obj() < 0)
693             return -1;
694     }
695     memb_ptr = cur_obj;
696     memb_ptr += ca->ca_off;
697     must_match = fldval_must_match(fldno);
698
699     switch (ca->ca_type) {
700     case NSC_STRING:
701         old = ((char **)memb_ptr)[idx];
702         if (!must_match)
703             /* FIXME may leak old value */
704             ((char **)memb_ptr)[idx] = str ? strdup(str) : NULL;
705         len = 65535;            /* really SIZE_MAX, but that's C99 */
706         break;
707     case NSC_STRINGY:
708         if (CANT_HAPPEN(idx))
709             return -1;
710         if (!str)
711             return gripe("Field %d doesn't take nil", fldno + 1);
712         len = ca->ca_len;
713         if (strlen(str) > len)
714             return gripe("Field %d takes at most %d characters",
715                          fldno + 1, (int)len);
716         old = memb_ptr;
717         if (!must_match)
718             strncpy(memb_ptr, str, len);
719         break;
720     default:
721         return gripe("Field %d doesn't take strings", fldno + 1);
722     }
723
724     if (must_match) {
725         if (old && (!str || strncmp(old, str, len)))
726             return gripe("Value for field %d must be \"%.*s\"",
727                          fldno + 1, (int)len, old);
728         if (!old && str)
729             return gripe("Value for field %d must be nil", fldno + 1);
730     }
731
732     return 1;
733 }
734
735 /*
736  * Resolve symbol name ID in table referred to by CA.
737  * Use field number N for error messages.
738  * Return index in referred table on success, -1 on failure.
739  */
740 static int
741 xunsymbol(char *id, struct castr *ca, int n)
742 {
743     int i = ef_elt_byname(ca->ca_table, id);
744     if (i < 0)
745         return gripe("%s %s symbol `%s' in field %d",
746                      i == M_NOTUNIQUE ? "Ambiguous" : "Unknown",
747                      ca->ca_name, id, n + 1);
748     return i;
749 }
750
751 /*
752  * Map symbol index to symbol value.
753  * CA is the table, and I is the index in it.
754  */
755 static int
756 symval(struct castr *ca, int i)
757 {
758     int type = ca->ca_table;
759
760     if (type != EF_BAD && ef_cadef(type) == symbol_ca)
761         /* symbol table, value is in the table */
762         return ((struct symbol *)ef_ptr(type, i))->value;
763     /* value is the table index */
764     return i;
765 }
766
767 /*
768  * Set value of field FLDNO in current object to value of symbol SYM.
769  * Return 1 on success, -1 on error.
770  */
771 static int
772 setsym(int fldno, char *sym)
773 {
774     struct castr *ca;
775     int i;
776
777     ca = getfld(fldno, NULL);
778     if (!ca)
779         return -1;
780
781     if (ca->ca_table == EF_BAD || (ca->ca_flags & NSC_BITS))
782         return gripe("Field %d doesn't take symbols", fldno + 1);
783
784     i = xunsymbol(sym, ca, fldno);
785     if (i < 0)
786         return -1;
787     return setnum(fldno, symval(ca, i));
788 }
789
790 /*
791  * Create an empty symbol set for field FLDNO in *SET.
792  * Return 1 on success, -1 on error.
793  */
794 static int
795 mtsymset(int fldno, long *set)
796 {
797     struct castr *ca;
798
799     ca = getfld(fldno, NULL);
800     if (!ca)
801         return -1;
802
803     if (ca->ca_table == EF_BAD || ef_cadef(ca->ca_table) != symbol_ca
804         || !(ca->ca_flags & NSC_BITS))
805         return gripe("Field %d doesn't take symbol sets", fldno + 1);
806     *set = 0;
807     return 0;
808 }
809
810 /*
811  * Add a symbol to a symbol set for field FLDNO in *SET.
812  * SYM is the name of the symbol to add.
813  * Return 1 on success, -1 on error.
814  */
815 static int
816 add2symset(int fldno, long *set, char *sym)
817 {
818     struct castr *ca;
819     int i;
820
821     ca = getfld(fldno, NULL);
822     if (!ca)
823         return -1;
824
825     i = xunsymbol(sym, ca, fldno);
826     if (i < 0)
827         return -1;
828     *set |= symval(ca, i);
829     return 0;
830 }
831
832 /*
833  * Read an xdump table header line from FP.
834  * Expect header for EXPECTED_TABLE, unless it is EF_BAD.
835  * Recognize header for machine- and human-readable syntax, and set
836  * human accordingly.
837  * Return table type on success, -2 on EOF before header, -1 on failure.
838  */
839 static int
840 xuheader(FILE *fp, int expected_table)
841 {
842     char name[64];
843     int res, ch;
844     int type;
845
846     while ((ch = skipfs(fp)) == '\n')
847         lineno++;
848     if (ch == EOF && expected_table == EF_BAD)
849         return -2;
850     ungetc(ch, fp);
851
852     human = ch == 'c';
853     res = -1;
854     if ((human
855          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
856          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
857                   name, &res) != 1) || res < 0)
858         return gripe("Expected xdump header");
859
860     type = ef_byname(name);
861     if (type < 0)
862         return gripe("Unknown table `%s'", name);
863     if (expected_table != EF_BAD && expected_table != type)
864         return gripe("Expected table `%s', not `%s'",
865                      ef_nameof(expected_table), name);
866
867     if (!empfile[type].file
868         || !ef_cadef(type) || !(ef_flags(type) & EFF_MEM)) {
869         CANT_HAPPEN(expected_table != EF_BAD);
870         return gripe("Table `%s' is not permitted here", name);
871     }
872
873     if (skipfs(fp) != '\n')
874         return gripe("Junk after xdump header");
875     lineno++;
876
877     return type;
878 }
879
880 /*
881  * Find fields in this xdump.
882  * If reading human-readable syntax, read a field header line from FP.
883  * Else take fields from the table's selectors in CA[].
884  * Set ellipsis, nflds, fldca[], fldidx[] and caflds[] accordingly.
885  * Return 0 on success, -1 on failure.
886  */
887 static int
888 xufldhdr(FILE *fp, struct castr ca[])
889 {
890     struct castr **fca;
891     int *fidx;
892     int ch, i, j, n;
893
894     for (i = 0; ca[i].ca_name; i++)
895         caflds[i] = 0;
896     ellipsis = 0;
897
898     if (human) {
899         while ((ch = skipfs(fp)) == '\n')
900             lineno++;
901         ungetc(ch, fp);
902         nflds = xuflds(fp, xufldname);
903         if (nflds < 0)
904             return -1;
905         nflds -= ellipsis != 0;
906     } else {
907         fca = fldca;
908         fidx = fldidx;
909
910         for (i = 0; ca[i].ca_name; i++) {
911             if ((ca[i].ca_flags & NSC_EXTRA))
912                 continue;
913             n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
914             j = 0;
915             do {
916                 *fca++ = &ca[i];
917                 *fidx++ = j;
918             } while (++j < n);
919         }
920
921         nflds = fidx - fldidx;
922     }
923
924     return 0;
925 }
926
927 /*
928  * Read xdump footer from FP.
929  * CA[] contains the table's selectors.
930  * The body had RECS records.
931  * Update cafldspp[] from caflds[].
932  * Return 0 on success, -1 on failure.
933  */
934 static int
935 xufooter(FILE *fp, struct castr ca[], int recs)
936 {
937     int res, n, i;
938
939     res = -1;
940     if (human) {
941         if (fscanf(fp, "config%n", &res) != 0 || res < 0)
942             return gripe("Malformed table footer");
943     } else {
944         if (fscanf(fp, "%d", &n) != 1)
945             return gripe("Malformed table footer");
946         if (recs != n)
947             return gripe("Read %d rows, which doesn't match footer "
948                          "%d rows", recs, n);
949     }
950     if (skipfs(fp) != '\n')
951         return gripe("Junk after table footer");
952     tbl_part_done();
953     lineno++;
954
955     for (i = 0; ca[i].ca_name; i++) {
956         if (cafldspp[i] < caflds[i])
957             cafldspp[i] = caflds[i];
958     }
959
960     return 0;
961 }
962
963 /*
964  * Read an xdump table from FP.
965  * Both machine- and human-readable xdump syntax are recognized.
966  * Expect table EXPECTED_TABLE, unless it is EF_BAD.
967  * Report errors to stderr.
968  * Messages assume FP starts in the file FILE at line *PLNO.
969  * Update *PLNO to reflect lines read from FP.
970  * Return table type on success, -2 on EOF before header, -1 on failure.
971  */
972 int
973 xundump(FILE *fp, char *file, int *plno, int expected_table)
974 {
975     struct castr *ca;
976     int type, nca, nf, i, ch;
977
978     fname = file;
979     lineno = *plno;
980
981     if ((type = xuheader(fp, expected_table)) < 0)
982         return type;
983
984     ca = ef_cadef(type);
985     if (CANT_HAPPEN(!ca))
986         return -1;
987
988     nca = nf = 0;
989     may_trunc = empfile[type].nent < 0;
990     for (i = 0; ca[i].ca_name; i++) {
991         nca++;
992         if (!(ca[i].ca_flags & NSC_EXTRA)) {
993             nf += MAX(1, ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0);
994             if (ca[i].ca_flags & NSC_CONST)
995                 may_trunc = 0;
996         }
997     }
998     fldca = malloc(nf * sizeof(*fldca));
999     fldidx = malloc(nf * sizeof(*fldidx));
1000     caflds = malloc(nca * sizeof(*caflds));
1001     cafldspp = calloc(nca, sizeof(*cafldspp));
1002
1003     tbl_start(type);
1004     if (xutail(fp, ca) < 0)
1005         type = EF_BAD;
1006     tbl_end();
1007
1008     free(cafldspp);
1009     free(caflds);
1010     free(fldidx);
1011     free(fldca);
1012
1013     /* Skip empty lines so that callers can easily check for EOF */
1014     while ((ch = skipfs(fp)) == '\n')
1015         lineno++;
1016     ungetc(ch, fp);
1017
1018     *plno = lineno;
1019     return type;
1020 }
1021
1022 /*
1023  * Read the remainder of an xdump after the table header line from FP.
1024  * CA[] contains the table's selectors.
1025  * Return 0 on success, -1 on failure.
1026  */
1027 static int
1028 xutail(FILE *fp, struct castr *ca)
1029 {
1030     int recs;
1031
1032     is_partial = 0;
1033     for (;;) {
1034         if (xufldhdr(fp, ca) < 0)
1035             return -1;
1036         if ((recs = xubody(fp)) < 0)
1037             return -1;
1038         if (xufooter(fp, ca, recs) < 0)
1039             return -1;
1040         if (!ellipsis)
1041             return 0;
1042         if (xuheader(fp, cur_type) < 0)
1043             return -1;
1044     }
1045 }
1046
1047 /*
1048  * Read the body of an xdump table from FP.
1049  * Return number of rows read on success, -1 on failure.
1050  */
1051 static int
1052 xubody(FILE *fp)
1053 {
1054     struct empfile *ep = &empfile[cur_type];
1055     int i, maxid, ch;
1056
1057     maxid = 0;
1058     for (i = 0;; ++i) {
1059         while ((ch = skipfs(fp)) == '\n')
1060             lineno++;
1061         if (ch == '/')
1062             break;
1063         ungetc(ch, fp);
1064         if (xuflds(fp, xufld) < 0)
1065             return -1;
1066         maxid = MAX(maxid, cur_id + 1);
1067     }
1068
1069     if (CANT_HAPPEN(maxid > ep->fids))
1070         maxid = ep->fids;
1071     if (maxid < ep->fids) {
1072         if (may_trunc) {
1073             if (!ef_truncate(cur_type, maxid))
1074                 return -1;
1075         } else
1076             return gripe("Table %s requires %d rows, got %d",
1077                          ef_nameof(cur_type), ep->fids, maxid);
1078     }
1079
1080     return i;
1081 }