source: git/src/filename.c @ dcf6125

RELEASE/1.0RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since dcf6125 was f1067a2, checked in by Olly Betts <olly@…>, 24 years ago

Added safe_fclose() which checks ferror() and return value from fclose().

git-svn-id: file:///home/survex-svn/survex/trunk@1223 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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