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