]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
Fix fields checks for split tables:
[empserver] / src / lib / common / xundump.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2007, 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-2007
33  */
34
35 /*
36  * FIXME:
37  * - Normalize terminology: table/rows/columns or file/records/fields
38  * - Loading tables with NSC_STRING elements more than once leaks memory
39  * TODO:
40  * - Check each partial table supplies the same rows
41  * - Check EFF_CFG tables are dense
42  * - Symbolic references to non-symbol tables
43  * - Symbolic array indexes
44  * TODO, but hardly worth the effort:
45  * - Permit reordering of array elements
46  */
47
48 #include <config.h>
49
50 #include <ctype.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <time.h>
54 #include "file.h"
55 #include "match.h"
56 #include "nsc.h"
57 #include "optlist.h"
58 #include "prototypes.h"
59
60 static char *fname;
61 static int lineno;
62 static int human;
63 static int ellipsis, is_partial;
64 static int cur_type, cur_id;
65 static void *cur_obj;
66 static int cur_obj_is_blank;
67 static int nflds;
68 static struct castr **fldca;
69 static int *fldidx;
70 static int *caflds;             /* Map selector number to #fields seen */
71 static int *cafldspp;           /* ditto, in previous parts */
72
73 static int gripe(char *, ...) ATTRIBUTE((format (printf, 1, 2)));
74 static int deffld(int, char *, int);
75 static int defellipsis(int fldno);
76 static int chkflds(void);
77 static int setnum(int, double);
78 static int setstr(int, char *);
79 static int xunsymbol(char *, struct castr *, int);
80 static int setsym(int, char *);
81 static int mtsymset(int, long *);
82 static int add2symset(int, long *, char *);
83 static int xubody(FILE *);
84 static int xutail(FILE *, struct castr *);
85
86 static int
87 gripe(char *fmt, ...)
88 {
89     va_list ap;
90
91     fprintf(stderr, "%s:%d: ", fname, lineno);
92     va_start(ap, fmt);
93     vfprintf(stderr, fmt, ap);
94     va_end(ap);
95     putc('\n', stderr);
96
97     return -1;
98 }
99
100 static int
101 skipfs(FILE *fp)
102 {
103     int ch;
104
105     do {
106         ch = getc(fp);
107     } while (ch == ' ' || ch == '\t');
108
109     if (ch == '#') {
110         do {
111             ch = getc(fp);
112         } while (ch != EOF && ch != '\n');
113     }
114
115     return ch;
116 }
117
118 static int
119 getid(FILE *fp, char *buf)
120 {
121     int n;
122     if (fscanf(fp, "%1023[^#()<>=#\" \t\n]%n", buf, &n) != 1
123         || !isalpha(buf[0]))
124         return -1;
125     return n;
126 }
127
128 static char *
129 xuesc(char *buf)
130 {
131     char *src, *dst;
132     int octal_chr, n;
133
134     dst = buf;
135     src = buf;
136     while (*src) {
137         if (*src == '\\') {
138             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
139                 return NULL;
140             *dst++ = (char)octal_chr;
141             src += 3;
142         } else
143             *dst++ = *src++;
144     }
145     *dst = '\0';
146     return buf;
147 }
148
149 static int
150 xufldname(FILE *fp, int i)
151 {
152     int ch, idx;
153     char buf[1024];
154
155     ch = skipfs(fp);
156     switch (ch) {
157     case EOF:
158         return gripe("Unexpected EOF");
159     case '\n':
160         if (chkflds() < 0)
161             return -1;
162         lineno++;
163         return 0;
164     case '.':
165         if (getc(fp) != '.' || getc(fp) != '.')
166             return gripe("Junk in header field %d", i + 1);
167         if (i == 0)
168             return gripe("... not allowed in field 1");
169         if (defellipsis(i) < 0)
170             return -1;
171         ch = skipfs(fp);
172         if (ch != EOF && ch != '\n')
173             return gripe("Junk after ...");
174         ungetc(ch, fp);
175         return 1;
176     default:
177         ungetc(ch, fp);
178         if (getid(fp, buf) < 0)
179             return gripe("Junk in header field %d", i + 1);
180         ch = getc(fp);
181         if (ch != '(') {
182             ungetc(ch, fp);
183             return deffld(i, buf, -1);
184         }
185         ch = getc(fp);
186         ungetc(ch, fp);
187         if (isdigit(ch) || ch == '-' || ch == '+') {
188             if (fscanf(fp, "%d", &idx) != 1)
189                 return gripe("Malformed number in index of header field %d",
190                              i + 1);
191             if (idx < 0)
192                 return gripe("Index must not be negative in header field %d",
193                              i + 1);
194         } else {
195             if (getid(fp, buf) < 0)
196                 return gripe("Malformed index in header field %d", i + 1);
197             return gripe("Symbolic index in header field %d not yet implemented",
198                          i + 1);
199         }
200         ch = getc(fp);
201         if (ch != ')')
202             return gripe("Malformed index in header field %d", i + 1);
203         return deffld(i, buf, idx);
204     }
205 }
206
207 static int
208 xufld(FILE *fp, int i)
209 {
210     int ch;
211     char buf[1024];
212     double dbl;
213     long set;
214
215     ch = skipfs(fp);
216     switch (ch) {
217     case EOF:
218         return gripe("Unexpected EOF");
219     case '\n':
220         if (i != nflds) {
221             if (fldca[i]->ca_type != NSC_STRINGY && fldca[i]->ca_len)
222                 return gripe("Field %s(%d) missing",
223                              fldca[i]->ca_name, fldidx[i]);
224             return gripe("Field %s missing", fldca[i]->ca_name);
225         }
226         lineno++;
227         return 0;
228     case '+': case '-': case '.':
229     case '0': case '1': case '2': case '3': case '4':
230     case '5': case '6': case '7': case '8': case '9':
231         ungetc(ch, fp);
232         if (fscanf(fp, "%lg", &dbl) != 1)
233             return gripe("Malformed number in field %d", i + 1);
234         return setnum(i, dbl);
235     case '"':
236         ch = getc(fp);
237         if (ch == '"')
238             buf[0] = 0;
239         else {
240             ungetc(ch, fp);
241             if (fscanf(fp, "%1023[^\"\n]", buf) != 1 || getc(fp) != '"')
242                 return gripe("Malformed string in field %d", i + 1);
243             if (!xuesc(buf))
244                 return gripe("Invalid escape sequence in field %d",
245                              i + 1);
246         }
247         return setstr(i, buf);
248     case '(':
249         if (mtsymset(i, &set) < 0)
250             return -1;
251         for (;;) {
252             ch = skipfs(fp);
253             if (ch == EOF || ch == '\n')
254                 return gripe("Unmatched '(' in field %d", i + 1);
255             if (ch == ')')
256                 break;
257             ungetc(ch, fp);
258             if (getid(fp, buf) < 0)
259                 return gripe("Junk in field %d", i + 1);
260             if (add2symset(i, &set, buf) < 0)
261                 return -1;
262         }
263         return setnum(i, set);
264     default:
265         ungetc(ch, fp);
266         if (getid(fp, buf) < 0)
267             return gripe("Junk in field %d", i + 1);
268         if (!strcmp(buf, "nil"))
269             return setstr(i, NULL);
270         else
271             return setsym(i, buf);
272     }
273 }
274
275 static int
276 xuflds(FILE *fp, int (*parse)(FILE *, int))
277 {
278     int i, ch, res;
279
280     for (i = 0; ; i++) {
281         res = parse(fp, i);
282         if (res < 0)
283             return -1;
284         if (res == 0)
285             return i;
286         ch = getc(fp);
287         if (ch == '\n')
288             ungetc(ch, fp);
289         else if (ch != ' ' && ch != '\t')
290             return gripe("Bad field separator after field %d", i + 1);
291     }
292 }
293
294 static int
295 deffld(int fldno, char *name, int idx)
296 {
297     struct castr *ca = ef_cadef(cur_type);
298     int res;
299
300     res = stmtch(name, ca, offsetof(struct castr, ca_name),
301                      sizeof(struct castr));
302     if (res < 0)
303         return gripe("Header %s of field %d is %s", name, fldno + 1,
304                      res == M_NOTUNIQUE ? "ambiguous" : "unknown");
305     if (ca[res].ca_type != NSC_STRINGY && ca[res].ca_len != 0) {
306         if (idx < 0)
307             return gripe("Header %s requires an index in field %d",
308                          ca[res].ca_name, fldno + 1);
309         if (idx >= ca[res].ca_len)
310             return gripe("Header %s(%d) index out of bounds in field %d",
311                          ca[res].ca_name, idx, fldno + 1);
312         if (idx < caflds[res])
313             return gripe("Duplicate header %s(%d) in field %d",
314                          ca[res].ca_name, idx, fldno + 1);
315         if (idx > caflds[res])
316             return gripe("Expected header %s(%d) in field %d",
317                          ca[res].ca_name, caflds[res], fldno + 1);
318     } else {
319         if (idx >= 0)
320             return gripe("Header %s doesn't take an index in field %d",
321                          ca[res].ca_name, fldno + 1);
322         idx = 0;
323         if (caflds[res])
324             return gripe("Duplicate header %s in field %d",
325                          ca[res].ca_name, fldno + 1);
326     }
327     fldca[fldno] = &ca[res];
328     fldidx[fldno] = idx;
329     caflds[res]++;
330     return 1;
331 }
332
333 static int
334 defellipsis(int fldno)
335 {
336     struct castr *ca = ef_cadef(cur_type);
337
338     if (ca[0].ca_table != cur_type)
339         return gripe("Table %s doesn't support ...", ef_nameof(cur_type));
340     ellipsis = fldno;
341     is_partial = 1;
342     return 0;
343 }
344
345 static int
346 chkflds(void)
347 {
348     struct castr *ca = ef_cadef(cur_type);
349     int i, len, cafldsmax, res = 0;
350
351     if (is_partial) {
352         /* Need a join field, use 0-th selector */
353         if (!caflds[0])
354             return gripe("Header field %s required with ...", ca[0].ca_name);
355     }
356
357     if (ellipsis)
358         return 0;
359
360     for (i = 0; ca[i].ca_name; i++) {
361         cafldsmax = MAX(caflds[i], cafldspp[i]);
362         if (ca[i].ca_flags & NSC_EXTRA)
363             continue;
364         len = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
365         if (!len && !cafldsmax)
366             res = gripe("Header field %s missing", ca[i].ca_name);
367         else if (len && cafldsmax == len - 1)
368             res = gripe("Header field %s(%d) missing",
369                         ca[i].ca_name, len - 1);
370         else if (len && cafldsmax < len - 1)
371             res = gripe("Header fields %s(%d) ... %s(%d) missing",
372                         ca[i].ca_name, cafldsmax, ca[i].ca_name, len - 1);
373     }
374
375     return res;
376 }
377
378 static struct castr *
379 getfld(int fldno, int *idx)
380 {
381     if (fldno >= nflds) {
382         gripe("Too many fields, expected only %d", nflds);
383         return NULL;
384     }
385     if (CANT_HAPPEN(fldno < 0))
386         return NULL;
387     if (idx)
388         *idx = fldidx[fldno];
389     return fldca[fldno];
390 }
391
392 static int
393 fldval_must_match(int fldno)
394 {
395     struct castr *ca = ef_cadef(cur_type);
396     int i = fldca[fldno] - ca;
397
398     /*
399      * Value must match if:
400      * it's for a const selector, unless the object is still blank, or
401      * it was already given in a previous part of a split table.
402      */
403     return (!cur_obj_is_blank && (fldca[fldno]->ca_flags & NSC_CONST))
404         || fldidx[fldno] < cafldspp[i];
405 }
406
407 static void *
408 getobj(void)
409 {
410     struct empfile *ep = &empfile[cur_type];
411     int need_sentinel = !EF_IS_GAME_STATE(cur_type);
412
413     if (!cur_obj) {
414         cur_obj_is_blank = cur_id >= ep->fids;
415         if (cur_obj_is_blank) {
416             /* TODO grow cache (and posssibly file) unless EFF_STATIC */
417             if (cur_id < ep->csize - !!need_sentinel)
418                 ep->cids = ep->fids = cur_id + 1;
419             /* else: ef_ptr() will fail */
420         }
421         cur_obj = ef_ptr(cur_type, cur_id);
422         if (!cur_obj)
423             gripe("Can't put ID %d into table %s, it holds only 0..%d.",
424                   cur_id, ep->name, ep->fids - 1);
425     }
426
427     return cur_obj;
428 }
429
430 static int
431 setnum(int fldno, double dbl)
432 {
433     struct castr *ca;
434     int idx;
435     char *memb_ptr;
436     double old;
437
438     ca = getfld(fldno, &idx);
439     if (!ca)
440         return -1;
441
442     /*
443      * If this is the record index, put it into cur_id.
444      */
445     if (fldno == 0 && ca->ca_table == cur_type)
446         cur_id = (int)dbl;
447
448     memb_ptr = getobj();
449     if (!memb_ptr)
450         return -1;
451     memb_ptr += ca->ca_off;
452
453     /* FIXME check assignment preserves value */
454     switch (ca->ca_type) {
455     case NSC_CHAR:
456         old = ((signed char *)memb_ptr)[idx];
457         ((signed char *)memb_ptr)[idx] = (signed char)dbl;
458         break;
459     case NSC_UCHAR:
460     case NSC_HIDDEN:
461         old = ((unsigned char *)memb_ptr)[idx];
462         ((unsigned char *)memb_ptr)[idx] = (unsigned char)dbl;
463         break;
464     case NSC_SHORT:
465         old = ((short *)memb_ptr)[idx];
466         ((short *)memb_ptr)[idx] = (short)dbl;
467         break;
468     case NSC_USHORT:
469         old = ((unsigned short *)memb_ptr)[idx];
470         ((unsigned short *)memb_ptr)[idx] = (unsigned short)dbl;
471         break;
472     case NSC_INT:
473         old = ((int *)memb_ptr)[idx];
474         ((int *)memb_ptr)[idx] = (int)dbl;
475         break;
476     case NSC_LONG:
477         old = ((long *)memb_ptr)[idx];
478         ((long *)memb_ptr)[idx] = (long)dbl;
479         break;
480     case NSC_XCOORD:
481         old = ((coord *)memb_ptr)[idx];
482         /* FIXME use variant of xrel() that takes orig instead of nation */
483         if (old >= WORLD_X / 2)
484             old -= WORLD_X;
485         ((coord *)memb_ptr)[idx] = XNORM((coord)dbl);
486         break;
487     case NSC_YCOORD:
488         old = ((coord *)memb_ptr)[idx];
489         /* FIXME use variant of yrel() that takes orig instead of nation */
490         if (old >= WORLD_Y / 2)
491             old -= WORLD_Y;
492         ((coord *)memb_ptr)[idx] = YNORM((coord)dbl);
493         break;
494     case NSC_FLOAT:
495         old = ((float *)memb_ptr)[idx];
496         ((float *)memb_ptr)[idx] = (float)dbl;
497         break;
498     case NSC_DOUBLE:
499         old = ((double *)memb_ptr)[idx];
500         ((double *)memb_ptr)[idx] = dbl;
501         break;
502     case NSC_TIME:
503         old = ((time_t *)memb_ptr)[idx];
504         ((time_t *)memb_ptr)[idx] = (time_t)dbl;
505         break;
506     default:
507         return gripe("Field %d doesn't take numbers", fldno + 1);
508     }
509
510     if (fldval_must_match(fldno) && old != dbl)
511         return gripe("Value for field %d must be %g", fldno + 1, old);
512
513     return 1;
514 }
515
516 static int
517 setstr(int fldno, char *str)
518 {
519     struct castr *ca;
520     int must_match, idx;
521     size_t len;
522     char *memb_ptr, *old;
523
524     ca = getfld(fldno, &idx);
525     if (!ca)
526         return -1;
527
528     memb_ptr = getobj();
529     if (!memb_ptr)
530         return -1;
531     memb_ptr += ca->ca_off;
532     must_match = fldval_must_match(fldno);
533
534     switch (ca->ca_type) {
535     case NSC_STRING:
536         old = ((char **)memb_ptr)[idx];
537         if (!must_match)
538             ((char **)memb_ptr)[idx] = str ? strdup(str) : NULL;
539         len = 65535;            /* really SIZE_MAX, but that's C99 */
540         break;
541     case NSC_STRINGY:
542         if (CANT_HAPPEN(idx))
543             return -1;
544         if (!str)
545             return gripe("Field %d doesn't take nil", fldno + 1);
546         len = ca->ca_len;
547         if (strlen(str) > len)
548             return gripe("Field %d takes at most %d characters",
549                          fldno + 1, (int)len);
550         old = memb_ptr;
551         if (!must_match)
552             strncpy(memb_ptr, str, len);
553         break;
554     default:
555         return gripe("Field %d doesn't take strings", fldno + 1);
556     }
557
558     if (must_match) {
559         if (old && (!str || strncmp(old, str, len)))
560             return gripe("Value for field %d must be \"%.*s\"",
561                          fldno + 1, (int)len, old);
562         if (!old && str)
563             return gripe("Value for field %d must be nil", fldno + 1);
564     }
565
566     return 1;
567 }
568
569 static int
570 xunsymbol(char *id, struct castr *ca, int n)
571 {
572     int i = ef_elt_byname(ca->ca_table, id);
573     if (i < 0)
574         return gripe("%s %s symbol `%s' in field %d",
575                      i == M_NOTUNIQUE ? "Ambiguous" : "Unknown",
576                      ca->ca_name, id, n + 1);
577     return i;
578 }
579
580 static int
581 symval(struct castr *ca, int i)
582 {
583     int type = ca->ca_table;
584
585     if (ef_check(type) < 0)
586         return -1;
587     if (ef_cadef(type) == symbol_ca)
588         /* symbol table, value is in the table */
589         return ((struct symbol *)ef_ptr(type, i))->value;
590     /* value is the table index */
591     return i;
592 }
593
594 static int
595 setsym(int fldno, char *sym)
596 {
597     struct castr *ca;
598     int i;
599
600     ca = getfld(fldno, NULL);
601     if (!ca)
602         return -1;
603
604     if (ca->ca_table == EF_BAD || (ca->ca_flags & NSC_BITS))
605         return gripe("Field %d doesn't take symbols", fldno + 1);
606
607     i = xunsymbol(sym, ca, fldno);
608     if (i < 0)
609         return -1;
610     return setnum(fldno, symval(ca, i));
611 }
612
613 static int
614 mtsymset(int fldno, long *set)
615 {
616     struct castr *ca;
617
618     ca = getfld(fldno, NULL);
619     if (!ca)
620         return -1;
621
622     if (ca->ca_table == EF_BAD || ef_cadef(ca->ca_table) != symbol_ca
623         || !(ca->ca_flags & NSC_BITS))
624         return gripe("Field %d doesn't take symbol sets", fldno + 1);
625     *set = 0;
626     return 0;
627 }
628
629 static int
630 add2symset(int fldno, long *set, char *sym)
631 {
632     struct castr *ca;
633     int i;
634
635     ca = getfld(fldno, NULL);
636     if (!ca)
637         return -1;
638
639     i = xunsymbol(sym, ca, fldno);
640     if (i < 0)
641         return -1;
642     *set |= symval(ca, i);
643     return 0;
644 }
645
646 static int
647 xuheader(FILE *fp, int expected_table)
648 {
649     char name[64];
650     int res, ch;
651     int type;
652
653     while ((ch = skipfs(fp)) == '\n')
654         lineno++;
655     if (ch == EOF && expected_table == EF_BAD)
656         return -2;
657     ungetc(ch, fp);
658
659     human = ch == 'c';
660     res = -1;
661     if ((human
662          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
663          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
664                   name, &res) != 1) || res < 0)
665         return gripe("Expected xdump header");
666
667     type = ef_byname(name);
668     if (type < 0)
669         return gripe("Unknown table `%s'", name);
670     if (expected_table != EF_BAD && expected_table != type)
671         return gripe("Expected table `%s', not `%s'",
672                      ef_nameof(expected_table), name);
673
674     if (!ef_cadef(type) || !(ef_flags(type) & EFF_MEM)) {
675         CANT_HAPPEN(expected_table != EF_BAD);
676         return gripe("Table `%s' is not permitted here", name);
677     }
678
679     if (skipfs(fp) != '\n')
680         return gripe("Junk after xdump header");
681     lineno++;
682
683     return type;
684 }
685
686 static int
687 xufldhdr(FILE *fp, struct castr ca[])
688 {
689     struct castr **fca;
690     int *fidx;
691     int ch, i, j, n;
692
693     for (i = 0; ca[i].ca_name; i++)
694         caflds[i] = 0;
695     ellipsis = 0;
696
697     if (human) {
698         while ((ch = skipfs(fp)) == '\n')
699             lineno++;
700         ungetc(ch, fp);
701         nflds = xuflds(fp, xufldname);
702         if (nflds < 0)
703             return -1;
704         nflds -= ellipsis != 0;
705     } else {
706         fca = fldca;
707         fidx = fldidx;
708
709         for (i = 0; ca[i].ca_name; i++) {
710             if ((ca[i].ca_flags & NSC_EXTRA))
711                 continue;
712             n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
713             j = 0;
714             do {
715                 *fca++ = &ca[i];
716                 *fidx++ = j;
717             } while (++j < n);
718         }
719
720         nflds = fidx - fldidx;
721     }
722
723     return 0;
724 }
725
726 static int
727 xufooter(FILE *fp, struct castr ca[], int row)
728 {
729     int res, rows, i;
730
731     res = -1;
732     if (human) {
733         if (fscanf(fp, "config%n",  &res) != 0 || res < 0)
734             return gripe("Malformed table footer");
735     } else {
736         if (fscanf(fp, "%d", &rows) != 1)
737             return gripe("Malformed table footer");
738         if (row != rows)
739             return gripe("Read %d rows, which doesn't match footer "
740                          "%d rows", row, rows);
741     }
742     if (skipfs(fp) != '\n')
743         return gripe("Junk after table footer");
744     lineno++;
745
746     for (i = 0; ca[i].ca_name; i++) {
747         if (cafldspp[i] < caflds[i])
748             cafldspp[i] = caflds[i];
749     }
750
751     return 0;
752 }
753
754 int
755 xundump(FILE *fp, char *file, int *plno, int expected_table)
756 {
757     struct castr *ca;
758     int type, nca, nf, i, ch;
759
760     fname = file;
761     lineno = *plno;
762
763     if ((type = xuheader(fp, expected_table)) < 0)
764         return type;
765
766     ca = ef_cadef(type);
767     if (CANT_HAPPEN(!ca))
768         return -1;
769
770     nca = nf = 0;
771     for (i = 0; ca[i].ca_name; i++) {
772         nca++;
773         if (!(ca[i].ca_flags & NSC_EXTRA))
774             nf += MAX(1, ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0);
775     }
776     fldca = calloc(nf, sizeof(*fldca));
777     fldidx = calloc(nf, sizeof(*fldidx));
778     caflds = malloc(nca * sizeof(*caflds));
779     cafldspp = calloc(nca, sizeof(*cafldspp));
780     cur_type = type;
781
782     if (xutail(fp, ca) < 0)
783         type = EF_BAD;
784
785     free(cafldspp);
786     free(caflds);
787     free(fldidx);
788     free(fldca);
789
790     /* Skip empty lines so that callers can easily check for EOF */
791     while ((ch = skipfs(fp)) == '\n')
792         lineno++;
793     ungetc(ch, fp);
794
795     *plno = lineno;
796     return type;
797 }
798
799 static int
800 xutail(FILE *fp, struct castr *ca)
801 {
802     int recs;
803
804     is_partial = 0;
805     for (;;) {
806         if (xufldhdr(fp, ca) < 0)
807             return -1;
808         if ((recs = xubody(fp)) < 0)
809             return -1;
810         if (xufooter(fp, ca, recs) < 0)
811             return -1;
812         if (!ellipsis)
813             return 0;
814         if (xuheader(fp, cur_type) < 0)
815             return -1;
816     }
817 }
818
819 static int
820 xubody(FILE *fp)
821 {
822     struct empfile *ep = &empfile[cur_type];
823     int need_sentinel = !EF_IS_GAME_STATE(cur_type);
824     int row, n, ch;
825
826     n = 0;
827     for (row = 0;; ++row) {
828         while ((ch = skipfs(fp)) == '\n')
829             lineno++;
830         if (ch == '/')
831             break;
832         ungetc(ch, fp);
833         cur_obj = NULL;
834         cur_id = row;
835         if (xuflds(fp, xufld) < 0)
836             return -1;
837         n = MAX(n, cur_id + 1);
838     }
839
840     if (CANT_HAPPEN(n > ep->fids))
841         n = ep->fids;
842     if (n < ep->fids) {
843         if (EF_IS_GAME_STATE(cur_type) && n != ep->csize)
844             /* TODO truncate file */
845             gripe("Warning: should resize table %s from %d to %d, not implemented",
846                   ef_nameof(cur_type), ep->csize, n);
847         else if (cur_type >= EF_SHIP_CHR && cur_type <= EF_NUKE_CHR)
848             ep->cids = ep->fids = n;
849         else
850             return gripe("Table %s requires %d rows, got %d",
851                          ef_nameof(cur_type), ep->fids, n);
852     }
853
854     if (need_sentinel) {
855         if (CANT_HAPPEN(n >= ep->csize))
856             return gripe("No space for sentinel");
857         memset(ep->cache + ep->size * n, 0, ep->size);
858     }
859
860     return row;
861 }