]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
(ef_ptr, ef_read): Undo rev. 1.52 for these two, because there are
[empserver] / src / lib / common / file.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2005, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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
34  */
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #if !defined(_WIN32)
42 #include <unistd.h>
43 #endif
44 #include "common.h"
45 #include "file.h"
46 #include "gen.h"
47 #include "match.h"
48 #include "misc.h"
49 #include "nsc.h"
50 #include "optlist.h"
51
52 static int fillcache(struct empfile *, int);
53 static int do_write(struct empfile *, void *, int, int);
54
55 /*
56  * Open the file-backed table TYPE (EF_SECTOR, ...).
57  * HOW are flags to control operation.  Naturally, immutable flags are
58  * not permitted.
59  * Return non-zero on success, zero on failure.
60  * You must call ef_close() before the next ef_open().
61  */
62 int
63 ef_open(int type, int how)
64 {
65     struct empfile *ep;
66     int oflags, fd, fsiz, size;
67
68     if (ef_check(type) < 0)
69         return 0;
70     if (CANT_HAPPEN(how & EFF_IMMUTABLE))
71         how &= ~EFF_IMMUTABLE;
72
73     /* open file */
74     ep = &empfile[type];
75     if (CANT_HAPPEN(ep->fd >= 0))
76         return 0;
77     oflags = O_RDWR;
78     if (how & EFF_RDONLY)
79         oflags = O_RDONLY;
80     if (how & EFF_CREATE)
81         oflags |= O_CREAT | O_TRUNC;
82 #if defined(_WIN32)
83     oflags |= O_BINARY;
84 #endif
85     if ((fd = open(ep->file, oflags, 0660)) < 0) {
86         logerror("Can't open %s (%s)", ep->file, strerror(errno));
87         return 0;
88     }
89
90     /* get file size */
91     fsiz = fsize(fd);
92     if (fsiz % ep->size) {
93         logerror("Can't open %s (file size not a multiple of record size %d)",
94                  ep->file, ep->size);
95         close(fd);
96         return 0;
97     }
98     ep->fids = fsiz / ep->size;
99
100     /* allocate cache */
101     if (ep->flags & EFF_STATIC) {
102         /* ep->cache already points to space for e->csize elements */
103         if (how & EFF_MEM) {
104             if (ep->fids > ep->csize) {
105                 logerror("Can't open %s: file larger than %d bytes",
106                          ep->file, ep->fids * ep->size);
107                 close(fd);
108                 return 0;
109             }
110         }
111     } else {
112         if (how & EFF_MEM)
113             ep->csize = ep->fids;
114         else
115             ep->csize = max(1, blksize(fd) / ep->size);
116         size = ep->csize * ep->size;
117         if (CANT_HAPPEN(ep->cache))
118             free(ep->cache);
119         ep->cache = malloc(size);
120         if (ep->cache == NULL && size) {
121             logerror("Can't open %s: out of memory", ep->file);
122             close(fd);
123             return 0;
124         }
125     }
126     ep->baseid = 0;
127     ep->cids = 0;
128     ep->flags = (ep->flags & EFF_IMMUTABLE) | (how & ~EFF_CREATE);
129     ep->fd = fd;
130
131     /* map file into cache */
132     if ((how & EFF_MEM) && ep->fids) {
133         if (fillcache(ep, 0) != ep->fids) {
134             ep->cids = 0;       /* prevent cache flush */
135             ep->flags &= EFF_IMMUTABLE; /* maintain invariant */
136             ef_close(type);
137             return 0;
138         }
139     }
140
141     /*
142      * Could close fd if both EFF_RDONLY and EFF_MEM, but that doesn't
143      * happen, so don't bother.
144      */
145
146     return 1;
147 }
148
149 /*
150  * Close the file-backed table TYPE (EF_SECTOR, ...).
151  * Return non-zero on success, zero on failure.
152  */
153 int
154 ef_close(int type)
155 {
156     struct empfile *ep;
157     int retval;
158
159     retval = ef_flush(type);
160     ep = &empfile[type];
161     ep->flags &= EFF_IMMUTABLE;
162     if (!(ep->flags & EFF_STATIC)) {
163         free(ep->cache);
164         ep->cache = NULL;
165     }
166     if (close(ep->fd) < 0) {
167         logerror("Error closing %s (%s)", ep->name, strerror(errno));
168         retval = 0;
169     }
170     ep->fd = -1;
171     return retval;
172 }
173
174 /*
175  * Flush file-backed table TYPE (EF_SECTOR, ...) to disk.
176  * Return non-zero on success, zero on failure.
177  */
178 int
179 ef_flush(int type)
180 {
181     struct empfile *ep;
182
183     if (ef_check(type) < 0)
184         return 0;
185     ep = &empfile[type];
186     if (CANT_HAPPEN(ep->fd < 0))
187         return 0;
188     /*
189      * We don't know which cache entries are dirty.  ef_write() writes
190      * through, but direct updates through ef_ptr() don't.  They are
191      * allowed only with EFF_MEM.  Assume the whole cash is dirty
192      * then.
193      */
194     if (!(ep->flags & EFF_RDONLY) && (ep->flags & EFF_MEM))
195         return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0;
196
197     return 1;
198 }
199
200 /*
201  * Return pointer to element ID in table TYPE if it exists, else NULL.
202  * The table must be fully cached, i.e. flags & EFF_MEM.
203  * The caller is responsible for flushing changes he makes.
204  */
205 void *
206 ef_ptr(int type, int id)
207 {
208     struct empfile *ep;
209
210     if (ef_check(type) < 0)
211         return NULL;
212     ep = &empfile[type];
213     if (CANT_HAPPEN(!(ep->flags & EFF_MEM) || !ep->cache))
214         return NULL;
215     if (id < 0 || id >= ep->fids)
216         return NULL;
217     return ep->cache + ep->size * id;
218 }
219
220 /*
221  * Read element ID from table TYPE into buffer INTO.
222  * FIXME pass buffer size!
223  * Return non-zero on success, zero on failure.
224  */
225 int
226 ef_read(int type, int id, void *into)
227 {
228     struct empfile *ep;
229     void *from;
230
231     if (ef_check(type) < 0)
232         return 0;
233     ep = &empfile[type];
234     if (CANT_HAPPEN(!ep->cache))
235         return 0;
236     if (id < 0 || id >= ep->fids)
237         return 0;
238
239     if (ep->flags & EFF_MEM) {
240         from = ep->cache + id * ep->size;
241     } else {
242         if (ep->baseid + ep->cids <= id || ep->baseid > id) {
243             if (fillcache(ep, id) < 1)
244                 return 0;
245         }
246         from = ep->cache + (id - ep->baseid) * ep->size;
247     }
248     memcpy(into, from, ep->size);
249
250     if (ep->postread)
251         ep->postread(id, into);
252     return 1;
253 }
254
255 /*
256  * Fill cache of EP with elements starting at ID.
257  * If any were read, return their number.
258  * Else return -1 and leave the cache unchanged.
259  */
260 static int
261 fillcache(struct empfile *ep, int start)
262 {
263     int n, ret;
264     char *p;
265
266     if (CANT_HAPPEN(ep->fd < 0 || !ep->cache))
267         return -1;
268
269     if (lseek(ep->fd, start * ep->size, SEEK_SET) == (off_t)-1) {
270         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
271         return -1;
272     }
273
274     p = ep->cache;
275     n = ep->csize * ep->size;
276     while (n > 0) {
277         ret = read(ep->fd, p, n);
278         if (ret < 0) {
279             if (errno != EAGAIN) {
280                 logerror("Error reading %s (%s)", ep->file, strerror(errno));
281                 break;
282             }
283         } else if (ret == 0) {
284             break;
285         } else {
286             p += ret;
287             n -= ret;
288         }
289     }
290
291     if (p == ep->cache)
292         return -1;              /* nothing read, old cache still ok */
293
294     ep->baseid = start;
295     ep->cids = (p - ep->cache) / ep->size;
296     return ep->cids;
297 }
298
299 /*
300  * Write COUNT elements from BUF to EP, starting at ID.
301  * Return 0 on success, -1 on error.
302  */
303 static int
304 do_write(struct empfile *ep, void *buf, int id, int count)
305 {
306     int n, ret;
307     char *p;
308
309     if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
310         return -1;
311
312     if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
313         logerror("Error seeking %s (%s)", ep->file, strerror(errno));
314         return -1;
315     }
316
317     p = buf;
318     n = count * ep->size;
319     while (n > 0) {
320         ret = write(ep->fd, p, n);
321         if (ret < 0) {
322             if (errno != EAGAIN) {
323                 logerror("Error writing %s (%s)", ep->file, strerror(errno));
324                 /* FIXME if this extended file, truncate back to old size */
325                 return -1;
326             }
327         } else {
328             p += ret;
329             n -= ret;
330         }
331     }
332
333     return 0;
334 }
335
336 /*
337  * Write element ID into file-backed table TYPE from buffer FROM.
338  * FIXME pass buffer size!
339  * Write through cache straight to disk.
340  * Cannot write beyond the end of fully cached table (flags & EFF_MEM).
341  * Can write at the end of partially cached table.
342  * Return non-zero on success, zero on failure.
343  */
344 int
345 ef_write(int type, int id, void *from)
346 {
347     struct empfile *ep;
348     char *to;
349
350     if (ef_check(type) < 0)
351         return 0;
352     ep = &empfile[type];
353     if (ep->prewrite)
354         ep->prewrite(id, from);
355     if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
356         return 0;               /* not implemented */
357     if (do_write(ep, from, id, 1) < 0)
358         return 0;
359     if (id >= ep->baseid && id < ep->baseid + ep->cids) {
360         /* update the cache if necessary */
361         to = ep->cache + (id - ep->baseid) * ep->size;
362         memcpy(to, from, ep->size);
363     }
364     if (id >= ep->fids) {
365         /* write beyond end of file extends it, take note */
366         ep->fids = id + 1;
367     }
368     return 1;
369 }
370
371 /*
372  * Extend the file-backed table TYPE by COUNT elements.
373  * Return non-zero on success, zero on failure.
374  */
375 int
376 ef_extend(int type, int count)
377 {
378     struct empfile *ep;
379     char *tmpobj;
380     int id, i, how;
381
382     if (ef_check(type) < 0)
383         return 0;
384     ep = &empfile[type];
385     if (CANT_HAPPEN(ep->fd < 0 || count < 0))
386         return 0;
387
388     tmpobj = calloc(1, ep->size);
389     id = ep->fids;
390     for (i = 0; i < count; i++) {
391         if (ep->init)
392             ep->init(id + i, tmpobj);
393         if (do_write(ep, tmpobj, id + i, 1) < 0)
394             break;
395     }
396     free(tmpobj);
397
398     if (ep->flags & EFF_MEM) {
399         /* FIXME lazy bastards...  do this right */
400         /* XXX this will cause problems if there are ef_ptrs (to the
401          * old allocated structure) active when we do the re-open */
402         how = ep->flags & ~EFF_IMMUTABLE;
403         ef_close(type);
404         ef_open(type, how);
405     } else {
406         ep->fids += i;
407     }
408
409     return i == count;
410 }
411
412 struct castr *
413 ef_cadef(int type)
414 {
415     return empfile[type].cadef;
416 }
417
418 int
419 ef_nelem(int type)
420 {
421     return empfile[type].fids;
422 }
423
424 int
425 ef_flags(int type)
426 {
427     return empfile[type].flags;
428 }
429
430 time_t
431 ef_mtime(int type)
432 {
433     if (empfile[type].fd <= 0)
434         return 0;
435     return fdate(empfile[type].fd);
436 }
437
438 /*
439  * Search for a table matching NAME, return its table type.
440  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
441  * several.
442  */
443 int
444 ef_byname(char *name)
445 {
446     return stmtch(name, empfile, offsetof(struct empfile, name),
447                   sizeof(empfile[0]));
448 }
449
450 /*
451  * Search CHOICES[] for a table type matching NAME, return it.
452  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
453  * several.
454  * CHOICES[] must be terminated with a negative value.
455  */
456 int
457 ef_byname_from(char *name, int choices[])
458 {
459     int res;
460     int *p;
461
462     res = M_NOTFOUND;
463     for (p = choices; *p >= 0; p++) {
464         if (ef_check(*p) < 0)
465             continue;
466         switch (mineq(name, empfile[*p].name)) {
467         case ME_MISMATCH:
468             break;
469         case ME_PARTIAL:
470             if (res >= 0)
471                 return M_NOTUNIQUE;
472             res = *p;
473             break;
474         case ME_EXACT:
475             return *p;
476         }
477     }
478     return res;
479 }
480
481 char *
482 ef_nameof(int type)
483 {
484     if (ef_check(type) < 0)
485         return "bad ef_type";
486     return empfile[type].name;
487 }
488
489 int
490 ef_check(int type)
491 {
492     if (CANT_HAPPEN((unsigned)type >= EF_MAX))
493         return -1;
494     return 0;
495 }
496
497 /*
498  * Ensure file-backed table contains ID.
499  * If necessary, extend it in steps of COUNT elements.
500  * Return non-zero on success, zero on failure.
501  */
502 int
503 ef_ensure_space(int type, int id, int count)
504 {
505     if (ef_check(type) < 0)
506         return 0;
507     CANT_HAPPEN(id < 0);
508
509     while (id >= empfile[type].fids) {
510         if (!ef_extend(type, count))
511             return 0;
512     }
513     return 1;
514 }
515
516 static void
517 ef_fix_size(struct empfile *ep, int n)
518 {
519     ep->cids = ep->fids = n;
520     ep->csize = n + 1;
521 }
522
523 static void
524 ef_init_chr(int type, size_t size, ptrdiff_t name_offs)
525 {
526     struct empfile *ep = &empfile[type];
527     char *p;
528
529     for (p = ep->cache; *((char **)(p + name_offs)); p += size) ;
530     ep->cids = ep->fids = (p - ep->cache) / size;
531 }
532
533 /*
534  * Initialize Empire tables.
535  * Must be called once, before using anything else from this module.
536  */
537 void
538 ef_init(void)
539 {
540     struct castr *ca;
541     struct empfile *ep;
542     struct symbol *lup;
543     int i;
544
545     empfile[EF_MAP].size = empfile[EF_BMAP].size = (WORLD_X * WORLD_Y) / 2;
546
547     ef_init_chr(EF_SHIP_CHR,
548                 sizeof(struct mchrstr), offsetof(struct mchrstr, m_name));
549     ef_init_chr(EF_PLANE_CHR,
550                 sizeof(struct plchrstr), offsetof(struct plchrstr, pl_name));
551     ef_init_chr(EF_LAND_CHR,
552                 sizeof(struct lchrstr), offsetof(struct lchrstr, l_name));
553     ef_init_chr(EF_NUKE_CHR,
554                 sizeof(struct nchrstr), offsetof(struct nchrstr, n_name));
555
556     ca = (struct castr *)empfile[EF_META].cache;
557     for (i = 0; ca[i].ca_name; i++) ;
558     ef_fix_size(&empfile[EF_META], i);
559
560     for (ep = empfile; ep->uid >= 0; ep++) {
561         if (ep->cadef == symbol_ca) {
562             lup = (struct symbol *)ep->cache;
563             for (i = 0; lup[i].name; i++) ;
564             ef_fix_size(ep, i);
565         }
566     }
567 }