xref: /openbsd-src/gnu/usr.bin/texinfo/makeinfo/files.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /* files.c -- file-related functions for Texinfo.
2    $Id: files.c,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $
3 
4    Copyright (C) 1998, 99 Free Software Foundation, Inc.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19 
20 #include "system.h"
21 #include "files.h"
22 #include "macro.h"
23 #include "makeinfo.h"
24 
25 FSTACK *filestack = NULL;
26 
27 static int node_filename_stack_index = 0;
28 static int node_filename_stack_size = 0;
29 static char **node_filename_stack = NULL;
30 
31 
32 /* Looking for include files.  */
33 
34 /* Given a string containing units of information separated by colons,
35    return the next one pointed to by INDEX, or NULL if there are no more.
36    Advance INDEX to the character after the colon. */
37 static char *
38 extract_colon_unit (string, index)
39      char *string;
40      int *index;
41 {
42   int start;
43   int path_sep_char = PATH_SEP[0];
44   int i = *index;
45 
46   if (!string || (i >= strlen (string)))
47     return NULL;
48 
49   /* Each call to this routine leaves the index pointing at a colon if
50      there is more to the path.  If i > 0, then increment past the
51      `:'.  If i == 0, then the path has a leading colon.  Trailing colons
52      are handled OK by the `else' part of the if statement; an empty
53      string is returned in that case. */
54   if (i && string[i] == path_sep_char)
55     i++;
56 
57   start = i;
58   while (string[i] && string[i] != path_sep_char) i++;
59   *index = i;
60 
61   if (i == start)
62     {
63       if (string[i])
64         (*index)++;
65 
66       /* Return "" in the case of a trailing `:'. */
67       return xstrdup ("");
68     }
69   else
70     {
71       char *value;
72 
73       value = xmalloc (1 + (i - start));
74       memcpy (value, &string[start], (i - start));
75       value [i - start] = 0;
76 
77       return value;
78     }
79 }
80 
81 /* Return the full pathname for FILENAME by searching along PATH.
82    When found, return the stat () info for FILENAME in FINFO.
83    If PATH is NULL, only the current directory is searched.
84    If the file could not be found, return a NULL pointer. */
85 static char *
86 get_file_info_in_path (filename, path, finfo)
87      char *filename, *path;
88      struct stat *finfo;
89 {
90   char *dir;
91   int result, index = 0;
92 
93   if (path == NULL)
94     path = ".";
95 
96   /* Handle absolute pathnames.  */
97   if (IS_ABSOLUTE (filename)
98       || (*filename == '.'
99           && (IS_SLASH (filename[1])
100               || (filename[1] == '.' && IS_SLASH (filename[2])))))
101     {
102       if (stat (filename, finfo) == 0)
103         return xstrdup (filename);
104       else
105         return NULL;
106     }
107 
108   while ((dir = extract_colon_unit (path, &index)))
109     {
110       char *fullpath;
111 
112       if (!*dir)
113         {
114           free (dir);
115           dir = xstrdup (".");
116         }
117 
118       fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
119       sprintf (fullpath, "%s/%s", dir, filename);
120       free (dir);
121 
122       result = stat (fullpath, finfo);
123 
124       if (result == 0)
125         return fullpath;
126       else
127         free (fullpath);
128     }
129   return NULL;
130 }
131 
132 /* Find and load the file named FILENAME.  Return a pointer to
133    the loaded file, or NULL if it can't be loaded. */
134 char *
135 find_and_load (filename)
136      char *filename;
137 {
138   struct stat fileinfo;
139   long file_size;
140   int file = -1, count = 0;
141   char *fullpath, *result;
142 #if O_BINARY || defined (VMS)
143   int n;
144 #endif
145 
146   result = fullpath = NULL;
147 
148   fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
149 
150   if (!fullpath)
151     goto error_exit;
152 
153   filename = fullpath;
154   file_size = (long) fileinfo.st_size;
155 
156   file = open (filename, O_RDONLY);
157   if (file < 0)
158     goto error_exit;
159 
160   /* Load the file, with enough room for a newline and a null. */
161   result = xmalloc (file_size + 2);
162 
163   /* VMS stat lies about the st_size value.  The actual number of
164      readable bytes is always less than this value.  The arcane
165      mysteries of VMS/RMS are too much to probe, so this hack
166     suffices to make things work. */
167 #if O_BINARY || defined (VMS)
168 #ifdef VMS
169   while ((n = read (file, result + count, file_size)) > 0)
170 #else  /* !VMS */
171 #ifndef WIN32
172   while ((n = read (file, result + count, file_size)) > 0)
173 #else /* WIN32 */
174   /* Does WIN32 really need reading 1 character at a time??  */
175   while ((n = read (file, result + count, 1)) > 0)
176 #endif /* WIN32 */
177 #endif /* !VMS */
178     count += n;
179   if (0 < count && count < file_size)
180     result = xrealloc (result, count + 2); /* why waste the slack? */
181   else if (n == -1)
182 #else /* !VMS && !O_BINARY */
183     count = file_size;
184     if (read (file, result, file_size) != file_size)
185 #endif /* !VMS && !WIN32 */
186 
187   error_exit:
188     {
189       if (result)
190         free (result);
191 
192       if (fullpath)
193         free (fullpath);
194 
195       if (file != -1)
196         close (file);
197 
198       return NULL;
199     }
200   close (file);
201 
202   /* Set the globals to the new file. */
203   input_text = result;
204   input_text_length = count;
205   input_filename = fullpath;
206   node_filename = xstrdup (fullpath);
207   input_text_offset = 0;
208   line_number = 1;
209   /* Not strictly necessary.  This magic prevents read_token () from doing
210      extra unnecessary work each time it is called (that is a lot of times).
211      INPUT_TEXT_LENGTH is one past the actual end of the text. */
212   input_text[input_text_length] = '\n';
213   /* This, on the other hand, is always necessary.  */
214   input_text[input_text_length+1] = 0;
215   return result;
216 }
217 
218 /* Pushing and popping files.  */
219 void
220 push_node_filename ()
221 {
222   if (node_filename_stack_index + 1 > node_filename_stack_size)
223     node_filename_stack = xrealloc
224     (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
225 
226   node_filename_stack[node_filename_stack_index] = node_filename;
227   node_filename_stack_index++;
228 }
229 
230 void
231 pop_node_filename ()
232 {
233   node_filename = node_filename_stack[--node_filename_stack_index];
234 }
235 
236 /* Save the state of the current input file. */
237 void
238 pushfile ()
239 {
240   FSTACK *newstack = xmalloc (sizeof (FSTACK));
241   newstack->filename = input_filename;
242   newstack->text = input_text;
243   newstack->size = input_text_length;
244   newstack->offset = input_text_offset;
245   newstack->line_number = line_number;
246   newstack->next = filestack;
247 
248   filestack = newstack;
249   push_node_filename ();
250 }
251 
252 /* Make the current file globals be what is on top of the file stack. */
253 void
254 popfile ()
255 {
256   FSTACK *tos = filestack;
257 
258   if (!tos)
259     abort ();                   /* My fault.  I wonder what I did? */
260 
261   if (macro_expansion_output_stream)
262     {
263       maybe_write_itext (input_text, input_text_offset);
264       forget_itext (input_text);
265     }
266 
267   /* Pop the stack. */
268   filestack = filestack->next;
269 
270   /* Make sure that commands with braces have been satisfied. */
271   if (!executing_string && !me_executing_string)
272     discard_braces ();
273 
274   /* Get the top of the stack into the globals. */
275   input_filename = tos->filename;
276   input_text = tos->text;
277   input_text_length = tos->size;
278   input_text_offset = tos->offset;
279   line_number = tos->line_number;
280   free (tos);
281 
282   /* Go back to the (now) current node. */
283   pop_node_filename ();
284 }
285 
286 /* Flush all open files on the file stack. */
287 void
288 flush_file_stack ()
289 {
290   while (filestack)
291     {
292       char *fname = input_filename;
293       char *text = input_text;
294       popfile ();
295       free (fname);
296       free (text);
297     }
298 }
299 
300 /* Return the index of the first character in the filename
301    which is past all the leading directory characters.  */
302 static int
303 skip_directory_part (filename)
304      char *filename;
305 {
306   int i = strlen (filename) - 1;
307 
308   while (i && !IS_SLASH (filename[i]))
309     i--;
310   if (IS_SLASH (filename[i]))
311     i++;
312   else if (filename[i] && HAVE_DRIVE (filename))
313     i = 2;
314 
315   return i;
316 }
317 
318 char *
319 filename_non_directory (name)
320      char *name;
321 {
322   return xstrdup (name + skip_directory_part (name));
323 }
324 
325 /* Return just the simple part of the filename; i.e. the
326    filename without the path information, or extensions.
327    This conses up a new string. */
328 char *
329 filename_part (filename)
330      char *filename;
331 {
332   char *basename = filename_non_directory (filename);
333 
334 #ifdef REMOVE_OUTPUT_EXTENSIONS
335   /* See if there is an extension to remove.  If so, remove it. */
336   {
337     char *temp;
338 
339     temp = strrchr (basename, '.');
340     if (temp)
341       *temp = 0;
342   }
343 #endif /* REMOVE_OUTPUT_EXTENSIONS */
344   return basename;
345 }
346 
347 /* Return the pathname part of filename.  This can be NULL. */
348 char *
349 pathname_part (filename)
350      char *filename;
351 {
352   char *expand_filename ();
353   char *result = NULL;
354   int i;
355 
356   filename = expand_filename (filename, "");
357 
358   i = skip_directory_part (filename);
359   if (i)
360     {
361       result = xmalloc (1 + i);
362       strncpy (result, filename, i);
363       result[i] = 0;
364     }
365   free (filename);
366   return result;
367 }
368 
369 /* Return the expansion of FILENAME. */
370 char *
371 expand_filename (filename, input_name)
372      char *filename, *input_name;
373 {
374   int i;
375   char *full_pathname ();
376 
377   if (filename)
378     {
379       filename = full_pathname (filename);
380       if (IS_ABSOLUTE (filename)
381 	  || (*filename == '.' &&
382 	      (IS_SLASH (filename[1]) ||
383 	       (filename[1] == '.' && IS_SLASH (filename[2])))))
384 	return filename;
385     }
386   else
387     {
388       filename = filename_non_directory (input_name);
389 
390       if (!*filename)
391         {
392           free (filename);
393           filename = xstrdup ("noname.texi");
394         }
395 
396       for (i = strlen (filename) - 1; i; i--)
397         if (filename[i] == '.')
398           break;
399 
400       if (!i)
401         i = strlen (filename);
402 
403       if (i + 6 > (strlen (filename)))
404         filename = xrealloc (filename, i + 6);
405       strcpy (filename + i, html ? ".html" : ".info");
406       return filename;
407     }
408 
409   if (IS_ABSOLUTE (input_name))
410     {
411       /* Make it so that relative names work. */
412       char *result;
413 
414       i = strlen (input_name) - 1;
415 
416       result = xmalloc (1 + strlen (input_name) + strlen (filename));
417       strcpy (result, input_name);
418 
419       while (!IS_SLASH (result[i]) && i)
420         i--;
421       if (IS_SLASH (result[i]))
422         i++;
423 
424       strcpy (&result[i], filename);
425       free (filename);
426       return result;
427     }
428   return filename;
429 }
430 
431 /* Return the full path to FILENAME. */
432 char *
433 full_pathname (filename)
434      char *filename;
435 {
436   int initial_character;
437   char *result;
438 
439   /* No filename given? */
440   if (!filename || !*filename)
441     return xstrdup ("");
442 
443   /* Already absolute? */
444   if (IS_ABSOLUTE (filename) ||
445       (*filename == '.' &&
446        (IS_SLASH (filename[1]) ||
447         (filename[1] == '.' && IS_SLASH (filename[2])))))
448     return xstrdup (filename);
449 
450   initial_character = *filename;
451   if (initial_character != '~')
452     {
453       char *localdir = xmalloc (1025);
454 #ifdef HAVE_GETCWD
455       if (!getcwd (localdir, 1024))
456 #else
457       if (!getwd (localdir))
458 #endif
459         {
460           fprintf (stderr, _("%s: getwd: %s, %s\n"),
461                    progname, filename, localdir);
462           xexit (1);
463         }
464 
465       strcat (localdir, "/");
466       strcat (localdir, filename);
467       result = xstrdup (localdir);
468       free (localdir);
469     }
470   else
471     { /* Does anybody know why WIN32 doesn't want to support $HOME?
472          If the reason is they don't have getpwnam, they should
473          only disable the else clause below.  */
474 #ifndef WIN32
475       if (IS_SLASH (filename[1]))
476         {
477           /* Return the concatenation of the environment variable HOME
478              and the rest of the string. */
479           char *temp_home;
480 
481           temp_home = (char *) getenv ("HOME");
482           result = xmalloc (strlen (&filename[1])
483                                     + 1
484                                     + temp_home ? strlen (temp_home)
485                                     : 0);
486           *result = 0;
487 
488           if (temp_home)
489             strcpy (result, temp_home);
490 
491           strcat (result, &filename[1]);
492         }
493       else
494         {
495           struct passwd *user_entry;
496           int i, c;
497           char *username = xmalloc (257);
498 
499           for (i = 1; (c = filename[i]); i++)
500             {
501               if (IS_SLASH (c))
502                 break;
503               else
504                 username[i - 1] = c;
505             }
506           if (c)
507             username[i - 1] = 0;
508 
509           user_entry = getpwnam (username);
510 
511           if (!user_entry)
512             return xstrdup (filename);
513 
514           result = xmalloc (1 + strlen (user_entry->pw_dir)
515                                     + strlen (&filename[i]));
516           strcpy (result, user_entry->pw_dir);
517           strcat (result, &filename[i]);
518         }
519 #endif /* not WIN32 */
520     }
521   return result;
522 }
523 
524 char *
525 output_name_from_input_name (name)
526      char *name;
527 {
528   return expand_filename (NULL, name);
529 }
530