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