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