Implement a Quake-like virtual file system layer
[neverball] / share / common.c
1 /*
2  *  Copyright (C) 2007  Neverball contributors
3  *
4  *  This  program is  free software;  you can  redistribute  it and/or
5  *  modify it  under the  terms of the  GNU General Public  License as
6  *  published by the Free Software Foundation; either version 2 of the
7  *  License, or (at your option) any later version.
8  *
9  *  This program  is distributed in the  hope that it  will be useful,
10  *  but  WITHOUT ANY WARRANTY;  without even  the implied  warranty of
11  *  MERCHANTABILITY or FITNESS FOR  A PARTICULAR PURPOSE.  See the GNU
12  *  General Public License for more details.
13  *
14  *  You should have received a  copy of the GNU General Public License
15  *  along  with this  program;  if  not, write  to  the Free  Software
16  *  Foundation,  Inc.,   59  Temple  Place,  Suite   330,  Boston,  MA
17  *  02111-1307 USA
18  */
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <time.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <assert.h>
28
29 #include "common.h"
30 #include "fs.h"
31
32 #define MAXSTR 256
33
34 /*---------------------------------------------------------------------------*/
35
36 int read_line(char **dst, fs_file fin)
37 {
38     char buffer[MAXSTR] = "";
39     int  buffer_size    = 0;
40
41     char *store      = NULL;
42     char *store_new  = NULL;
43     int   store_size = 0;
44
45     int seen_newline = 0;
46
47     while (!seen_newline)
48     {
49         if (fs_gets(buffer, sizeof (buffer), fin) == NULL)
50         {
51             if (store_size > 0)
52                 break;
53             else
54             {
55                 *dst = NULL;
56                 return 0;
57             }
58         }
59
60         buffer_size = strlen(buffer) + 1;
61
62         /* Erase trailing newline. */
63
64         if (buffer[buffer_size - 2] == '\n')
65         {
66             seen_newline = 1;
67             buffer[buffer_size - 2] = '\0';
68             buffer_size--;
69         }
70
71         /* Allocate or reallocate space for the buffer. */
72
73         if ((store_new = (char *) realloc(store, store_size + buffer_size)))
74         {
75             /* Avoid passing garbage to string functions. */
76
77             if (store == NULL)
78                 store_new[0] = '\0';
79
80             store       = store_new;
81             store_size += buffer_size;
82
83             store_new = NULL;
84         }
85         else
86         {
87             fprintf(stderr, "Failed to allocate memory.\n");
88
89             free(store);
90             *dst = NULL;
91             return 0;
92         }
93
94         strncat(store, buffer, buffer_size);
95     }
96
97     *dst = store;
98
99     return 1;
100 }
101
102 char *strip_newline(char *str)
103 {
104     char *c = str + strlen(str) - 1;
105
106     while (c >= str && (*c == '\n' || *c =='\r'))
107         *c-- = '\0';
108
109     return str;
110 }
111
112 char *dupe_string(const char *src)
113 {
114     char *dst = NULL;
115
116     if (src && (dst = malloc(strlen(src) + 1)))
117         strcpy(dst, src);
118
119     return dst;
120 }
121
122 char *concat_string(const char *first, ...)
123 {
124     char *full;
125
126     if ((full = strdup(first)))
127     {
128         const char *part;
129         va_list ap;
130
131         va_start(ap, first);
132
133         while ((part = va_arg(ap, const char *)))
134         {
135             char *new;
136
137             if ((new = realloc(full, strlen(full) + strlen(part) + 1)))
138             {
139                 full = new;
140                 strcat(full, part);
141             }
142             else
143             {
144                 free(full);
145                 full = NULL;
146                 break;
147             }
148         }
149
150         va_end(ap);
151     }
152
153     return full;
154 }
155
156 char *trunc_string(const char *src, char *dst, int len)
157 {
158     static const char ell[] = "...";
159
160     assert(len > sizeof (ell));
161
162     if (dst[len - 1] = '\0', strncpy(dst, src, len), dst[len - 1] != '\0')
163         strcpy(dst + len - sizeof (ell), ell);
164
165     return dst;
166 }
167
168 time_t make_time_from_utc(struct tm *tm)
169 {
170     struct tm local, *utc;
171     time_t t;
172
173     t = mktime(tm);
174
175     local = *localtime(&t);
176     utc   =  gmtime(&t);
177
178     local.tm_year += local.tm_year - utc->tm_year;
179     local.tm_mon  += local.tm_mon  - utc->tm_mon ;
180     local.tm_mday += local.tm_mday - utc->tm_mday;
181     local.tm_hour += local.tm_hour - utc->tm_hour;
182     local.tm_min  += local.tm_min  - utc->tm_min ;
183     local.tm_sec  += local.tm_sec  - utc->tm_sec ;
184
185     return mktime(&local);
186 }
187
188 const char *date_to_str(time_t i)
189 {
190     static char str[sizeof ("YYYY-mm-dd HH:MM:SS")];
191     strftime(str, sizeof (str), "%Y-%m-%d %H:%M:%S", localtime(&i));
192     return str;
193 }
194
195 int file_exists(const char *name)
196 {
197     FILE *fp;
198
199     if ((fp = fopen(name, "r")))
200     {
201         fclose(fp);
202         return 1;
203     }
204     return 0;
205 }
206
207 int file_rename(const char *src, const char *dst)
208 {
209 #ifdef _WIN32
210     if (file_exists(dst))
211         remove(dst);
212 #endif
213     return rename(src, dst);
214 }
215
216 void file_copy(FILE *fin, FILE *fout)
217 {
218     char   buff[MAXSTR];
219     size_t size;
220
221     while ((size = fread(buff, 1, sizeof (buff), fin)) > 0)
222         fwrite(buff, 1, size, fout);
223 }
224
225 /*---------------------------------------------------------------------------*/
226
227 int path_is_sep(int c)
228 {
229 #ifdef _WIN32
230     return c == '/' || c == '\\';
231 #else
232     return c == '/';
233 #endif
234 }
235
236 int path_is_abs(const char *path)
237 {
238     if (path_is_sep(path[0]))
239         return 1;
240
241 #ifdef _WIN32
242     if (isalpha(path[0]) && path[1] == ':' && path_is_sep(path[2]))
243         return 1;
244 #endif
245
246     return 0;
247 }
248
249 static char *path_last_sep(const char *path)
250 {
251     char *sep;
252
253     sep = strrchr(path, '/');
254
255 #ifdef _WIN32
256     if (!sep)
257     {
258         sep = strrchr(path, '\\');
259     }
260     else
261     {
262         char *tmp;
263
264         if ((tmp = strrchr(sep, '\\')))
265             sep = tmp;
266     }
267 #endif
268
269     return sep;
270 }
271
272 char *base_name(const char *name, const char *suffix)
273 {
274     static char buf[MAXSTR];
275     char *base;
276
277     if (!name)
278         return NULL;
279
280     /* Remove the directory part. */
281
282     base = path_last_sep(name);
283
284     strncpy(buf, base ? base + 1 : name, sizeof (buf));
285
286     /* Remove the suffix. */
287
288     if (suffix)
289     {
290         int l = strlen(buf) - strlen(suffix);
291
292         if (l >= 0 && strcmp(buf + l, suffix) == 0)
293             buf[l] = '\0';
294     }
295
296     return buf;
297 }
298
299 const char *dir_name(const char *name)
300 {
301     static char buff[MAXSTR];
302
303     char *sep;
304
305     strncpy(buff, name, sizeof (buff) - 1);
306
307     if ((sep = path_last_sep(buff)))
308     {
309         if (sep == buff)
310             return "/";
311
312         *sep = '\0';
313
314         return buff;
315     }
316
317     return ".";
318 }
319
320 /*
321  * Given a path to a file REF and another path REL relative to REF,
322  * construct and return a new path that can be used to refer to REL
323  * directly.
324  */
325 char *path_resolve(const char *ref, const char *rel)
326 {
327     static char new[MAXSTR * 2];
328
329     if (path_is_abs(rel))
330     {
331         strncpy(new, rel, sizeof (new) - 1);
332         return new;
333     }
334
335     strncpy(new, dir_name(ref), sizeof (new) - 1);
336     strncat(new, "/",           sizeof (new) - strlen(new) - 1);
337     strncat(new, rel,           sizeof (new) - strlen(new) - 1);
338
339     return new;
340 }
341
342 /*---------------------------------------------------------------------------*/
343
344 int rand_between(int low, int high)
345 {
346     return low + rand() / (RAND_MAX / (high - low + 1) + 1);
347 }
348
349 /*---------------------------------------------------------------------------*/