Fix coding style
[survex.git] / src / filename.c
blobee2e0f9ef621c7b4e24ec28d995d094ffb92a126
1 /* OS dependent filename manipulation routines
2 * Copyright (c) Olly Betts 1998-2003,2004,2005,2010,2011,2014
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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
12 * GNU General Public License for more details.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #include "filename.h"
24 #include "debug.h"
26 #include <ctype.h>
27 #include <string.h>
29 typedef struct filelist {
30 char *fnm;
31 FILE *fh;
32 struct filelist *next;
33 } filelist;
35 static filelist *flhead = NULL;
37 static void filename_register_output_with_fh(const char *fnm, FILE *fh);
39 /* safe_fopen should be used when writing a file
40 * fopenWithPthAndExt should be used when reading a file
43 /* Wrapper for fopen which throws a fatal error if it fails.
44 * Some versions of fopen() are quite happy to open a directory.
45 * We aren't, so catch this case. */
46 extern FILE *
47 safe_fopen(const char *fnm, const char *mode)
49 FILE *f;
50 SVX_ASSERT(mode[0] == 'w'); /* only expect to be used for writing */
51 if (fDirectory(fnm))
52 fatalerror(/*Filename “%s” refers to directory*/44, fnm);
54 f = fopen(fnm, mode);
55 if (!f) fatalerror(/*Failed to open output file “%s”*/47, fnm);
57 filename_register_output_with_fh(fnm, f);
58 return f;
61 /* Wrapper for fclose which throws a fatal error if there's been a write
62 * error.
64 extern void
65 safe_fclose(FILE *f)
67 SVX_ASSERT(f);
68 /* NB: use of | rather than || - we always want to call fclose() */
69 if (ferror(f) | (fclose(f) == EOF)) {
70 filelist *p;
71 for (p = flhead; p != NULL; p = p->next)
72 if (p->fh == f) break;
74 if (p && p->fnm) {
75 const char *fnm = p->fnm;
76 p->fnm = NULL;
77 p->fh = NULL;
78 (void)remove(fnm);
79 fatalerror(/*Error writing to file “%s”*/110, fnm);
81 /* f wasn't opened with safe_fopen(), so we don't know the filename. */
82 fatalerror(/*Error writing to file*/111);
86 extern FILE *
87 safe_fopen_with_ext(const char *fnm, const char *ext, const char *mode)
89 FILE *f;
90 char *p;
91 p = add_ext(fnm, ext);
92 f = safe_fopen(p, mode);
93 osfree(p);
94 return f;
97 static FILE *
98 fopen_not_dir(const char *fnm, const char *mode)
100 if (fDirectory(fnm)) return NULL;
101 return fopen(fnm, mode);
104 extern char *
105 path_from_fnm(const char *fnm)
107 char *pth;
108 const char *lf;
109 int lenpth = 0;
111 lf = strrchr(fnm, FNM_SEP_LEV);
112 #ifdef FNM_SEP_LEV2
114 const char *lf2 = strrchr(lf ? lf + 1 : fnm, FNM_SEP_LEV2);
115 if (lf2) lf = lf2;
117 #endif
118 #ifdef FNM_SEP_DRV
119 if (!lf) lf = strrchr(fnm, FNM_SEP_DRV);
120 #endif
121 if (lf) lenpth = lf - fnm + 1;
123 pth = osmalloc(lenpth + 1);
124 memcpy(pth, fnm, lenpth);
125 pth[lenpth] = '\0';
127 return pth;
130 extern char *
131 base_from_fnm(const char *fnm)
133 char *p;
135 p = strrchr(fnm, FNM_SEP_EXT);
136 /* Trim off any leaf extension, but dirs can have extensions too */
137 if (p && !strchr(p, FNM_SEP_LEV)
138 #ifdef FNM_SEP_LEV2
139 && !strchr(p, FNM_SEP_LEV2)
140 #endif
142 size_t len = (const char *)p - fnm;
144 p = osmalloc(len + 1);
145 memcpy(p, fnm, len);
146 p[len] = '\0';
147 return p;
150 return osstrdup(fnm);
153 extern char *
154 baseleaf_from_fnm(const char *fnm)
156 const char *p;
157 char *q;
158 size_t len;
160 p = fnm;
161 q = strrchr(p, FNM_SEP_LEV);
162 if (q) p = q + 1;
163 #ifdef FNM_SEP_LEV2
164 q = strrchr(p, FNM_SEP_LEV2);
165 if (q) p = q + 1;
166 #endif
168 q = strrchr(p, FNM_SEP_EXT);
169 if (q) len = (const char *)q - p; else len = strlen(p);
171 q = osmalloc(len + 1);
172 memcpy(q, p, len);
173 q[len] = '\0';
174 return q;
177 extern char *
178 leaf_from_fnm(const char *fnm)
180 const char *lf;
181 lf = strrchr(fnm, FNM_SEP_LEV);
182 if (lf) fnm = lf + 1;
183 #ifdef FNM_SEP_LEV2
184 lf = strrchr(fnm, FNM_SEP_LEV2);
185 if (lf) fnm = lf + 1;
186 #endif
187 #ifdef FNM_SEP_DRV
188 lf = strrchr(fnm, FNM_SEP_DRV);
189 if (lf) fnm = lf + 1;
190 #endif
191 return osstrdup(fnm);
194 /* Make fnm from pth and lf, inserting an FNM_SEP_LEV if appropriate */
195 extern char *
196 use_path(const char *pth, const char *lf)
198 char *fnm;
199 int len, len_total;
200 bool fAddSep = fFalse;
202 len = strlen(pth);
203 len_total = len + strlen(lf) + 1;
205 /* if there's a path and it doesn't end in a separator, insert one */
206 if (len && pth[len - 1] != FNM_SEP_LEV) {
207 #ifdef FNM_SEP_LEV2
208 if (pth[len - 1] != FNM_SEP_LEV2) {
209 #endif
210 #ifdef FNM_SEP_DRV
211 if (pth[len - 1] != FNM_SEP_DRV) {
212 #endif
213 fAddSep = fTrue;
214 len_total++;
215 #ifdef FNM_SEP_DRV
217 #endif
218 #ifdef FNM_SEP_LEV2
220 #endif
223 fnm = osmalloc(len_total);
224 strcpy(fnm, pth);
225 if (fAddSep) fnm[len++] = FNM_SEP_LEV;
226 strcpy(fnm + len, lf);
227 return fnm;
230 /* Add ext to fnm, inserting an FNM_SEP_EXT if appropriate */
231 extern char *
232 add_ext(const char *fnm, const char *ext)
234 char * fnmNew;
235 int len, len_total;
236 #ifdef FNM_SEP_EXT
237 bool fAddSep = fFalse;
238 #endif
240 len = strlen(fnm);
241 len_total = len + strlen(ext) + 1;
242 #ifdef FNM_SEP_EXT
243 if (ext[0] != FNM_SEP_EXT) {
244 fAddSep = fTrue;
245 len_total++;
247 #endif
249 fnmNew = osmalloc(len_total);
250 strcpy(fnmNew, fnm);
251 #ifdef FNM_SEP_EXT
252 if (fAddSep) fnmNew[len++] = FNM_SEP_EXT;
253 #endif
254 strcpy(fnmNew + len, ext);
255 return fnmNew;
258 /* fopen file, found using pth and fnm
259 * fnmUsed is used to return filename used to open file (ignored if NULL)
260 * or NULL if file didn't open
262 extern FILE *
263 fopenWithPthAndExt(const char *pth, const char *fnm, const char *ext,
264 const char *mode, char **fnmUsed)
266 char *fnmFull = NULL;
267 FILE *fh = NULL;
268 bool fAbs;
270 /* if no pth treat fnm as absolute */
271 fAbs = (pth == NULL || *pth == '\0' || fAbsoluteFnm(fnm));
273 /* if appropriate, try it without pth */
274 if (fAbs) {
275 fh = fopen_not_dir(fnm, mode);
276 if (fh) {
277 if (fnmUsed) fnmFull = osstrdup(fnm);
278 } else {
279 if (ext && *ext) {
280 /* we've been given an extension so try using it */
281 fnmFull = add_ext(fnm, ext);
282 fh = fopen_not_dir(fnmFull, mode);
285 } else {
286 /* try using path given - first of all without the extension */
287 fnmFull = use_path(pth, fnm);
288 fh = fopen_not_dir(fnmFull, mode);
289 if (!fh) {
290 if (ext && *ext) {
291 /* we've been given an extension so try using it */
292 char *fnmTmp;
293 fnmTmp = fnmFull;
294 fnmFull = add_ext(fnmFull, ext);
295 osfree(fnmTmp);
296 fh = fopen_not_dir(fnmFull, mode);
301 /* either it opened or didn't. If not, fh == NULL from fopen_not_dir() */
303 /* free name if it didn't open or name isn't wanted */
304 if (fh == NULL || fnmUsed == NULL) osfree(fnmFull);
305 if (fnmUsed) *fnmUsed = (fh ? fnmFull : NULL);
306 return fh;
309 /* Like fopenWithPthAndExt except that "foreign" paths are translated to
310 * native ones (e.g. on Unix dir\file.ext -> dir/file.ext) */
311 FILE *
312 fopen_portable(const char *pth, const char *fnm, const char *ext,
313 const char *mode, char **fnmUsed)
315 FILE *fh = fopenWithPthAndExt(pth, fnm, ext, mode, fnmUsed);
316 if (fh == NULL) {
317 #if OS_UNIX
318 int f_changed = 0;
319 char *fnm_trans, *p;
320 fnm_trans = osstrdup(fnm);
321 for (p = fnm_trans; *p; p++) {
322 switch (*p) {
323 case '\\': /* swap a backslash to a forward slash */
324 *p = '/';
325 f_changed = 1;
326 break;
329 if (f_changed)
330 fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed);
332 /* as a last ditch measure, try lowercasing the filename */
333 if (fh == NULL) {
334 f_changed = 0;
335 for (p = fnm_trans; *p ; p++) {
336 unsigned char ch = *p;
337 if (isupper(ch)) {
338 *p = tolower(ch);
339 f_changed = 1;
342 if (f_changed)
343 fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed);
345 osfree(fnm_trans);
346 #endif
348 return fh;
351 void
352 filename_register_output(const char *fnm)
354 filelist *p = osnew(filelist);
355 SVX_ASSERT(fnm);
356 p->fnm = osstrdup(fnm);
357 p->fh = NULL;
358 p->next = flhead;
359 flhead = p;
362 static void
363 filename_register_output_with_fh(const char *fnm, FILE *fh)
365 filelist *p = osnew(filelist);
366 SVX_ASSERT(fnm);
367 p->fnm = osstrdup(fnm);
368 p->fh = fh;
369 p->next = flhead;
370 flhead = p;
373 void
374 filename_delete_output(void)
376 while (flhead) {
377 filelist *p = flhead;
378 flhead = flhead->next;
379 if (p->fnm) {
380 (void)remove(p->fnm);
381 osfree(p->fnm);
383 osfree(p);