source: git/src/filename.c @ f96897b

faster-cavernloglog-selectwalls-datawalls-data-hanging-as-warning
Last change on this file since f96897b was 4c83f84, checked in by Olly Betts <olly@…>, 4 weeks ago

Don't check HAVE_CONFIG_H in most cases

This check is only useful for img.c, which is intended to be usable
outside of Survex (and had fallbacks for functions which may not be
available which will get used if built in a non-autotools project).
For all the other source files it's just useless boilerplate.

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