]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
6ca49ee95b51f6f4d14884d93491e64e682af9dc
[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;            /* FIXME can this happen? */
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)
237         return 0;               /* FIXME can this happen? */
238     if (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         memcpy(to, from, ep->size);
365     }
366     if (id >= ep->fids) {
367         /* write beyond end of file extends it, take note */
368         ep->fids = id + 1;
369     }
370     return 1;
371 }
372
373 /*
374  * Extend the file-backed table TYPE by COUNT elements.
375  * Return non-zero on success, zero on failure.
376  */
377 int
378 ef_extend(int type, int count)
379 {
380     struct empfile *ep;
381     char *tmpobj;
382     int id, i, how;
383
384     if (ef_check(type) < 0)
385         return 0;
386     ep = &empfile[type];
387     if (CANT_HAPPEN(ep->fd < 0 || count < 0))
388         return 0;
389
390     tmpobj = calloc(1, ep->size);
391     id = ep->fids;
392     for (i = 0; i < count; i++) {
393         if (ep->init)
394             ep->init(id + i, tmpobj);
395         if (do_write(ep, tmpobj, id + i, 1) < 0)
396             break;
397     }
398     free(tmpobj);
399
400     if (ep->flags & EFF_MEM) {
401         /* FIXME lazy bastards...  do this right */
402         /* XXX this will cause problems if there are ef_ptrs (to the
403          * old allocated structure) active when we do the re-open */
404         how = ep->flags & ~EFF_IMMUTABLE;
405         ef_close(type);
406         ef_open(type, how);
407     } else {
408         ep->fids += i;
409     }
410
411     return i == count;
412 }
413
414 struct castr *
415 ef_cadef(int type)
416 {
417     return empfile[type].cadef;
418 }
419
420 int
421 ef_nelem(int type)
422 {
423     return empfile[type].fids;
424 }
425
426 int
427 ef_flags(int type)
428 {
429     return empfile[type].flags;
430 }
431
432 time_t
433 ef_mtime(int type)
434 {
435     if (empfile[type].fd <= 0)
436         return 0;
437     return fdate(empfile[type].fd);
438 }
439
440 /*
441  * Search for a table matching NAME, return its table type.
442  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
443  * several.
444  */
445 int
446 ef_byname(char *name)
447 {
448     return stmtch(name, empfile, offsetof(struct empfile, name),
449                   sizeof(empfile[0]));
450 }
451
452 /*
453  * Search CHOICES[] for a table type matching NAME, return it.
454  * Return M_NOTFOUND if there are no matches, M_NOTUNIQUE if there are
455  * several.
456  * CHOICES[] must be terminated with a negative value.
457  */
458 int
459 ef_byname_from(char *name, int choices[])
460 {
461     int res;
462     int *p;
463
464     res = M_NOTFOUND;
465     for (p = choices; *p >= 0; p++) {
466         if (ef_check(*p) < 0)
467             continue;
468         switch (mineq(name, empfile[*p].name)) {
469         case ME_MISMATCH:
470             break;
471         case ME_PARTIAL:
472             if (res >= 0)
473                 return M_NOTUNIQUE;
474             res = *p;
475             break;
476         case ME_EXACT:
477             return *p;
478         }
479     }
480     return res;
481 }
482
483 char *
484 ef_nameof(int type)
485 {
486     if (ef_check(type) < 0)
487         return "bad ef_type";
488     return empfile[type].name;
489 }
490
491 int
492 ef_check(int type)
493 {
494     if (CANT_HAPPEN((unsigned)type >= EF_MAX))
495         return -1;
496     return 0;
497 }
498
499 /*
500  * Ensure file-backed table contains ID.
501  * If necessary, extend it in steps of COUNT elements.
502  * Return non-zero on success, zero on failure.
503  */
504 int
505 ef_ensure_space(int type, int id, int count)
506 {
507     if (ef_check(type) < 0)
508         return 0;
509
510     while (id >= empfile[type].fids) {
511         if (!ef_extend(type, count))
512             return 0;
513     }
514     return 1;
515 }
516
517 static void
518 ef_fix_size(struct empfile *ep, int n)
519 {
520     ep->cids = ep->fids = n;
521     ep->csize = n + 1;
522 }
523
524 static void
525 ef_init_chr(int type, size_t size, ptrdiff_t name_offs)
526 {
527     struct empfile *ep = &empfile[type];
528     char *p;
529
530     for (p = ep->cache;
531          *((char **)(p + name_offs)) && **((char **)(p + name_offs));
532          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 }