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