source: git/src/filename.c @ b3b0900

walls-datawalls-data-hanging-as-warning
Last change on this file since b3b0900 was b3b0900, checked in by Olly Betts <olly@…>, 2 weeks ago

Clean up header includes

  • Property mode set to 100644
File size: 9.4 KB
Line 
1/* OS dependent filename manipulation routines
2 * Copyright (c) Olly Betts 1998-2003,2004,2005,2010,2011,2014
3 *
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.
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
12 * GNU 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include <config.h>
20
21#include "filename.h"
22#include "debug.h"
23#include "osalloc.h"
24
25#include <ctype.h>
26#include <string.h>
27
28typedef struct filelist {
29   char *fnm;
30   FILE *fh;
31   struct filelist *next;
32} filelist;
33
34static filelist *flhead = NULL;
35
36static void filename_register_output_with_fh(const char *fnm, FILE *fh);
37
38/* safe_fopen should be used when writing a file
39 * fopenWithPthAndExt should be used when reading a file
40 */
41
42/* Wrapper for fopen which throws a fatal error if it fails.
43 * Some versions of fopen() are quite happy to open a directory.
44 * We aren't, so catch this case. */
45extern FILE *
46safe_fopen(const char *fnm, const char *mode)
47{
48   FILE *f;
49   SVX_ASSERT(mode[0] == 'w'); /* only expect to be used for writing */
50   if (fDirectory(fnm))
51      fatalerror(/*Filename “%s” refers to directory*/44, fnm);
52
53   f = fopen(fnm, mode);
54   if (!f) fatalerror(/*Failed to open output file “%s”*/47, fnm);
55
56   filename_register_output_with_fh(fnm, f);
57   return f;
58}
59
60/* Wrapper for fclose which throws a fatal error if there's been a write
61 * error.
62 */
63extern void
64safe_fclose(FILE *f)
65{
66   SVX_ASSERT(f);
67   /* NB: use of | rather than || - we always want to call fclose() */
68   if (ferror(f) | (fclose(f) == EOF)) {
69      filelist *p;
70      for (p = flhead; p != NULL; p = p->next)
71         if (p->fh == f) break;
72
73      if (p && p->fnm) {
74         const char *fnm = p->fnm;
75         p->fnm = NULL;
76         p->fh = NULL;
77         (void)remove(fnm);
78         fatalerror(/*Error writing to file “%s”*/110, fnm);
79      }
80      /* f wasn't opened with safe_fopen(), so we don't know the filename. */
81      fatalerror(/*Error writing to file*/111);
82   }
83}
84
85extern FILE *
86safe_fopen_with_ext(const char *fnm, const char *ext, const char *mode)
87{
88   FILE *f;
89   char *p;
90   p = add_ext(fnm, ext);
91   f = safe_fopen(p, mode);
92   osfree(p);
93   return f;
94}
95
96static FILE *
97fopen_not_dir(const char *fnm, const char *mode)
98{
99   if (fDirectory(fnm)) return NULL;
100   return fopen(fnm, mode);
101}
102
103extern char *
104path_from_fnm(const char *fnm)
105{
106   char *pth;
107   const char *lf;
108   int lenpth = 0;
109
110   lf = strrchr(fnm, FNM_SEP_LEV);
111#ifdef FNM_SEP_LEV2
112   {
113      const char *lf2 = strrchr(lf ? lf + 1 : fnm, FNM_SEP_LEV2);
114      if (lf2) lf = lf2;
115   }
116#endif
117#ifdef FNM_SEP_DRV
118   if (!lf) lf = strrchr(fnm, FNM_SEP_DRV);
119#endif
120   if (lf) lenpth = lf - fnm + 1;
121
122   pth = osmalloc(lenpth + 1);
123   memcpy(pth, fnm, lenpth);
124   pth[lenpth] = '\0';
125
126   return pth;
127}
128
129extern char *
130base_from_fnm(const char *fnm)
131{
132   char *p;
133
134   p = strrchr(fnm, FNM_SEP_EXT);
135   /* Trim off any leaf extension, but dirs can have extensions too */
136   if (p && !strchr(p, FNM_SEP_LEV)
137#ifdef FNM_SEP_LEV2
138       && !strchr(p, FNM_SEP_LEV2)
139#endif
140       ) {
141      size_t len = (const char *)p - fnm;
142
143      p = osmalloc(len + 1);
144      memcpy(p, fnm, len);
145      p[len] = '\0';
146      return p;
147   }
148
149   return osstrdup(fnm);
150}
151
152extern char *
153baseleaf_from_fnm(const char *fnm)
154{
155   const char *p;
156   char *q;
157   size_t len;
158
159   p = fnm;
160   q = strrchr(p, FNM_SEP_LEV);
161   if (q) p = q + 1;
162#ifdef FNM_SEP_LEV2
163   q = strrchr(p, FNM_SEP_LEV2);
164   if (q) p = q + 1;
165#endif
166
167   q = strrchr(p, FNM_SEP_EXT);
168   if (q) len = (const char *)q - p; else len = strlen(p);
169
170   q = osmalloc(len + 1);
171   memcpy(q, p, len);
172   q[len] = '\0';
173   return q;
174}
175
176extern char *
177leaf_from_fnm(const char *fnm)
178{
179   const char *lf;
180   lf = strrchr(fnm, FNM_SEP_LEV);
181   if (lf) fnm = lf + 1;
182#ifdef FNM_SEP_LEV2
183   lf = strrchr(fnm, FNM_SEP_LEV2);
184   if (lf) fnm = lf + 1;
185#endif
186#ifdef FNM_SEP_DRV
187   lf = strrchr(fnm, FNM_SEP_DRV);
188   if (lf) fnm = lf + 1;
189#endif
190   return osstrdup(fnm);
191}
192
193/* Make fnm from pth and lf, inserting an FNM_SEP_LEV if appropriate */
194extern char *
195use_path(const char *pth, const char *lf)
196{
197   char *fnm;
198   int len, len_total;
199   bool fAddSep = false;
200
201   len = strlen(pth);
202   len_total = len + strlen(lf) + 1;
203
204   /* if there's a path and it doesn't end in a separator, insert one */
205   if (len && pth[len - 1] != FNM_SEP_LEV) {
206#ifdef FNM_SEP_LEV2
207      if (pth[len - 1] != FNM_SEP_LEV2) {
208#endif
209#ifdef FNM_SEP_DRV
210         if (pth[len - 1] != FNM_SEP_DRV) {
211#endif
212            fAddSep = true;
213            len_total++;
214#ifdef FNM_SEP_DRV
215         }
216#endif
217#ifdef FNM_SEP_LEV2
218      }
219#endif
220   }
221
222   fnm = osmalloc(len_total);
223   strcpy(fnm, pth);
224   if (fAddSep) fnm[len++] = FNM_SEP_LEV;
225   strcpy(fnm + len, lf);
226   return fnm;
227}
228
229/* Add ext to fnm, inserting an FNM_SEP_EXT if appropriate */
230extern char *
231add_ext(const char *fnm, const char *ext)
232{
233   char * fnmNew;
234   int len, len_total;
235   bool fAddSep = false;
236
237   len = strlen(fnm);
238   len_total = len + strlen(ext) + 1;
239   if (ext[0] != FNM_SEP_EXT) {
240      fAddSep = true;
241      len_total++;
242   }
243
244   fnmNew = osmalloc(len_total);
245   strcpy(fnmNew, fnm);
246   if (fAddSep) fnmNew[len++] = FNM_SEP_EXT;
247   strcpy(fnmNew + len, ext);
248   return fnmNew;
249}
250
251/* fopen file, found using pth and fnm
252 * fnmUsed is used to return filename used to open file (ignored if NULL)
253 * or NULL if file didn't open
254 */
255extern FILE *
256fopenWithPthAndExt(const char *pth, const char *fnm, const char *ext,
257                   const char *mode, char **fnmUsed)
258{
259   char *fnmFull = NULL;
260   FILE *fh = NULL;
261   bool fAbs;
262
263   /* if no pth treat fnm as absolute */
264   fAbs = (pth == NULL || *pth == '\0' || fAbsoluteFnm(fnm));
265
266   /* if appropriate, try it without pth */
267   if (fAbs) {
268      fh = fopen_not_dir(fnm, mode);
269      if (fh) {
270         if (fnmUsed) fnmFull = osstrdup(fnm);
271      } else {
272         if (ext && *ext) {
273            /* we've been given an extension so try using it */
274            fnmFull = add_ext(fnm, ext);
275            fh = fopen_not_dir(fnmFull, mode);
276         }
277      }
278   } else {
279      /* try using path given - first of all without the extension */
280      fnmFull = use_path(pth, fnm);
281      fh = fopen_not_dir(fnmFull, mode);
282      if (!fh) {
283         if (ext && *ext) {
284            /* we've been given an extension so try using it */
285            char *fnmTmp;
286            fnmTmp = fnmFull;
287            fnmFull = add_ext(fnmFull, ext);
288            osfree(fnmTmp);
289            fh = fopen_not_dir(fnmFull, mode);
290         }
291      }
292   }
293
294   /* either it opened or didn't. If not, fh == NULL from fopen_not_dir() */
295
296   /* free name if it didn't open or name isn't wanted */
297   if (fh == NULL || fnmUsed == NULL) osfree(fnmFull);
298   if (fnmUsed) *fnmUsed = (fh ? fnmFull : NULL);
299   return fh;
300}
301
302/* Like fopenWithPthAndExt except that "foreign" paths are translated to
303 * native ones (e.g. on Unix dir\file.ext -> dir/file.ext) */
304FILE *
305fopen_portable(const char *pth, const char *fnm, const char *ext,
306               const char *mode, char **fnmUsed)
307{
308   FILE *fh = fopenWithPthAndExt(pth, fnm, ext, mode, fnmUsed);
309   if (fh == NULL) {
310#if OS_UNIX
311      bool changed = false;
312      char *fnm_trans = osstrdup(fnm);
313      for (char *p = fnm_trans; *p; p++) {
314         switch (*p) {
315         case '\\': /* swap a backslash to a forward slash */
316            *p = '/';
317            changed = true;
318            break;
319         }
320      }
321      if (changed)
322         fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed);
323
324      /* To help users process data that originated on a case-insensitive
325       * filing system, try lowercasing the filename if not found.
326       */
327      if (fh == NULL) {
328         bool had_lower = false;
329         changed = false;
330         for (char *p = fnm_trans; *p ; p++) {
331            unsigned char ch = *p;
332            if (isupper(ch)) {
333               *p = tolower(ch);
334               changed = true;
335            } else if (islower(ch)) {
336               had_lower = true;
337            }
338         }
339         if (changed)
340            fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed);
341
342         /* If that fails, try upper casing the initial character of the leaf. */
343         if (fh == NULL) {
344            char *leaf = strrchr(fnm_trans, '/');
345            leaf = (leaf ? leaf + 1 : fnm_trans);
346            if (islower((unsigned char)*leaf)) {
347               *leaf = toupper((unsigned char)*leaf);
348               fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed);
349            }
350            if (fh == NULL && had_lower) {
351               /* Finally, try upper casing the filename if it wasn't all
352                * upper case to start with. */
353               for (char *p = fnm_trans; *p ; p++) {
354                  *p = toupper((unsigned char)*p);
355               }
356               fh = fopenWithPthAndExt(pth, fnm_trans, ext, mode, fnmUsed);
357            }
358         }
359      }
360      osfree(fnm_trans);
361#endif
362   }
363   return fh;
364}
365
366void
367filename_register_output(const char *fnm)
368{
369   filelist *p = osnew(filelist);
370   SVX_ASSERT(fnm);
371   p->fnm = osstrdup(fnm);
372   p->fh = NULL;
373   p->next = flhead;
374   flhead = p;
375}
376
377static void
378filename_register_output_with_fh(const char *fnm, FILE *fh)
379{
380   filelist *p = osnew(filelist);
381   SVX_ASSERT(fnm);
382   p->fnm = osstrdup(fnm);
383   p->fh = fh;
384   p->next = flhead;
385   flhead = p;
386}
387
388void
389filename_delete_output(void)
390{
391   while (flhead) {
392      filelist *p = flhead;
393      flhead = flhead->next;
394      if (p->fnm) {
395         (void)remove(p->fnm);
396         osfree(p->fnm);
397      }
398      osfree(p);
399   }
400}
Note: See TracBrowser for help on using the repository browser.