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
29 typedef struct filelist
{
32 struct filelist
*next
;
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. */
47 safe_fopen(const char *fnm
, const char *mode
)
50 SVX_ASSERT(mode
[0] == 'w'); /* only expect to be used for writing */
52 fatalerror(/*Filename “%s” refers to directory*/44, fnm
);
55 if (!f
) fatalerror(/*Failed to open output file “%s”*/47, fnm
);
57 filename_register_output_with_fh(fnm
, f
);
61 /* Wrapper for fclose which throws a fatal error if there's been a write
68 /* NB: use of | rather than || - we always want to call fclose() */
69 if (ferror(f
) | (fclose(f
) == EOF
)) {
71 for (p
= flhead
; p
!= NULL
; p
= p
->next
)
72 if (p
->fh
== f
) break;
75 const char *fnm
= p
->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);
87 safe_fopen_with_ext(const char *fnm
, const char *ext
, const char *mode
)
91 p
= add_ext(fnm
, ext
);
92 f
= safe_fopen(p
, mode
);
98 fopen_not_dir(const char *fnm
, const char *mode
)
100 if (fDirectory(fnm
)) return NULL
;
101 return fopen(fnm
, mode
);
105 path_from_fnm(const char *fnm
)
111 lf
= strrchr(fnm
, FNM_SEP_LEV
);
114 const char *lf2
= strrchr(lf
? lf
+ 1 : fnm
, FNM_SEP_LEV2
);
119 if (!lf
) lf
= strrchr(fnm
, FNM_SEP_DRV
);
121 if (lf
) lenpth
= lf
- fnm
+ 1;
123 pth
= osmalloc(lenpth
+ 1);
124 memcpy(pth
, fnm
, lenpth
);
131 base_from_fnm(const char *fnm
)
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
)
139 && !strchr(p
, FNM_SEP_LEV2
)
142 size_t len
= (const char *)p
- fnm
;
144 p
= osmalloc(len
+ 1);
150 return osstrdup(fnm
);
154 baseleaf_from_fnm(const char *fnm
)
161 q
= strrchr(p
, FNM_SEP_LEV
);
164 q
= strrchr(p
, FNM_SEP_LEV2
);
168 q
= strrchr(p
, FNM_SEP_EXT
);
169 if (q
) len
= (const char *)q
- p
; else len
= strlen(p
);
171 q
= osmalloc(len
+ 1);
178 leaf_from_fnm(const char *fnm
)
181 lf
= strrchr(fnm
, FNM_SEP_LEV
);
182 if (lf
) fnm
= lf
+ 1;
184 lf
= strrchr(fnm
, FNM_SEP_LEV2
);
185 if (lf
) fnm
= lf
+ 1;
188 lf
= strrchr(fnm
, FNM_SEP_DRV
);
189 if (lf
) fnm
= lf
+ 1;
191 return osstrdup(fnm
);
194 /* Make fnm from pth and lf, inserting an FNM_SEP_LEV if appropriate */
196 use_path(const char *pth
, const char *lf
)
200 bool fAddSep
= fFalse
;
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
) {
208 if (pth
[len
- 1] != FNM_SEP_LEV2
) {
211 if (pth
[len
- 1] != FNM_SEP_DRV
) {
223 fnm
= osmalloc(len_total
);
225 if (fAddSep
) fnm
[len
++] = FNM_SEP_LEV
;
226 strcpy(fnm
+ len
, lf
);
230 /* Add ext to fnm, inserting an FNM_SEP_EXT if appropriate */
232 add_ext(const char *fnm
, const char *ext
)
237 bool fAddSep
= fFalse
;
241 len_total
= len
+ strlen(ext
) + 1;
243 if (ext
[0] != FNM_SEP_EXT
) {
249 fnmNew
= osmalloc(len_total
);
252 if (fAddSep
) fnmNew
[len
++] = FNM_SEP_EXT
;
254 strcpy(fnmNew
+ len
, ext
);
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
263 fopenWithPthAndExt(const char *pth
, const char *fnm
, const char *ext
,
264 const char *mode
, char **fnmUsed
)
266 char *fnmFull
= NULL
;
270 /* if no pth treat fnm as absolute */
271 fAbs
= (pth
== NULL
|| *pth
== '\0' || fAbsoluteFnm(fnm
));
273 /* if appropriate, try it without pth */
275 fh
= fopen_not_dir(fnm
, mode
);
277 if (fnmUsed
) fnmFull
= osstrdup(fnm
);
280 /* we've been given an extension so try using it */
281 fnmFull
= add_ext(fnm
, ext
);
282 fh
= fopen_not_dir(fnmFull
, mode
);
286 /* try using path given - first of all without the extension */
287 fnmFull
= use_path(pth
, fnm
);
288 fh
= fopen_not_dir(fnmFull
, mode
);
291 /* we've been given an extension so try using it */
294 fnmFull
= add_ext(fnmFull
, ext
);
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
);
309 /* Like fopenWithPthAndExt except that "foreign" paths are translated to
310 * native ones (e.g. on Unix dir\file.ext -> dir/file.ext) */
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
);
320 fnm_trans
= osstrdup(fnm
);
321 for (p
= fnm_trans
; *p
; p
++) {
323 case '\\': /* swap a backslash to a forward slash */
330 fh
= fopenWithPthAndExt(pth
, fnm_trans
, ext
, mode
, fnmUsed
);
332 /* as a last ditch measure, try lowercasing the filename */
335 for (p
= fnm_trans
; *p
; p
++) {
336 unsigned char ch
= *p
;
343 fh
= fopenWithPthAndExt(pth
, fnm_trans
, ext
, mode
, fnmUsed
);
352 filename_register_output(const char *fnm
)
354 filelist
*p
= osnew(filelist
);
356 p
->fnm
= osstrdup(fnm
);
363 filename_register_output_with_fh(const char *fnm
, FILE *fh
)
365 filelist
*p
= osnew(filelist
);
367 p
->fnm
= osstrdup(fnm
);
374 filename_delete_output(void)
377 filelist
*p
= flhead
;
378 flhead
= flhead
->next
;
380 (void)remove(p
->fnm
);