]> git.pond.sub.org Git - empserver/blob - src/lib/common/file.c
(ef_close): Reset member fd.
[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: Misc. operations on files
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1989
32  *     Steve McClure, 2000
33  */
34
35 #include <string.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <stdlib.h>
39 #if !defined(_WIN32)
40 #include <unistd.h>
41 #endif
42 #include "misc.h"
43 #include "nsc.h"
44 #include "file.h"
45 #include "common.h"
46 #include "gen.h"
47
48
49 static void fillcache(struct empfile *ep, int start);
50
51 /*
52  * Open the binary file for table TYPE (EF_SECTOR, ...).
53  * MODE is passed to open().
54  * HOW are EFF_OPEN flags to control operation.
55  * Return non-zero on success, zero on failure.
56  * You must call ef_close() before the next ef_open().
57  */
58 int
59 ef_open(int type, int mode, int how)
60 {
61     struct empfile *ep;
62     int size;
63
64 #if defined(_WIN32)
65     mode |= O_BINARY;
66 #endif
67     if (ef_check(type) < 0)
68         return 0;
69     if (CANT_HAPPEN(how & ~EFF_OPEN))
70         how &= EFF_OPEN;
71     ep = &empfile[type];
72     if (CANT_HAPPEN(ep->fd >= 0))
73         return 0;
74     if ((ep->fd = open(ep->file, mode, 0660)) < 0) {
75         logerror("%s: open failed", ep->file);
76         return 0;
77     }
78     ep->baseid = 0;
79     ep->cids = 0;
80     ep->mode = mode;
81     ep->flags |= how;
82     ep->fids = fsize(ep->fd) / ep->size;
83     if (ep->flags & EFF_MEM)
84         ep->csize = ep->fids;
85     else
86         ep->csize = max(1, blksize(ep->fd) / ep->size);
87     size = ep->csize * ep->size;
88     ep->cache = malloc(size);
89     if (ep->cache == NULL) {
90         logerror("ef_open: %s malloc(%d) failed\n", ep->file, size);
91         return 0;
92     }
93     if (ep->flags & EFF_MEM) {
94         if (read(ep->fd, ep->cache, size) != size) {
95             logerror("ef_open: read(%s) failed\n", ep->file);
96             return 0;
97         }
98         ep->cids = size / ep->size;
99     }
100     return 1;
101 }
102
103 /*
104  * Close the file containing objects of the type 'type', flushing the cache
105  * if applicable.
106  */
107 int
108 ef_close(int type)
109 {
110     struct empfile *ep;
111     int r;
112
113     if (ef_check(type) < 0)
114         return 0;
115     ep = &empfile[type];
116     if (ep->cache == NULL) {
117         /* no cache implies never opened */
118         return 0;
119     }
120     ef_flush(type);
121     ep->flags &= ~EFF_MEM;
122     free(ep->cache);
123     ep->cache = NULL;
124     if ((r = close(ep->fd)) < 0) {
125         logerror("ef_close: %s close(%d) -> %d", ep->name, ep->fd, r);
126     }
127     ep->fd = -1;
128     return 1;
129 }
130
131 /*
132  * Flush the cache of the file containing objects of type 'type' to disk.
133  */
134 int
135 ef_flush(int type)
136 {
137     struct empfile *ep;
138     int size;
139     int r;
140
141     if (ef_check(type) < 0)
142         return 0;
143     ep = &empfile[type];
144     if (ep->cache == NULL) {
145         /* no cache implies never opened */
146         return 0;
147     }
148     size = ep->csize * ep->size;
149     if (ep->mode > 0 && (ep->flags & EFF_MEM)) {
150         if ((r = lseek(ep->fd, 0L, SEEK_SET)) < 0) {
151             logerror("ef_flush: %s cache lseek(%d, 0L, SEEK_SET) -> %d",
152                      ep->name, ep->fd, r);
153             return 0;
154         }
155         if (write(ep->fd, ep->cache, size) != size) {
156             logerror("ef_flush: %s cache write(%d, %p, %d) -> %d",
157                      ep->name, ep->fd, ep->cache, ep->size, r);
158             return 0;
159         }
160     }
161     /*ef_zapcache(type); */
162     return 1;
163 }
164
165 /*
166  * Return a pointer the id 'id' of object of type 'type' in the cache.
167  */
168 char *
169 ef_ptr(int type, int id)
170 {
171     struct empfile *ep;
172
173     if (ef_check(type) < 0)
174         return NULL;
175     ep = &empfile[type];
176     if (id < 0 || id >= ep->fids)
177         return NULL;
178     if ((ep->flags & EFF_MEM) == 0) {
179         logerror("ef_ptr: (%s) only valid for EFF_MEM entries", ep->file);
180         return NULL;
181     }
182     return ep->cache + ep->size * id;
183 }
184
185 /*
186  * buffered read.  Tries to read a large number of items.
187  * This system won't work if item size is > sizeof buffer area.
188  */
189 int
190 ef_read(int type, int id, void *into)
191 {
192     struct empfile *ep;
193     void *from;
194
195     if (ef_check(type) < 0)
196         return 0;
197     ep = &empfile[type];
198     if (id < 0)
199         return 0;
200     if (ep->flags & EFF_MEM) {
201         if (id >= ep->fids)
202             return 0;
203         from = ep->cache + (id * ep->size);
204     } else {
205         if (id >= ep->fids) {
206             ep->fids = fsize(ep->fd) / ep->size;
207             if (id >= ep->fids)
208                 return 0;
209         }
210         if (ep->baseid + ep->cids <= id || ep->baseid > id)
211             fillcache(ep, id);
212         from = ep->cache + (id - ep->baseid) * ep->size;
213     }
214     memcpy(into, from, ep->size);
215
216     if (ep->postread)
217         ep->postread(id, into);
218     return 1;
219 }
220
221 static void
222 fillcache(struct empfile *ep, int start)
223 {
224     int n;
225
226     ep->baseid = start;
227     lseek(ep->fd, start * ep->size, SEEK_SET);
228     n = read(ep->fd, ep->cache, ep->csize * ep->size);
229     ep->cids = n / ep->size;
230 }
231
232 /*
233  * buffered write.  Modifies read cache (if applicable)
234  * and writes through to disk.
235  */
236 int
237 ef_write(int type, int id, void *from)
238 {
239     int r;
240     struct empfile *ep;
241     char *to;
242
243     if (ef_check(type) < 0)
244         return 0;
245     ep = &empfile[type];
246     if (id > 65536) {
247         /* largest unit id; this may bite us in large games */
248         logerror("ef_write: type %d id %d is too large!\n", type, id);
249         return 0;
250     }
251     if ((r = lseek(ep->fd, id * ep->size, SEEK_SET)) < 0) {
252         logerror("ef_write: %s #%d lseek(%d, %d, SEEK_SET) -> %d",
253                  ep->name, id, ep->fd, id * ep->size, r);
254         return 0;
255     }
256     if (ep->prewrite)
257         ep->prewrite(id, from);
258     if ((r = write(ep->fd, from, ep->size)) != ep->size) {
259         logerror("ef_write: %s #%d write(%d, %p, %d) -> %d",
260                  ep->name, id, ep->fd, from, ep->size, r);
261         return 0;
262     }
263     if (id >= ep->baseid && id < ep->baseid + ep->cids) {
264         /* update the cache if necessary */
265         to = ep->cache + (id - ep->baseid) * ep->size;
266         memcpy(to, from, ep->size);
267     }
268     CANT_HAPPEN(id > ep->fids);
269     if (id >= ep->fids) {
270         if (ep->flags & EFF_MEM) {
271             logerror("file %s went beyond %d items; won't be able toread item w/o restart",
272                      ep->name, ep->fids);
273         } else {
274             /* write expanded file; ep->fids = last id + 1 */
275             ep->fids = id + 1;
276         }
277     }
278     return 1;
279 }
280
281 /*
282  * Grow the file containing objects of the type 'type' by 'count' objects.
283  */
284 int
285 ef_extend(int type, int count)
286 {
287     struct empfile *ep;
288     char *tmpobj;
289     int cur, max;
290     int mode, how;
291     int r;
292
293     if (ef_check(type) < 0)
294         return 0;
295     ep = &empfile[type];
296     max = ep->fids + count;
297     cur = ep->fids;
298     tmpobj = calloc(1, ep->size);
299     if ((r = lseek(ep->fd, ep->fids * ep->size, SEEK_SET)) < 0) {
300         logerror("ef_extend: %s +#%d lseek(%d, %d, SEEK_SET) -> %d",
301                  ep->name, count, ep->fd, ep->fids * ep->size, r);
302         free(tmpobj);
303         return 0;
304     }
305     for (cur = ep->fids; cur < max; cur++) {
306         if (ep->init)
307             ep->init(cur, tmpobj);
308         if ((r = write(ep->fd, tmpobj, ep->size)) != ep->size) {
309             logerror("ef_extend: %s +#%d write(%d, %p, %d) -> %d",
310                      ep->name, count, ep->fd, tmpobj, ep->size, r);
311             free(tmpobj);
312             return 0;
313         }
314     }
315     free(tmpobj);
316     if (ep->flags & EFF_MEM) {
317         /* XXX this will cause problems if there are ef_ptrs (to the
318          * old allocated structure) active when we do the re-open */
319         mode = ep->mode;
320         how = ep->flags;
321         ef_close(type);
322         ef_open(type, mode, how);
323     } else {
324         ep->fids += count;
325     }
326     return 1;
327 }
328
329 /*
330  * Mark the cache for the file containing objects of type 'type' as unused.
331  */
332 void
333 ef_zapcache(int type)
334 {
335     struct empfile *ep = &empfile[type];
336     if ((ep->flags & EFF_MEM) == 0) {
337         ep->cids = 0;
338         ep->baseid = -1;
339     }
340 }
341
342 struct castr *
343 ef_cadef(int type)
344 {
345     return empfile[type].cadef;
346 }
347
348 int
349 ef_nelem(int type)
350 {
351     return empfile[type].fids;
352 }
353
354 int
355 ef_flags(int type)
356 {
357     return empfile[type].flags;
358 }
359
360 time_t
361 ef_mtime(int type)
362 {
363     if (empfile[type].fd <= 0)
364         return 0;
365     return fdate(empfile[type].fd);
366 }
367
368 /*
369  * Search empfile[0..EF_MAX-1] for element named NAME.
370  * Return its index in empfile[] if found, else -1.
371  */
372 int
373 ef_byname(char *name)
374 {
375     struct empfile *ef;
376     int i;
377     int len;
378
379     len = strlen(name);
380     for (i = 0; i < EF_MAX; i++) {
381         ef = &empfile[i];
382         if (strncmp(ef->name, name, min(len, strlen(ef->name))) == 0)
383             return i;
384     }
385     return -1;
386 }
387
388 char *
389 ef_nameof(int type)
390 {
391     if (type < 0 || type >= EF_MAX)
392         return "bad item type";
393     return empfile[type].name;
394 }
395
396 int
397 ef_check(int type)
398 {
399     if (type < 0 || type >= EF_MAX) {
400         logerror("ef_ptr: bad EF_type %d\n", type);
401         return -1;
402     }
403     return 0;
404 }
405
406 int
407 ef_ensure_space(int type, int id, int count)
408 {
409     while (id >= empfile[type].fids) {
410         if (!ef_extend(type, count))
411             return 0;
412     }
413     return 1;
414 }