]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
(xufldname, xufld): New, factored out of xuflds and specialized for
[empserver] / src / lib / common / xundump.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  xundump.c: Load back xdump output
29  * 
30  *  Known contributors to this file:
31  *     Ron Koenderink, 2005
32  *     Markus Armbruster, 2005
33  */
34
35 /* FIXME normalize terminology: table/rows/columns or file/records/fields */
36
37 #include <config.h>
38
39 #include <stdio.h>
40 #include <stdlib.h>
41
42 #include <ctype.h>
43 #include <string.h>
44 #include <stdarg.h>
45 #include <time.h>
46
47 #include "prototypes.h"
48 #include "file.h"
49 #include "nsc.h"
50 #include "match.h"
51
52 #define MAX_NUM_COLUMNS 256
53
54 static char *fname = "";
55 static int lineno = 0;
56 static int human;
57
58 /*
59  * TODO
60  * This structure could be replaced with struct valstr.
61  */
62 enum enum_value {
63     VAL_NOTUSED,
64     VAL_STRING,         /* uses v_string */
65     VAL_SYMBOL,         /* uses v_string */
66     VAL_SYMBOL_SET,     /* uses v_string */
67     VAL_INDEX_ID,       /* uses v_index_name and v_int */
68     VAL_INDEX_SYMBOL,   /* uses v_index_name and v_string */
69     VAL_DOUBLE          /* uses v_double */
70 };
71
72 struct value {
73     enum enum_value v_type;
74     union {
75         char *v_string;
76         double v_double;
77         int v_int;
78     } v_field;
79     char *v_index_name;
80 };
81
82 static int gripe(char *fmt, ...) ATTRIBUTE((format (printf, 1, 2)));
83
84 static int
85 gripe(char *fmt, ...)
86 {
87     va_list ap;
88
89     fprintf(stderr, "%s:%d: ", fname, lineno);
90     va_start(ap, fmt);
91     vfprintf(stderr, fmt, ap);
92     va_end(ap);
93     putc('\n', stderr);
94
95     return -1;
96 }
97
98 static int
99 skipfs(FILE *fp)
100 {
101     int ch;
102
103     do {
104         ch = getc(fp);
105     } while (ch == ' ' || ch == '\t');
106
107     if (ch == '#') {
108         do {
109             ch = getc(fp);
110         } while (ch != EOF && ch != '\n');
111     }
112
113     return ch;
114 }
115
116 static int
117 getid(FILE *fp, char *buf)
118 {
119     int n;
120     if (fscanf(fp, "%1023[^#() \t\n]%n", buf, &n) != 1 || !isalpha(buf[0]))
121         return -1;
122     return n;
123 }
124
125 static char *
126 xuesc(char *buf)
127 {
128     char *src, *dst;
129     int octal_chr, n;
130
131     dst = buf;
132     src = buf;
133     while (*src) {
134         if (*src == '\\') {
135             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
136                 return NULL;
137             *dst++ = (char)octal_chr;
138             src += 3;
139         } else
140             *dst++ = *src++;
141     }
142     *dst = '\0';
143     return buf;
144 }
145
146 static int
147 xufldname(FILE *fp, struct value values[], int i)
148 {
149     int ch;
150     char buf[1024];
151
152     values[i].v_type = VAL_NOTUSED;
153
154     ch = skipfs(fp);
155     switch (ch) {
156     case EOF:
157         return gripe("Unexpected EOF");
158     case '\n':
159         return 0;
160     default:
161         ungetc(ch, fp);
162         if (getid(fp, buf) < 0)
163             return gripe("Junk in field %d", i + 1);
164         ch = getc(fp);
165         if (ch != '(') {
166             ungetc(ch, fp);
167             values[i].v_type = VAL_SYMBOL;
168             values[i].v_field.v_string = strdup(buf);
169             return 1;
170         }
171         ch = getc(fp);
172         ungetc(ch, fp);
173         if (isdigit(ch) || ch == '-' || ch == '+') {
174             if (fscanf(fp, "%d", &values[i].v_field.v_int) != 1) {
175                 return gripe("Malformed number in index field %d", i + 1);
176             }
177         } else {
178             if (getid(fp, buf) < 0)
179                 return gripe("Malformed string in index field %d", i + 1);
180             return gripe("Symbolic index in field %d not yet implemented",
181                          i + 1);
182         }
183         ch = getc(fp);
184         if (ch != ')')
185             return gripe("Malformed index field %d", i + 1);
186         values[i].v_index_name = strdup(buf);
187         values[i].v_type = VAL_INDEX_ID;
188         return 1;
189     }
190 }
191
192 static int
193 xufld(FILE *fp, struct value values[], int i)
194 {
195     int ch;
196     char buf[1024];
197     char *p;
198     int len, l1;
199
200     values[i].v_type = VAL_NOTUSED;
201
202     ch = skipfs(fp);
203     switch (ch) {
204     case EOF:
205         return gripe("Unexpected EOF");
206     case '\n':
207         return 0;
208     case '+': case '-': case '.':
209     case '0': case '1': case '2': case '3': case '4':
210     case '5': case '6': case '7': case '8': case '9':
211         ungetc(ch, fp);
212         if (fscanf(fp, "%lg", &values[i].v_field.v_double) != 1)
213             return gripe("Malformed number in field %d", i + 1);
214         values[i].v_type = VAL_DOUBLE;
215         return 1;
216     case '"':
217         ch = getc(fp);
218         if (ch == '"')
219             buf[0] = 0;
220         else {
221             ungetc(ch, fp);
222             if (fscanf(fp, "%1023[^\"\n]", buf) != 1 || getc(fp) != '"')
223                 return gripe("Malformed string in field %d", i + 1);
224             if (!xuesc(buf))
225                 return gripe("Invalid escape sequence in field %d",
226                              i + 1);
227         }
228         values[i].v_type = VAL_STRING;
229         values[i].v_field.v_string = strdup(buf);
230         return 1;
231     case '(':
232         p = strdup("");
233         len = 0;
234         for (;;) {
235             ch = skipfs(fp);
236             if (ch == EOF || ch == '\n')
237                 return gripe("Unmatched '(' in field %d", i + 1);
238             if (ch == ')')
239                 break;
240             ungetc(ch, fp);
241             l1 = getid(fp, buf);
242             if (l1 < 0)
243                 return gripe("Junk in field %d", i + 1);
244             p = realloc(p, len + l1 + 2);
245             strcpy(p + len, buf);
246             strcpy(p + len + l1, " ");
247             len += l1 + 1;
248         }
249         if (len)
250             p[len - 1] = 0;
251         values[i].v_type = VAL_SYMBOL_SET;
252         values[i].v_field.v_string = p;
253         return 1;
254     default:
255         ungetc(ch, fp);
256         if (getid(fp, buf) < 0)
257             return gripe("Junk in field %d", i + 1);
258         if (!strcmp(buf, "nil")) {
259             values[i].v_type = VAL_STRING;
260             values[i].v_field.v_string = NULL;
261         } else {
262             values[i].v_type = VAL_SYMBOL;
263             values[i].v_field.v_string = strdup(buf);
264         }
265         return 1;
266     }
267 }
268
269 static int
270 xuflds(FILE *fp, struct value values[],
271        int (*parse)(FILE *, struct value values[], int))
272 {
273     int i, ch, res;
274
275     for (i = 0; ; i++) {
276         if (i >= MAX_NUM_COLUMNS)
277             return gripe("Too many columns");
278         res = parse(fp, values, i);
279         if (res < 0)
280             return -1;
281         if (res == 0)
282             return i;
283         ch = getc(fp);
284         if (ch == '\n')
285             ungetc(ch, fp);
286         else if (ch != ' ' && ch != '\t')
287             return gripe("Bad field separator after field %d", i + 1);
288     }
289 }
290
291 static void
292 freeflds(struct value values[])
293 {
294     struct value *vp;
295
296     for (vp = values; vp->v_type != VAL_NOTUSED; vp++) {
297         if (vp->v_type != VAL_DOUBLE && vp->v_type != VAL_INDEX_ID)
298             free(vp->v_field.v_string);
299     }
300     free(vp->v_index_name);
301     vp->v_index_name = NULL;
302 }
303
304 static int
305 xunsymbol1(char *id, struct symbol *symtab, struct castr *ca, int n)
306 {
307     int i = stmtch(id, symtab, offsetof(struct symbol, name),
308                    sizeof(struct symbol));
309     if (i < 0)
310         return gripe("%s %s symbol `%s' in field %d",
311                      i == M_NOTUNIQUE ? "Ambiguous" : "Unknown",
312                      ca->ca_name, id, n);
313     return i;
314 }
315
316 static int
317 xunsymbol(struct castr *ca, struct value *vp, int n)
318 {
319     struct symbol *symtab = (struct symbol *)empfile[ca->ca_table].cache;
320     char *buf = vp->v_field.v_string;
321     int i;
322     int value;
323     char *token;
324
325     if (ca->ca_table == EF_BAD || ef_cadef(ca->ca_table) != symbol_ca)
326         return gripe("%s doesn't take a symbol or symbol set in field %d",
327                      ca->ca_name, n);
328
329     if (vp->v_type == VAL_SYMBOL_SET) {
330         if (!(ca->ca_flags & NSC_BITS))
331             return gripe("%s doesn't take a symbol set in field %d",
332                          ca->ca_name, n);
333         value = 0;
334         for (token = strtok(buf, " "); token; token = strtok(NULL, " ")) {
335             i = xunsymbol1(token, symtab, ca, n);
336             if (i < 0)
337                 return -1;
338             value |= symtab[i].value;
339         }
340     } else if (vp->v_type == VAL_SYMBOL) {
341         if (ca->ca_flags & NSC_BITS)
342             return gripe("%s doesn't take a symbol in field %d",
343                          ca->ca_name, n);
344         i = xunsymbol1(buf, symtab, ca, n);
345         if (i < 0)
346             return -1;
347         value = symtab[i].value;
348     } else
349         return 0;
350
351     vp->v_type = VAL_DOUBLE;
352     vp->v_field.v_double = value;
353     return 0;
354 }
355
356 static int
357 has_const(struct castr ca[])
358 {
359     int i;
360
361     for (i = 0; ca[i].ca_name; i++) {
362         if (ca[i].ca_flags & NSC_CONST)
363             return 1;
364     }
365     return 0;
366 }
367
368 static void
369 xuinitrow(int type, int row)
370 {
371     struct empfile *ep = &empfile[type];
372     char *ptr = ep->cache + ep->size * row;
373
374     memset(ptr, 0, ep->size);
375
376     if (ep->init)
377         ep->init(row, ptr);
378 }
379
380 static int
381 xuloadrow(int type, int row, struct value values[])
382 {
383     int i,j,k;
384     struct empfile *ep = &empfile[type];
385     char *ptr = ep->cache + ep->size * row;
386     struct castr *ca = ep->cadef;
387     void *row_ref;
388
389     i = 0;
390     j = 0;
391     while (ca[i].ca_type != NSC_NOTYPE &&
392            values[j].v_type != VAL_NOTUSED) {
393         if (ca[i].ca_flags & NSC_EXTRA) {
394             i++;
395             continue;
396         }
397         row_ref = (char *)ptr + ca[i].ca_off;
398         k = 0;
399         do {
400             /*
401              * TODO
402              * factor out NSC_CONST comparsion
403              */
404             switch (values[j].v_type) {
405             case VAL_SYMBOL:
406             case VAL_SYMBOL_SET:
407                 if (xunsymbol(&ca[i], &values[j], j) < 0)
408                     return -1;
409                 /* fall through */
410             case VAL_DOUBLE:
411                 switch (ca[i].ca_type) {
412                 case NSC_INT:
413                     if (ca[i].ca_flags & NSC_CONST) {
414                         if (((int *)row_ref)[k] != (int)
415                              values[j].v_field.v_double)
416                             gripe("Field %s must be same, "
417                                 "read %d != expected %d",
418                                 ca[i].ca_name,
419                                 ((int *)row_ref)[k],
420                                 (int)values[j].v_field.v_double);
421
422                     } else
423                         ((int *)row_ref)[k] =
424                             (int)values[j].v_field.v_double;
425                     break;
426                 case NSC_LONG:
427                     if (ca[i].ca_flags & NSC_CONST) {
428                         if (((long *)row_ref)[k] != (long)
429                              values[j].v_field.v_double)
430                             gripe("Field %s must be same, "
431                                 "read %ld != expected %ld",
432                                 ca[i].ca_name,
433                                 ((long *)row_ref)[k],
434                                 (long)values[j].v_field.v_double);
435                     } else
436                         ((long *)row_ref)[k] = (long)
437                             values[j].v_field.v_double;
438                     break;
439                 case NSC_SHORT:
440                     if (ca[i].ca_flags & NSC_CONST) {
441                         if (((short *)row_ref)[k] !=
442                              (short)values[j].v_field.v_double)
443                             gripe("Field %s must be same, "
444                                 "read %d != expected %d",
445                                 ca[i].ca_name,
446                                 ((short *)row_ref)[k],
447                                 (short)values[j].v_field.v_double);
448                     } else
449                         ((short *)row_ref)[k] = (short)
450                             values[j].v_field.v_double;
451                     break;
452                 case NSC_USHORT:
453                     if (ca[i].ca_flags & NSC_CONST) {
454                         if (((unsigned short *)row_ref)[k] !=
455                              (unsigned short)values[j].v_field.v_double)
456                             gripe("Field %s must be same, "
457                                 "read %d != expected %d",
458                                 ca[i].ca_name,
459                                 ((unsigned short *)row_ref)[k],
460                                 (unsigned short)values[j].v_field.v_double);
461                     } else
462                         ((unsigned short *)row_ref)[k] = (unsigned short)
463                             values[j].v_field.v_double;
464                     break;
465                 case NSC_UCHAR:
466                     if (ca[i].ca_flags & NSC_CONST) {
467                         if (((unsigned char *)row_ref)[k] != (unsigned char)
468                              values[j].v_field.v_double)
469                             gripe("Field %s must be same, "
470                                 "read %d != expected %d",
471                                 ca[i].ca_name,
472                                 ((unsigned char *)row_ref)[k],
473                                 (unsigned char)values[j].v_field.v_double);
474                     } else
475                         ((unsigned char *)row_ref)[k] = (unsigned char)
476                             values[j].v_field.v_double;
477                     break;
478                 case NSC_FLOAT:
479                     if (ca[i].ca_flags & NSC_CONST) {
480                         if (((float *)row_ref)[k] != (float)
481                              values[j].v_field.v_double)
482                             gripe("Field %s must be same, "
483                                 "read %g != expected %g",
484                                 ca[i].ca_name,
485                                 ((float *)row_ref)[k],
486                                 (float)values[j].v_field.v_double);
487                     } else
488                         ((float *)row_ref)[k] = (float)
489                             values[j].v_field.v_double;
490                     break;
491                 case NSC_STRING:
492                     return gripe("Field %s is a string type, "
493                         "but %lg was read which is a number",
494                         ca[i].ca_name, values[j].v_field.v_double);
495                 default:
496                     return gripe("Field %s's type %d is not supported",
497                         ca[i].ca_name, ca[i].ca_type);
498                 }
499                 break;
500             case VAL_STRING:
501                 switch(ca[i].ca_type) {
502                 case NSC_STRING:
503                     if (ca[i].ca_flags & NSC_CONST) {
504                         if (strcmp(((char **)row_ref)[k],
505                                    values[j].v_field.v_string) != 0)
506                             gripe("Field %s must be same, "
507                                 "read %s != expected %s",
508                                 ca[i].ca_name,
509                                 ((char **)row_ref)[k],
510                                 values[j].v_field.v_string);
511                     } else
512                         ((char **)row_ref)[k]
513                             = strdup(values[j].v_field.v_string);
514                     break;
515                 case NSC_INT:
516                 case NSC_LONG:
517                 case NSC_SHORT:
518                 case NSC_USHORT:
519                 case NSC_UCHAR:
520                 case NSC_FLOAT:
521                     return gripe("Field %s is a number type %d, "
522                         "but %s was read which is a string",
523                         ca[i].ca_name, ca[i].ca_type,
524                         values[j].v_field.v_string);
525                 default:
526                     return gripe("Field %s's type %d is not supported",
527                             ca[i].ca_name, ca[i].ca_type);
528                 }
529                 break;
530             case VAL_NOTUSED:
531                 return gripe("Missing column %s in file", ca[i].ca_name);
532             case VAL_INDEX_ID:
533             case VAL_INDEX_SYMBOL:
534                 return gripe("Index fields not supported in data rows in %s file",
535                     ca[i].ca_name);
536             default:
537                 return gripe("Unknown value type %d", values[j].v_type);
538             }
539             k++;
540             j++;
541         } while (k < ca[i].ca_len);
542         i++;
543     }
544     if (ca[i].ca_type != NSC_NOTYPE)
545         return gripe("Missing column %s in file", ca[i].ca_name);
546     switch  (values[j].v_type) {
547     case VAL_NOTUSED:
548         break;
549     case VAL_STRING:
550     case VAL_SYMBOL:
551     case VAL_SYMBOL_SET:
552         return gripe("Extra junk after the last column, read %s",
553             values[j].v_field.v_string);
554     case VAL_DOUBLE:
555         return gripe("Extra junk after the last column, read %lg",
556             values[j].v_field.v_double);
557     default:
558         return gripe("Extra junk after the last column, "
559             "unknown value type %d", values[j].v_type);
560     }
561     return 0;
562 }
563
564 static int
565 xuheader(FILE *fp, int expected_table)
566 {
567     char name[64];
568     struct empfile *ep;
569     int res, ch;
570     int type;
571
572     while ((ch = skipfs(fp)) == '\n')
573         lineno++;
574     if (ch == EOF && expected_table == EF_BAD)
575         return -1;
576     ungetc(ch, fp);
577
578     human = ch == 'c';
579     res = -1;
580     if ((human
581          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
582          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
583                   name, &res) != 1) || res < 0)
584         return gripe("Expected xdump header");
585
586     if (skipfs(fp) != '\n')
587         return gripe("Junk after xdump header");
588
589     type = ef_byname(name);
590     if (type < 0)
591         return gripe("Unknown table `%s'", name);
592     ep = &empfile[type];
593     if (CANT_HAPPEN(!(ep->flags & EFF_MEM)))
594         return -1;              /* not implemented */
595
596     if (expected_table != EF_BAD && expected_table != type)
597         return gripe("Expected table `%s', not `%s'",
598                      ef_nameof(expected_table), name);
599     return type;
600 }
601
602 static int
603 xucolumnheader(FILE *fp, int type, struct value values[])
604 {
605     char ch;
606     struct empfile *ep = &empfile[type];
607     struct castr *ca = ep->cadef;
608     int i, j, k;
609
610     if (!human)
611         return 0;
612
613     while ((ch = skipfs(fp)) == '\n');
614     ungetc(ch, fp);
615
616     /* FIXME parse column header */
617     if (xuflds(fp, values, xufldname) <= 0) {
618         freeflds(values);
619         return -1;
620     }
621     /* TODO
622      * check column count form xuflds()
623      */
624
625     j = 0;
626     for (i = 0; ca[i].ca_name; i++) {
627         if (ca[i].ca_flags & NSC_EXTRA)
628             continue;
629         k = 0;
630         do {
631             if (values[j].v_type == VAL_NOTUSED) {
632                 freeflds(values);
633                 return gripe("Not enough columns in the header for table %s",
634                              ef_nameof(type));
635             }
636             switch(values[j].v_type) {
637             case VAL_SYMBOL:
638                 if (ca[i].ca_len != 0) {
639                     freeflds(values);
640                     return gripe("Column %s is a not index format for table %s",
641                         ca[i].ca_name, ef_nameof(type));
642                 }
643                 if (strcmp(values[j].v_field.v_string, ca[i].ca_name) != 0) {
644                     gripe("Column name (%s) does not match header name (%s)",
645                         ca[i].ca_name, values[j].v_field.v_string);
646                     freeflds(values);
647                     return -1;
648                 }
649                 break;
650             case VAL_INDEX_SYMBOL:
651                     return gripe("Column %s is does not currently support index symbol format for table %s",
652                         ca[i].ca_name, ef_nameof(type));
653             case VAL_INDEX_ID:
654                 if (ca[i].ca_len == 0) {
655                     freeflds(values);
656                     return gripe("Column %s is in index format and should not be for table %s",
657                         ca[i].ca_name, ef_nameof(type));
658                 }
659                 if (values[j].v_field.v_int != k) {
660                     freeflds(values);
661                     return gripe("Column Array index %d does not match %d",
662                         values[j].v_field.v_int, k);
663                 }
664                 if (strcmp(values[j].v_index_name, ca[i].ca_name) != 0) {
665                     gripe("Column name (%s) does not match header name (%s)",
666                         ca[i].ca_name, values[j].v_field.v_string);
667                     freeflds(values);
668                     return -1;
669                 }
670                 break;
671             default:
672                 freeflds(values);
673                 return gripe("Column %s is a not string for table %s",
674                     ca[i].ca_name, ef_nameof(type));
675             }
676             j++;
677             k++;
678         } while (k < ca[i].ca_len);
679     }
680     if (values[j].v_type != VAL_NOTUSED) {
681         freeflds(values);
682         return gripe("Too many columns in the header for table %s",
683             ef_nameof(type));
684     }
685     lineno++;
686     freeflds(values);
687     return 0;
688 }
689
690 static int
691 xutrailer(FILE *fp, int type, int row)
692 {
693     int rows, ch, res;
694
695     res = -1;
696     if (human) {
697         if (fscanf(fp, "config%n",  &res) != 0) {
698             return gripe("Malformed table footer");
699         }
700     }
701     ch = skipfs(fp);
702     if (!isdigit(ch)) {
703         if (ch != '\n' || !human)
704             return gripe("Malformed table footer");
705     } else {
706         ungetc(ch, fp);
707         if (fscanf(fp, "%d", &rows) != 1)
708             return gripe("Malformed table footer");
709         if (row != rows)
710             return gripe("Read %d rows, which doesn't match footer "
711                          "%d rows", row, rows);
712     }
713     if (skipfs(fp) != '\n')
714         return gripe("Junk after table footer");
715
716     while ((ch = skipfs(fp)) == '\n') ;
717     ungetc(ch, fp);
718
719     return 0;
720 }
721
722 int
723 xundump(FILE *fp, char *file, int expected_table)
724 {
725     int row, res, ch;
726     struct empfile *ep;
727     int need_sentinel;
728     struct value values[MAX_NUM_COLUMNS + 1];
729     int type;
730     int fixed_rows;
731
732     memset(values, 0, sizeof(values));
733
734     if (strcmp(fname, file) != 0) {
735         fname = file;
736         lineno = 1;
737     } else
738         lineno++;
739
740     if ((type = xuheader(fp, expected_table)) == -1)
741         return -1;
742     
743     ep = &empfile[type];
744     fixed_rows = has_const(ef_cadef(type));
745     need_sentinel = !fixed_rows; /* FIXME only approximation */
746
747     if (xucolumnheader(fp, type, values) == -1)
748         return -1;
749
750     row = 0;
751     for (;;) {
752         lineno++;
753         ch = skipfs(fp);
754         if (ch == '/')
755             break;
756         ungetc(ch, fp);
757         /*
758          * TODO
759          * Add column count check to the return value of xuflds()
760          */
761         res = xuflds(fp, values, xufld);
762         if (res > 0 && row >= ep->csize - 1) {
763             /* TODO grow cache unless EFF_STATIC */
764             gripe("Too many rows for table %s", ef_nameof(type));
765             res = -1;
766         }
767         if (res > 0) {
768             if (!fixed_rows)
769                 xuinitrow(type, row);
770             res = xuloadrow(type, row, values);
771             row++;
772         }
773         freeflds(values);
774         if (res < 0)
775             return -1;
776     }
777     if (fixed_rows && row != ep->csize -1)
778         return gripe("Table %s requires %d rows, got %d",
779                      ef_nameof(type), ep->csize - 1, row);
780
781     if (need_sentinel)
782         xuinitrow(type, row);
783
784     if (xutrailer(fp, type, row) == -1)
785         return -1;
786     
787     ep->fids = ep->cids = row;
788     return type;
789 }