]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
8b0962f5e5d96d04cdc9384e8bbcc651209445c8
[empserver] / src / lib / common / file.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, 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  *  file.c: Operations on Empire tables (`files' for historical reasons)
29  *
30  *  Known contributors to this file:
31  *     Dave Pare, 1989
32  *     Steve McClure, 2000
33  *     Markus Armbruster, 2005-2008
34  */
35
36 #include <config.h>
37
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include "file.h"
44 #include "match.h"
45 #include "misc.h"
46 #include "nsc.h"
47 #include "prototypes.h"
48
49 static int ef_realloc_cache(struct empfile *, int);
50 static int fillcache(struct empfile *, int);
51 static int do_read(struct empfile *, void *, int, int);
52 static int do_write(struct empfile *, void *, int, int);
53 static unsigned get_seqno(struct empfile *, int);
54 static void new_seqno(struct empfile *, void *);
55 static void do_blank(struct empfile *, void *, int, int);
56 static int ef_check(int);
57
58 /*
59  * Open the file-backed table TYPE (EF_SECTOR, ...).
60  * HOW are flags to control operation.  Naturally, immutable flags are
61  * not permitted.
62  * If NELT is non-negative, the table must have that many elements.
63  * Return non-zero on success, zero on failure.
64  * You must call ef_close() before the next ef_open().
65  */
66 int
67 ef_open(int type, int how, int nelt)
68 {
69     struct empfile *ep;
70     struct flock lock;
71     int oflags, fd, fsiz, nslots;
72
73     if (ef_check(type) < 0)
74         return 0;
75     if (CANT_HAPPEN(how & EFF_IMMUTABLE))
76         how &= ~EFF_IMMUTABLE;
77
78     /* open file */
79     ep = &empfile[type];
80     if (CANT_HAPPEN(ep->fd >= 0))
81         return 0;
82     oflags = O_RDWR;
83     if (how & EFF_PRIVATE)
84         oflags = O_RDONLY;
85     if (how & EFF_CREATE)
86         oflags |= O_CREAT | O_TRUNC;
87 #if defined(_WIN32)
88     oflags |= O_BINARY;
89 #endif
90     if ((fd = open(ep->file, oflags, S_IRWUG)) < 0) {
91         logerror("Can't open %s (%s)", ep->file, strerror(errno));
92         return 0;
93     }
94
95     lock.l_type = how & EFF_PRIVATE ? F_RDLCK : F_WRLCK;
96     lock.l_whence = SEEK_SET;
97     lock.l_start = lock.l_len = 0;
98     if (fcntl(fd, F_SETLK, &lock) == -1) {
99         logerror("Can't lock %s (%s)", ep->file, strerror(errno));
100         close(fd);
101         return 0;
102     }
103
104     /* get file size */
105     fsiz = fsize(fd);
106     if (fsiz % ep->size) {
107         logerror("Can't open %s (file size not a multiple of record size %d)",
108                  ep->file, ep->size);
109         close(fd);
110         return 0;
111     }
112     ep->fids = fsiz / ep->size;
113     if (nelt >= 0 && nelt != ep->fids) {
114         logerror("Can't open %s (got %d records instead of %d)",
115                  ep->file, ep->fids, nelt);
116         close(fd);
117         return 0;
118     }
119
120     /* allocate cache */
121     if (ep->flags & EFF_STATIC) {
122         /* ep->cache already points to space for ep->csize elements */
123         if (how & EFF_MEM) {
124             if (ep->fids > ep->csize) {
125                 logerror("Can't open %s: file larger than %d bytes",
126                          ep->file, ep->fids * ep->size);
127                 close(fd);
128                 return 0;
129             }
130         }
131     } else {
132         if (CANT_HAPPEN(ep->cache))
133             free(ep->cache);
134         if (how & EFF_MEM)
135             nslots = ep->fids;
136         else
137             nslots = blksize(fd) / ep->size;
138         if (!ef_realloc_cache(ep, nslots)) {
139             logerror("Can't map %s (%s)", ep->file, strerror(errno));
140             close(fd);
141             return 0;
142         }
143     }
144     ep->baseid = 0;
145     ep->cids = 0;
146     ep->flags = (ep->flags & EFF_IMMUTABLE) | (how & ~EFF_CREATE);
147     ep->fd = fd;
148
149     /* map file into cache */
150     if ((how & EFF_MEM) && ep->fids) {
151         if (fillcache(ep, 0) != ep->fids) {
152             ep->cids = 0;       /* prevent cache flush */
153             ep->flags &= EFF_IMMUTABLE; /* maintain invariant */
154             ef_close(type);
155             return 0;
156         }
157     }
158
159     if (ep->onresize && ep->onresize(type) < 0)
160         return 0;
161     return 1;
162 }
163
164 /*
165  * Reallocate cache for table EP to hold COUNT slots.
166  * The table must not be allocated statically.
167  * The cache may still be unmapped.
168  * If reallocation succeeds, any pointers obtained from ef_ptr()
169  * become invalid.
170  * If it fails, the cache is unchanged, and errno is set.
171  * Return non-zero on success, zero on failure.
172  */
173 static int
174 ef_realloc_cache(struct empfile *ep, int count)
175 {
176     void *cache;
177
178     if (CANT_HAPPEN(ep->flags & EFF_STATIC))
179         return 0;
180     if (CANT_HAPPEN(count < 0))
181         count = 0;
182
183     /*
184      * Avoid zero slots, because that can lead to null cache, which
185      * would be interpreted as unmapped cache.
186      */
187     if (count == 0)
188         count++;
189     cache = realloc(ep->cache, count * ep->size);
190     if (!cache)
191         return 0;
192
193     ep->cache = cache;
194     ep->csize = count;
195     return 1;
196 }
197
198 /*
199  * Open the table TYPE as view of table BASE.
200  * Return non-zero on success, zero on failure.
201  * Beware: views work only as long as BASE doesn't change size!
202  * You must call ef_close(TYPE) before closing BASE.
203  */
204 int
205 ef_open_view(int type, int base)
206 {
207     struct empfile *ep;
208
209     if (CANT_HAPPEN(!EF_IS_VIEW(type)))
210         return -1;
211     ep = &empfile[type];
212     if (CANT_HAPPEN(!(ef_flags(base) & EFF_MEM)))
213         return -1;
214
215     ep->cache = empfile[base].cache;
216     ep->csize = empfile[base].csize;
217     ep->flags |= EFF_MEM;
218     ep->baseid = empfile[base].baseid;
219     ep->cids = empfile[base].cids;
220     ep->fids = empfile[base].fids;
221     return 0;
222 }
223
224 /*
225  * Close the file-backed table TYPE (EF_SECTOR, ...).
226  * Return non-zero on success, zero on failure.
227  */
228 int
229 ef_close(int type)
230 {
231     struct empfile *ep;
232     int retval = 1;
233
234     if (ef_check(type) < 0)
235         return 0;
236     ep = &empfile[type];
237
238     if (EF_IS_VIEW(type))
239         ep->cache = NULL;
240     else {
241         if (!ef_flush(type))
242             retval = 0;
243         ep->flags &= EFF_IMMUTABLE;
244         if (!(ep->flags & EFF_STATIC)) {
245             free(ep->cache);
246             ep->cache = NULL;
247         }
248         if (close(ep->fd) < 0) {
249             logerror("Error closing %s (%s)", ep->file, strerror(errno));
250             retval = 0;
251         }
252         ep->fd = -1;
253     }
254     ep->baseid = ep->cids = ep->fids = 0;
255     if (ep->onresize && ep->onresize(type) < 0)
256         retval = 0;
257     return retval;
258 }
259
260 /*
261  * Flush file-backed table TYPE (EF_SECTOR, ...) to its backing file.
262  * Do nothing if the table is privately mapped.
263  * Update timestamps of written elements if table is EFF_TYPED.
264  * Return non-zero on success, zero on failure.
265  */
266 int
267 ef_flush(int type)
268 {
269     struct empfile *ep;
270
271     if (ef_check(type) < 0)
272         return 0;
273     ep = &empfile[type];
274     if (ep->flags & EFF_PRIVATE)
275         return 1;               /* nothing to do */
276     if (CANT_HAPPEN(ep->fd < 0))
277         return 0;
278     /*
279      * We don't know which cache entries are dirty.  ef_write() writes
280      * through, but direct updates through ef_ptr() don't.  They are
281      * allowed only with EFF_MEM.  Assume the whole cash is dirty
282      * then.
283      */
284     if (ep->flags & EFF_MEM) {
285         if (do_write(ep, ep->cache, ep->baseid, ep->cids) < 0)
286             return 0;
287     }
288
289     return 1;
290 }
291
292 /*
293  * Return pointer to element ID in table TYPE if it exists, else NULL.
294  * The table must be fully cached, i.e. flags & EFF_MEM.
295  * The caller is responsible for flushing changes he makes.
296  */
297 void *
298 ef_ptr(int type, int id)
299 {
300     struct empfile *ep;
301
302     if (ef_check(type) < 0)
303         return NULL;
304     ep = &empfile[type];
305     if (CANT_HAPPEN(!(ep->flags & EFF_MEM) || !ep->cache))
306         return NULL;
307     if (id < 0 || id >= ep->fids)
308         return NULL;
309     return ep->cache + ep->size * id;
310 }
311
312 /*
313  * Read element ID from table TYPE into buffer INTO.
314  * FIXME pass buffer size!
315  * Return non-zero on success, zero on failure.
316  */
317 int
318 ef_read(int type, int id, void *into)
319 {
320     struct empfile *ep;
321     void *cachep;
322
323     if (ef_check(type) < 0)
324         return 0;
325     ep = &empfile[type];
326     if (CANT_HAPPEN(!ep->cache))
327         return 0;
328     if (id < 0 || id >= ep->fids)
329         return 0;
330
331     if (ep->flags & EFF_MEM) {
332         cachep = ep->cache + id * ep->size;
333     } else {
334         if (ep->baseid + ep->cids <= id || ep->baseid > id) {
335             if (fillcache(ep, id) < 1)
336                 return 0;
337         }
338         cachep = ep->cache + (id - ep->baseid) * ep->size;
339     }
340     memcpy(into, cachep, ep->size);
341
342     if (ep->postread)
343         ep->postread(id, into);
344     return 1;
345 }
346
347 /*
348  * Fill cache of file-backed EP with elements starting at ID.
349  * If any were read, return their number.
350  * Else return -1 and leave the cache unchanged.
351  */
352 static int
353 fillcache(struct empfile *ep, int id)
354 {
355     int ret;
356
357     if (CANT_HAPPEN(!ep->cache))
358         return -1;
359
360     ret = do_read(ep, ep->cache, id, MIN(ep->csize, ep->fids - id));
361     if (ret >= 0) {
362         /* cache changed */
363         ep->baseid = id;
364         ep->cids = ret;
365     }
366     return ret;
367 }
368
369 static int
370 do_read(struct empfile *ep, void *buf, int id, int count)
371 {
372     int n, ret;
373     char *p;
374
375     if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
376         return -1;
377
378     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
379         logerror("Error seeking %s to elt %d (%s)",
380                  ep->file, id, strerror(errno));
381         return -1;
382     }
383
384     p = buf;
385     n = count * ep->size;
386     while (n > 0) {
387         ret = read(ep->fd, p, n);
388         if (ret < 0) {
389             if (errno != EINTR) {
390                 logerror("Error reading %s elt %d (%s)",
391                          ep->file,
392                          id + (int)((p - (char *)buf) / ep->size),
393                          strerror(errno));
394                 break;
395             }
396         } else if (ret == 0) {
397             logerror("Unexpected EOF reading %s elt %d",
398                      ep->file, id + (int)((p - (char *)buf) / ep->size));
399             break;
400         } else {
401             p += ret;
402             n -= ret;
403         }
404     }
405
406     return (p - (char *)buf) / ep->size;
407 }
408
409 /*
410  * Write COUNT elements starting at ID from BUF to file-backed EP.
411  * Update the timestamp if the table is EFF_TYPED.
412  * Don't actually write if table is privately mapped.
413  * Return 0 on success, -1 on error (file may be corrupt then).
414  */
415 static int
416 do_write(struct empfile *ep, void *buf, int id, int count)
417 {
418     int i, n, ret;
419     char *p;
420     struct emptypedstr *elt;
421     time_t now;
422
423     if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
424         return -1;
425
426     if (ep->flags & EFF_TYPED) {
427         now = ep->flags & EFF_NOTIME ? (time_t)-1 : time(NULL);
428         for (i = 0; i < count; i++) {
429             /*
430              * TODO Oopses here could be due to bad data corruption.
431              * Fail instead of attempting to recover?
432              */
433             elt = (struct emptypedstr *)((char *)buf + i * ep->size);
434             if (CANT_HAPPEN(elt->ef_type != ep->uid))
435                 elt->ef_type = ep->uid;
436             if (CANT_HAPPEN(elt->uid != id + i))
437                 elt->uid = id + i;
438             if (now != (time_t)-1)
439                 elt->timestamp = now;
440         }
441     }
442
443     if (ep->flags & EFF_PRIVATE)
444         return 0;
445
446     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
447         logerror("Error seeking %s to elt %d (%s)",
448                  ep->file, id, strerror(errno));
449         return -1;
450     }
451
452     p = buf;
453     n = count * ep->size;
454     while (n > 0) {
455         ret = write(ep->fd, p, n);
456         if (ret < 0) {
457             if (errno != EINTR) {
458                 logerror("Error writing %s elt %d (%s)",
459                          ep->file,
460                          id + (int)((p - (char *)buf) / ep->size),
461                          strerror(errno));
462                 return -1;
463             }
464         } else {
465             p += ret;
466             n -= ret;
467         }
468     }
469
470     return 0;
471 }
472
473 /*
474  * Write element ID into table TYPE from buffer FROM.
475  * FIXME pass buffer size!
476  * Update timestamp in FROM if table is EFF_TYPED.
477  * If table is file-backed and not privately mapped, write through
478  * cache straight to disk.
479  * Cannot write beyond the end of fully cached table (flags & EFF_MEM).
480  * Can write at the end of partially cached table.
481  * Return non-zero on success, zero on failure.
482  */
483 int
484 ef_write(int type, int id, void *from)
485 {
486     struct empfile *ep;
487     char *cachep;
488
489     if (ef_check(type) < 0)
490         return 0;
491     ep = &empfile[type];
492     if (CANT_HAPPEN((ep->flags & (EFF_MEM | EFF_PRIVATE)) == EFF_PRIVATE))
493         return 0;
494     if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
495         return 0;               /* not implemented */
496     new_seqno(ep, from);
497     if (id >= ep->fids) {
498         /* write beyond end of file extends it, take note */
499         ep->fids = id + 1;
500         if (ep->onresize && ep->onresize(type) < 0)
501             return 0;
502     }
503     if (id >= ep->baseid && id < ep->baseid + ep->cids)
504         cachep = ep->cache + (id - ep->baseid) * ep->size;
505     else
506         cachep = NULL;
507     if (ep->prewrite)
508         ep->prewrite(id, cachep, from);
509     if (ep->fd >= 0) {
510         if (do_write(ep, from, id, 1) < 0)
511             return 0;
512     }
513     if (cachep && cachep != from)       /* update the cache if necessary */
514         memcpy(cachep, from, ep->size);
515     return 1;
516 }
517
518 /*
519  * Change element id.
520  * BUF is an element of table TYPE.
521  * ID is its new element ID.
522  * If table is EFF_TYPED, change id and sequence number stored in BUF.
523  * Else do nothing.
524  */
525 void
526 ef_set_uid(int type, void *buf, int uid)
527 {
528     struct emptypedstr *elt;
529     struct empfile *ep;
530
531     if (ef_check(type) < 0)
532         return;
533     ep = &empfile[type];
534     if (!(ep->flags & EFF_TYPED))
535         return;
536     elt = buf;
537     if (elt->uid == uid)
538         return;
539     elt->uid = uid;
540     elt->seqno = get_seqno(ep, uid);
541 }
542
543 /*
544  * Return sequence number of element ID in table EP.
545  * Return zero if table is not EFF_TYPED (it has no sequence number
546  * then).
547  */
548 static unsigned
549 get_seqno(struct empfile *ep, int id)
550 {
551     struct emptypedstr *elt;
552
553     if (!(ep->flags & EFF_TYPED))
554         return 0;
555     if (id < 0 || id >= ep->fids)
556         return 0;
557     if (id >= ep->baseid && id < ep->baseid + ep->cids)
558         elt = (void *)(ep->cache + (id - ep->baseid) * ep->size);
559     else {
560         /* need a buffer, steal last cache slot */
561         if (ep->cids == ep->csize)
562             ep->cids--;
563         elt = (void *)(ep->cache + ep->cids * ep->size);
564         if (do_read(ep, elt, id, 1) < 0)
565             return 0;           /* deep trouble */
566     }
567     return elt->seqno;
568 }
569
570 /*
571  * Increment sequence number in BUF, which is about to be written to EP.
572  * Do nothing if table is not EFF_TYPED (it has no sequence number
573  * then).
574  */
575 static void
576 new_seqno(struct empfile *ep, void *buf)
577 {
578     struct emptypedstr *elt = buf;
579     unsigned old_seqno;
580
581     if (!(ep->flags & EFF_TYPED))
582         return;
583     old_seqno = get_seqno(ep, elt->uid);
584     if (CANT_HAPPEN(old_seqno != elt->seqno))
585         old_seqno = MAX(old_seqno, elt->seqno);
586     elt->seqno = old_seqno + 1;
587 }
588
589 /*
590  * Extend table TYPE by COUNT elements.
591  * Any pointers obtained from ef_ptr() become invalid.
592  * Return non-zero on success, zero on failure.
593  */
594 int
595 ef_extend(int type, int count)
596 {
597     struct empfile *ep;
598     char *p;
599     int need_sentinel, i, id;
600
601     if (ef_check(type) < 0 || CANT_HAPPEN(EF_IS_VIEW(type)))
602         return 0;
603     ep = &empfile[type];
604     if (CANT_HAPPEN(count < 0))
605         return 0;
606
607     id = ep->fids;
608     if (ep->flags & EFF_MEM) {
609         need_sentinel = (ep->flags & EFF_SENTINEL) != 0;
610         if (id + count + need_sentinel > ep->csize) {
611             if (ep->flags & EFF_STATIC) {
612                 logerror("Can't extend %s beyond %d elements",
613                          ep->name, ep->csize - need_sentinel);
614                 return 0;
615             }
616             if (!ef_realloc_cache(ep, id + count + need_sentinel)) {
617                 logerror("Can't extend %s to %d elements (%s)",
618                          ep->name, id + count, strerror(errno));
619                 return 0;
620             }
621         }
622         p = ep->cache + id * ep->size;
623         do_blank(ep, p, id, count);
624         if (ep->fd >= 0) {
625             if (do_write(ep, p, id, count) < 0)
626                 return 0;
627         }
628         if (need_sentinel)
629             memset(ep->cache + (id + count) * ep->size, 0, ep->size);
630         ep->cids = id + count;
631     } else {
632         /* need a buffer, steal last cache slot */
633         if (ep->cids == ep->csize)
634             ep->cids--;
635         p = ep->cache + ep->cids * ep->size;
636         for (i = 0; i < count; i++) {
637             do_blank(ep, p, id + i, 1);
638             if (do_write(ep, p, id + i, 1) < 0)
639                 return 0;
640         }
641     }
642     ep->fids = id + count;
643     if (ep->onresize && ep->onresize(type) < 0)
644         return 0;
645     return 1;
646 }
647
648 /*
649  * Initialize element ID for EP in BUF.
650  * FIXME pass buffer size!
651  */
652 void
653 ef_blank(int type, int id, void *buf)
654 {
655     struct empfile *ep;
656     struct emptypedstr *elt;
657
658     if (ef_check(type) < 0)
659         return;
660     ep = &empfile[type];
661     do_blank(ep, buf, id, 1);
662     if (ep->flags & EFF_TYPED) {
663         elt = buf;
664         elt->seqno = get_seqno(ep, elt->uid);
665     }
666 }
667
668 /*
669  * Initialize COUNT elements of EP in BUF, starting with element ID.
670  */
671 static void
672 do_blank(struct empfile *ep, void *buf, int id, int count)
673 {
674     int i;
675     struct emptypedstr *elt;
676
677     memset(buf, 0, count * ep->size);
678     for (i = 0; i < count; i++) {
679         elt = (struct emptypedstr *)((char *)buf + i * ep->size);
680         if (ep->flags & EFF_TYPED) {
681             elt->ef_type = ep->uid;
682             elt->uid = id + i;
683         }
684         if (ep->oninit)
685             ep->oninit(elt);
686     }
687 }
688
689 /*
690  * Truncate table TYPE to COUNT elements.
691  * Any pointers obtained from ef_ptr() become invalid.
692  * Return non-zero on success, zero on failure.
693  */
694 int
695 ef_truncate(int type, int count)
696 {
697     struct empfile *ep;
698     int need_sentinel;
699
700     if (ef_check(type) < 0 || CANT_HAPPEN(EF_IS_VIEW(type)))
701         return 0;
702     ep = &empfile[type];
703     if (CANT_HAPPEN(count < 0 || count > ep->fids))
704         return 0;
705
706     if (ep->fd >= 0 && !(ep->flags & EFF_PRIVATE)) {
707         if (ftruncate(ep->fd, count * ep->size) < 0) {
708             logerror("Can't truncate %s to %d elements (%s)",
709                      ep->file, count, strerror(errno));
710             return 0;
711         }
712     }
713     ep->fids = count;
714
715     if (ep->flags & EFF_MEM) {
716         need_sentinel = (ep->flags & EFF_SENTINEL) != 0;
717         if (!(ep->flags & EFF_STATIC)) {
718             if (!ef_realloc_cache(ep, count + need_sentinel)) {
719                 logerror("Can't shrink %s cache after truncate (%s)",
720                          ep->name, strerror(errno));
721                 /* continue with unshrunk cache */
722             }
723         }
724         if (need_sentinel)
725             memset(ep->cache + count * ep->size, 0, ep->size);
726         ep->cids = count;
727     } else {
728         if (ep->baseid >= count)
729             ep->cids = 0;
730         else if (ep->cids > count - ep->baseid)
731             ep->cids = count - ep->baseid;
732     }
733
734     if (ep->onresize && ep->onresize(type) < 0)
735         return 0;
736     return 1;
737 }
738
739 struct castr *
740 ef_cadef(int type)
741 {
742     if (ef_check(type) < 0)
743         return NULL;
744     return empfile[type].cadef;
745 }
746
747 int
748 ef_nelem(int type)
749 {
750     if (ef_check(type) < 0)
751         return 0;
752     return empfile[type].fids;
753 }
754
755 int
756 ef_flags(int type)
757 {
758     if (ef_check(type) < 0)
759         return 0;
760     return empfile[type].flags;
761 }
762
763 time_t
764 ef_mtime(int type)
765 {
766     if (ef_check(type) < 0)
767         return 0;
768     if (empfile[type].fd <= 0)
769         return 0;
770     return fdate(empfile[type].fd);
771 }
772
773 /*
774  * Search for a table matching NAME, return its table type.
775  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
776  * several.
777  */
778 int
779 ef_byname(char *name)
780 {
781     return stmtch(name, empfile, offsetof(struct empfile, name),
782                   sizeof(empfile[0]));
783 }
784
785 /*
786  * Search CHOICES[] for a table type matching NAME, return it.
787  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
788  * several.
789  * CHOICES[] must be terminated with a negative value.
790  */
791 int
792 ef_byname_from(char *name, int choices[])
793 {
794     int res;
795     int *p;
796
797     res = M_NOTFOUND;
798     for (p = choices; *p >= 0; p++) {
799         if (ef_check(*p) < 0)
800             continue;
801         switch (mineq(name, empfile[*p].name)) {
802         case ME_MISMATCH:
803             break;
804         case ME_PARTIAL:
805             if (res >= 0)
806                 return M_NOTUNIQUE;
807             res = *p;
808             break;
809         case ME_EXACT:
810             return *p;
811         }
812     }
813     return res;
814 }
815
816 char *
817 ef_nameof(int type)
818 {
819     if (ef_check(type) < 0)
820         return "bad ef_type";
821     return empfile[type].name;
822 }
823
824 static int
825 ef_check(int type)
826 {
827     if (CANT_HAPPEN((unsigned)type >= EF_MAX))
828         return -1;
829     return 0;
830 }
831
832 /*
833  * Ensure table contains element ID.
834  * If necessary, extend it in steps of COUNT elements.
835  * Return non-zero on success, zero on failure.
836  */
837 int
838 ef_ensure_space(int type, int id, int count)
839 {
840     if (ef_check(type) < 0)
841         return 0;
842     CANT_HAPPEN(id < 0);
843
844     while (id >= empfile[type].fids) {
845         if (!ef_extend(type, count))
846             return 0;
847     }
848     return 1;
849 }