1 /*** pxfile.c *****************************************************************
2 **
3 ** This file is part of BibTool.
4 ** It is distributed under the GNU General Public License.
5 ** See the file COPYING for details.
6 **
7 ** (c) 1996-2020 Gerd Neugebauer
8 **
9 ** Net: gene@gerd-neugebauer.de
10 **
11 ** This program is free software; you can redistribute it and/or modify
12 ** it under the terms of the GNU General Public License as published by
13 ** the Free Software Foundation; either version 2, or (at your option)
14 ** any later version.
15 **
16 ** This program is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ** GNU General Public License for more details.
20 **
21 ** You should have received a copy of the GNU General Public License
22 ** along with this program; if not, write to the Free Software
23 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 **
25 **-----------------------------------------------------------------------------
26 ** Description:
27 ** This file provides routines for extended file opening. Files
28 ** are sought in a list of directories and optionally with a set
29 ** of extensions appended to them.
30 **
31 ** Patterns may be given which are used to determine the full file
32 ** name.
33 ** The patterns are stored in a special data structure. A
34 ** function is provided to allocate a pattern structure and fill
35 ** it from a string specification.
36 **
37 ******************************************************************************/
38
39 #include "config.h"
40 #include <bibtool/general.h>
41 #include <bibtool/pxfile.h>
42 #include <bibtool/sbuffer.h>
43
44 #include <ctype.h>
45
46 #ifndef __STDC__
47 #ifndef HAVE_GETENV
48 extern char * getenv _ARG((char* name)); /* */
49 #endif
50 #endif
51
52 #ifndef DEFAULT_PATTERN
53 #ifdef MSDOS
54 #define DEFAULT_PATTERN "%s\\%s"
55 #else
56 #define DEFAULT_PATTERN "%s/%s"
57 #endif
58 #endif
59
60 /*****************************************************************************/
61 /* Internal Programs */
62 /*===========================================================================*/
63
64 #ifdef __STDC__
65 #define _ARG(A) A
66 #else
67 #define _ARG(A) ()
68 #endif
69 FILE * px_fopen _ARG((char * name,char * mode,char **pattern,char **path,int (*show)_ARG((char*))));
70 char ** px_s2p _ARG((char * s,int sep));
71 static bool absolute_file _ARG((char *name,char **basename,char ***path));
72 static void expand_env _ARG((char * s,char * se,StringBuffer * res));
73
74 /*****************************************************************************/
75 /* External Programs */
76 /*===========================================================================*/
77
78
79 /*****************************************************************************/
80
81 /*-----------------------------------------------------------------------------
82 ** Variable: px_filename
83 ** Purpose: This variable contains the file name actually used by
84 ** the last |px_fopen()| call. The memory is automatically
85 ** managed and will be reused by the next call to
86 ** |px_fopen()|. Thus if you need to use it make a
87 ** private copy immediately after the call to the
88 ** function |px_fopen()|.
89 **___________________________________________________ */
90 char *px_filename = "" ;
91 static size_t px_len = 0 ;
92
93 static char * no_path[] = { "." , NULL };
94 static char * no_pattern[] = { DEFAULT_PATTERN, NULL };
95
96 /*-----------------------------------------------------------------------------
97 ** Function*: absolute_file()
98 ** Purpose: Check is the file is an absolute file name.
99 ** In this case make a new path array which contains the absolute
100 ** path only and its location in path. basename is the modified
101 ** file name.
102 **
103 ** Currently UN*X and MSDOS like file names are supported.
104 ** Arguments:
105 ** name the name of the file
106 ** basename
107 ** path
108 ** Returns: The return value is |true|
109 ** Otherwise |false| is returned and nothing changed.
110 **___________________________________________________ */
absolute_file(name,basename,path)111 static bool absolute_file(name,basename,path) /* */
112 char *name; /* */
113 char **basename; /* */
114 char ***path; /* */
115 { static char *absolut_path[2]; /* */
116 static int first = (-1); /* */
117 size_t l; /* */
118 char *sp; /* */
119 #undef SEPARATOR
120 /* */
121 #ifdef MSDOS
122 #define SEPARATOR '\\'
123 if ( *name != SEPARATOR /* starting with root or */
124 && ( *name == '\0' /* */
125 || *(name+1) != ':' /* */
126 || *(name+2) != SEPARATOR ) ) /* with drive */
127 #endif
128 #ifdef AMIGA
129 #define SEPARATOR '/'
130 if ( strchr(name,':') == NULL ) /* starting with root */
131 #endif
132 #ifndef SEPARATOR
133 #define SEPARATOR '/'
134 if ( *name != SEPARATOR ) /* starting with root */
135 #endif
136 { return false; } /* */
137 /* */
138 sp = strrchr(name,SEPARATOR); /* find last separator */
139 /* existence guarenteed! */
140 #ifdef AMIGA
141 if ( sp == NULL ) sp = strchr(name,':'); /* */
142 #endif
143 l = (size_t)(sp-name); /* length of directory. */
144 if (first) /* */
145 { first = 0; /* */
146 absolut_path[1] = NULL; /* mark end of array. */
147 absolut_path[0] = malloc(l+1); /* allocate */
148 } /* or */
149 else /* reallocate */
150 { absolut_path[0] = realloc(*absolut_path,l+1); /* space for the */
151 } /* directory name */
152 /* */
153 (void)strncpy(absolut_path[0],name,l); /* save directory name */
154 *(absolut_path[0]+l) = '\0'; /* and mark the end. */
155 /* */
156 *basename = name+l+1; /* return the base name */
157 *path = absolut_path; /* return the path array */
158 /* */
159 return true; /* finished. */
160 } /*------------------------*/
161
162 /*-----------------------------------------------------------------------------
163 ** Function: px_fopen()
164 ** Purpose: Open a file using path and pattern.
165 **
166 **
167 ** Arguments:
168 ** name (base) name of the file to open.
169 ** mode Mode for opening the file like used with |fopen()|.
170 ** pattern A |NULL| terminated array of patterns.
171 ** path The |NULL| terminated array of directories.
172 ** show A function pointer or |NULL|.
173 ** Returns: A file pointer refering to the file or |NULL|.
174 **___________________________________________________ */
px_fopen(name,mode,pattern,path,show)175 FILE * px_fopen(name,mode,pattern,path,show) /* */
176 char * name; /* */
177 char * mode; /* */
178 char **pattern; /* */
179 char **path; /* */
180 int (*show)_ARG((char*)); /* */
181 { char **fmt; /* */
182 char **pp; /* */
183 FILE * file; /* */
184 size_t len,plen; /* */
185 char **new_path; /* */
186 char *new_name; /* */
187 /* */
188 if ( path == NULL ) path = no_path; /* use default path */
189 if ( pattern == NULL ) pattern = no_pattern; /* use default pattern */
190 /* */
191 if ( absolute_file(name,&new_name,&new_path) ) /* */
192 { name = new_name; /* */
193 path = new_path; /* */
194 } /* */
195 /* */
196 for ( plen=0,fmt=pattern;*fmt;++fmt ) /* compute longest */
197 { len = strlen(*fmt); /* pattern */
198 if ( len > plen ) plen = len; /* */
199 } /* */
200 plen += 1+strlen(name); /* add length of name */
201 /* and 1 for '\0' */
202 if ( px_len == 0 && /* initial filename */
203 (px_filename = malloc(plen))==NULL ) /* then allocate or */
204 return(NULL); /* terminate */
205 px_len = plen-1; /* usable is 1 less */
206 /* */
207 for ( pp=path;*pp;++pp ) /* use all paths */
208 { len = strlen(*pp) + plen; /* required space */
209 if ( px_len < len ) /* if less present get it */
210 { char * old = px_filename; /* */
211 if ( (px_filename = realloc(px_filename,len))==NULL ) {/* */
212 free(old); /* */
213 return(NULL); /* */
214 } /* */
215 px_len = len-1; /* usable is 1 less */
216 } /* */
217 /* */
218 for ( fmt=pattern;*fmt;++fmt ) /* use all patterns */
219 { (void)sprintf(px_filename,*fmt,*pp,name); /* construct new filename */
220 if ( ( show == NULL || /* if function present */
221 (*show)(px_filename) ) && /* and it says yes */
222 (file=fopen(px_filename,mode)) != NULL )/* and open succeeds */
223 return(file); /* then return file ptr */
224 } /* */
225 } /* */
226 *px_filename = '\0'; /* clear filename */
227 return(NULL); /* failure */
228 } /*------------------------*/
229
230 #define INIT_SIZE 32
231 #define INC_SIZE 16
232
233 /*-----------------------------------------------------------------------------
234 ** Function: px_s2p()
235 ** Purpose: Translate a path string specification into an array of the
236 ** components.
237 ** The memory of the array is malloced and should be freed when
238 ** not used any longer.
239 ** Arguments:
240 ** s String to analyze
241 ** sep Separator
242 ** Returns: The array of the components
243 **___________________________________________________ */
px_s2p(s,sep)244 char ** px_s2p(s,sep) /* */
245 char * s; /* */
246 int sep; /* */
247 { register char *cp; /* */
248 register size_t l = 1; /* */
249 char **pattern; /* */
250 int *array; /* */
251 size_t array_size = INIT_SIZE; /* */
252 size_t array_ptr = 0; /* */
253 char *t; /* */
254 StringBuffer *sb; /* */
255 /* */
256 if (s == NULL) return NULL; /* No string to analyze */
257 /* */
258 if ( (array=(int*)malloc(array_size*sizeof(int)))/* */
259 == NULL ) /* */
260 { return NULL; } /* */
261 /* */
262 t = cp = s; /* */
263 sb = sbopen(); /* */
264 for(;;) /* Count the number of */
265 { /* fields and expand */
266 if ( *cp == sep || *cp == '\0' ) /* */
267 { if ( array_ptr >= array_size ) /* */
268 { array_size += INC_SIZE; /* */
269 int* old = array; /* */
270 array = (int*)realloc(array, /* */
271 array_size*sizeof(int));/* */
272 if ( array == NULL ) { /* */
273 free(old); /* */
274 return NULL; /* */
275 } /* */
276 } /* */
277 array[array_ptr++] = sbtell(sb); /* */
278 expand_env(t,cp,sb); /* */
279 sbputchar('\0',sb); /* */
280 t = cp+1; /* */
281 if ( *cp ) cp++; /* */
282 else break; /* */
283 } /* */
284 else /* */
285 { cp++; /* */
286 } /* */
287 } /* */
288 /* */
289 l = sbtell(sb); /* */
290 s = sbflush(sb); /* */
291 /* */
292 if ( (pattern=(char**)malloc( (array_ptr+1)*sizeof(char*)/* */
293 +l*sizeof(char))) /* */
294 == (char**)NULL ) /* Try to allocate space */
295 { return NULL; } /* */
296 /* */
297 cp = (char*)&pattern[array_ptr+1]; /* */
298 (void)memcpy(cp,s,l); /* save the string spec */
299 /* */
300 for ( l = 0; l < array_ptr; l++ ) /* */
301 { pattern[l] = cp + array[l]; } /* */
302 pattern[array_ptr] = 0L; /* Mark end */
303 /* */
304 (void)free(array); /* */
305 (void)sbclose(sb); /* */
306 return pattern; /* */
307 } /*------------------------*/
308
309 /*-----------------------------------------------------------------------------
310 ** Function*: expand_env()
311 ** Type: static char *
312 ** Purpose:
313 **
314 ** Arguments:
315 ** s
316 ** se
317 ** Returns:
318 **___________________________________________________ */
expand_env(s,se,res)319 static void expand_env(s,se,res) /* */
320 char * s; /* */
321 char * se; /* */
322 StringBuffer * res; /* */
323 { /* */
324 StringBuffer * val = sbopen(); /* */
325 /* */
326 for (; s < se; s++) /* */
327 { /* */
328 if (*s == '$') /* */
329 { char beg = '\0'; /* */
330 char * env; /* */
331 sbrewind(val); /* */
332 s++; /* */
333 if ( *s == '{' || *s == '(' ) /* */
334 { beg = *(s++); } /* */
335 while ( isalpha(*s) || isdigit(*s) || *s == '_' )/* */
336 { sbputchar(*s,val); /* */
337 s++; /* */
338 } /* */
339 if ( ( beg == '{' && *s == '}' ) || /* */
340 ( beg == '(' && *s == ')' ) /* */
341 ) /* */
342 { s++; } /* */
343 env = sbflush(val); /* */
344 if (*env) /* */
345 { env = getenv(env); /* */
346 if ( env != NULL ) /* */
347 { sbputs(env,res); } /* */
348 } /* */
349 } /* */
350 #ifdef HOME_ENV_VAR
351 else if (*s == '~') /* */
352 { /* */
353 char * env = getenv(HOME_ENV_VAR); /* */
354 if (env != NULL) /* */
355 { sbputs(env,res); } /* */
356 } /* */
357 #endif
358 else /* */
359 { sbputchar(*s,res); /* */
360 } /* */
361 } /* */
362 } /*------------------------*/
363
364