Make sure strncpy always keeps one byte untouched for NUL
[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 buff[MAXSTR];
39
40     char *line, *new;
41     size_t len0, len1;
42
43     line = NULL;
44
45     while (fs_gets(buff, sizeof (buff), fin))
46     {
47         /* Append to data read so far. */
48
49         if (line)
50         {
51             new  = concat_string(line, buff, NULL);
52             free(line);
53             line = new;
54         }
55         else
56         {
57             line = strdup(buff);
58         }
59
60         /* Strip newline, if any. */
61
62         len0 = strlen(line);
63         strip_newline(line);
64         len1 = strlen(line);
65
66         if (len1 != len0)
67         {
68             /* We hit a newline, clean up and break. */
69             line = realloc(line, len1 + 1);
70             break;
71         }
72     }
73
74     return (*dst = line) ? 1 : 0;
75 }
76
77 char *strip_newline(char *str)
78 {
79     char *c = str + strlen(str) - 1;
80
81     while (c >= str && (*c == '\n' || *c =='\r'))
82         *c-- = '\0';
83
84     return str;
85 }
86
87 char *dupe_string(const char *src)
88 {
89     char *dst = NULL;
90
91     if (src && (dst = malloc(strlen(src) + 1)))
92         strcpy(dst, src);
93
94     return dst;
95 }
96
97 char *concat_string(const char *first, ...)
98 {
99     char *full;
100
101     if ((full = strdup(first)))
102     {
103         const char *part;
104         va_list ap;
105
106         va_start(ap, first);
107
108         while ((part = va_arg(ap, const char *)))
109         {
110             char *new;
111
112             if ((new = realloc(full, strlen(full) + strlen(part) + 1)))
113             {
114                 full = new;
115                 strcat(full, part);
116             }
117             else
118             {
119                 free(full);
120                 full = NULL;
121                 break;
122             }
123         }
124
125         va_end(ap);
126     }
127
128     return full;
129 }
130
131 char *trunc_string(const char *src, char *dst, int len)
132 {
133     static const char ell[] = "...";
134
135     assert(len > sizeof (ell));
136
137     if (dst[len - 1] = '\0', strncpy(dst, src, len), dst[len - 1] != '\0')
138         strcpy(dst + len - sizeof (ell), ell);
139
140     return dst;
141 }
142
143 time_t make_time_from_utc(struct tm *tm)
144 {
145     struct tm local, *utc;
146     time_t t;
147
148     t = mktime(tm);
149
150     local = *localtime(&t);
151     utc   =  gmtime(&t);
152
153     local.tm_year += local.tm_year - utc->tm_year;
154     local.tm_mon  += local.tm_mon  - utc->tm_mon ;
155     local.tm_mday += local.tm_mday - utc->tm_mday;
156     local.tm_hour += local.tm_hour - utc->tm_hour;
157     local.tm_min  += local.tm_min  - utc->tm_min ;
158     local.tm_sec  += local.tm_sec  - utc->tm_sec ;
159
160     return mktime(&local);
161 }
162
163 const char *date_to_str(time_t i)
164 {
165     static char str[sizeof ("YYYY-mm-dd HH:MM:SS")];
166     strftime(str, sizeof (str), "%Y-%m-%d %H:%M:%S", localtime(&i));
167     return str;
168 }
169
170 int file_exists(const char *name)
171 {
172     FILE *fp;
173
174     if ((fp = fopen(name, "r")))
175     {
176         fclose(fp);
177         return 1;
178     }
179     return 0;
180 }
181
182 int file_rename(const char *src, const char *dst)
183 {
184 #ifdef _WIN32
185     if (file_exists(dst))
186         remove(dst);
187 #endif
188     return rename(src, dst);
189 }
190
191 void file_copy(FILE *fin, FILE *fout)
192 {
193     char   buff[MAXSTR];
194     size_t size;
195
196     while ((size = fread(buff, 1, sizeof (buff), fin)) > 0)
197         fwrite(buff, 1, size, fout);
198 }
199
200 /*---------------------------------------------------------------------------*/
201
202 int path_is_sep(int c)
203 {
204 #ifdef _WIN32
205     return c == '/' || c == '\\';
206 #else
207     return c == '/';
208 #endif
209 }
210
211 int path_is_abs(const char *path)
212 {
213     if (path_is_sep(path[0]))
214         return 1;
215
216 #ifdef _WIN32
217     if (isalpha(path[0]) && path[1] == ':' && path_is_sep(path[2]))
218         return 1;
219 #endif
220
221     return 0;
222 }
223
224 static char *path_last_sep(const char *path)
225 {
226     char *sep;
227
228     sep = strrchr(path, '/');
229
230 #ifdef _WIN32
231     if (!sep)
232     {
233         sep = strrchr(path, '\\');
234     }
235     else
236     {
237         char *tmp;
238
239         if ((tmp = strrchr(sep, '\\')))
240             sep = tmp;
241     }
242 #endif
243
244     return sep;
245 }
246
247 char *base_name(const char *name, const char *suffix)
248 {
249     static char buf[MAXSTR];
250     char *base;
251
252     if (!name)
253         return NULL;
254
255     /* Remove the directory part. */
256
257     base = path_last_sep(name);
258
259     strncpy(buf, base ? base + 1 : name, sizeof (buf) - 1);
260
261     /* Remove the suffix. */
262
263     if (suffix)
264     {
265         int l = strlen(buf) - strlen(suffix);
266
267         if (l >= 0 && strcmp(buf + l, suffix) == 0)
268             buf[l] = '\0';
269     }
270
271     return buf;
272 }
273
274 const char *dir_name(const char *name)
275 {
276     static char buff[MAXSTR];
277
278     char *sep;
279
280     strncpy(buff, name, sizeof (buff) - 1);
281
282     if ((sep = path_last_sep(buff)))
283     {
284         if (sep == buff)
285             return "/";
286
287         *sep = '\0';
288
289         return buff;
290     }
291
292     return ".";
293 }
294
295 /*
296  * Given a path to a file REF and another path REL relative to REF,
297  * construct and return a new path that can be used to refer to REL
298  * directly.
299  */
300 char *path_resolve(const char *ref, const char *rel)
301 {
302     static char new[MAXSTR * 2];
303
304     if (path_is_abs(rel))
305     {
306         strncpy(new, rel, sizeof (new) - 1);
307         return new;
308     }
309
310     strncpy(new, dir_name(ref), sizeof (new) - 1);
311     strncat(new, "/",           sizeof (new) - strlen(new) - 1);
312     strncat(new, rel,           sizeof (new) - strlen(new) - 1);
313
314     return new;
315 }
316
317 /*---------------------------------------------------------------------------*/
318
319 int rand_between(int low, int high)
320 {
321     return low + rand() / (RAND_MAX / (high - low + 1) + 1);
322 }
323
324 /*---------------------------------------------------------------------------*/