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