source: trunk/third/texinfo/makeinfo/files.c @ 18945

Revision 18945, 14.0 KB checked in by amb, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18944, which included commits to RCS files with non-trunk default branches.
Line 
1/* files.c -- file-related functions for makeinfo.
2   $Id: files.c,v 1.1.1.2 2003-02-28 17:44:04 amb Exp $
3
4   Copyright (C) 1998, 1999, 2000, 2001, 2002 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
25FSTACK *filestack = NULL;
26
27static int node_filename_stack_index = 0;
28static int node_filename_stack_size = 0;
29static 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. */
37static char *
38extract_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. */
85static char *
86get_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. */
134char *
135find_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  int n, bytes_to_read;
143
144  result = fullpath = NULL;
145
146  fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
147
148  if (!fullpath)
149    goto error_exit;
150
151  filename = fullpath;
152  file_size = (long) fileinfo.st_size;
153
154  file = open (filename, O_RDONLY);
155  if (file < 0)
156    goto error_exit;
157
158  /* Load the file, with enough room for a newline and a null. */
159  result = xmalloc (file_size + 2);
160
161  /* VMS stat lies about the st_size value.  The actual number of
162     readable bytes is always less than this value.  The arcane
163     mysteries of VMS/RMS are too much to probe, so this hack
164    suffices to make things work.  It's also needed on Cygwin.  And so
165    we might as well use it everywhere.  */
166  bytes_to_read = file_size;
167  while ((n = read (file, result + count, bytes_to_read)) > 0)
168    {
169      count += n;
170      bytes_to_read -= n;
171    }
172  if (0 < count && count < file_size)
173    result = xrealloc (result, count + 2); /* why waste the slack? */
174  else if (n == -1)
175error_exit:
176    {
177      if (result)
178        free (result);
179
180      if (fullpath)
181        free (fullpath);
182
183      if (file != -1)
184        close (file);
185
186      return NULL;
187    }
188  close (file);
189
190  /* Set the globals to the new file. */
191  input_text = result;
192  input_text_length = count;
193  input_filename = fullpath;
194  node_filename = xstrdup (fullpath);
195  input_text_offset = 0;
196  line_number = 1;
197  /* Not strictly necessary.  This magic prevents read_token () from doing
198     extra unnecessary work each time it is called (that is a lot of times).
199     INPUT_TEXT_LENGTH is one past the actual end of the text. */
200  input_text[input_text_length] = '\n';
201  /* This, on the other hand, is always necessary.  */
202  input_text[input_text_length+1] = 0;
203  return result;
204}
205
206/* Pushing and popping files.  */
207void
208push_node_filename ()
209{
210  if (node_filename_stack_index + 1 > node_filename_stack_size)
211    node_filename_stack = xrealloc
212    (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
213
214  node_filename_stack[node_filename_stack_index] = node_filename;
215  node_filename_stack_index++;
216}
217
218void
219pop_node_filename ()
220{
221  node_filename = node_filename_stack[--node_filename_stack_index];
222}
223
224/* Save the state of the current input file. */
225void
226pushfile ()
227{
228  FSTACK *newstack = xmalloc (sizeof (FSTACK));
229  newstack->filename = input_filename;
230  newstack->text = input_text;
231  newstack->size = input_text_length;
232  newstack->offset = input_text_offset;
233  newstack->line_number = line_number;
234  newstack->next = filestack;
235
236  filestack = newstack;
237  push_node_filename ();
238}
239
240/* Make the current file globals be what is on top of the file stack. */
241void
242popfile ()
243{
244  FSTACK *tos = filestack;
245
246  if (!tos)
247    abort ();                   /* My fault.  I wonder what I did? */
248
249  if (macro_expansion_output_stream)
250    {
251      maybe_write_itext (input_text, input_text_offset);
252      forget_itext (input_text);
253    }
254
255  /* Pop the stack. */
256  filestack = filestack->next;
257
258  /* Make sure that commands with braces have been satisfied. */
259  if (!executing_string && !me_executing_string)
260    discard_braces ();
261
262  /* Get the top of the stack into the globals. */
263  input_filename = tos->filename;
264  input_text = tos->text;
265  input_text_length = tos->size;
266  input_text_offset = tos->offset;
267  line_number = tos->line_number;
268  free (tos);
269
270  /* Go back to the (now) current node. */
271  pop_node_filename ();
272}
273
274/* Flush all open files on the file stack. */
275void
276flush_file_stack ()
277{
278  while (filestack)
279    {
280      char *fname = input_filename;
281      char *text = input_text;
282      popfile ();
283      free (fname);
284      free (text);
285    }
286}
287
288/* Return the index of the first character in the filename
289   which is past all the leading directory characters.  */
290static int
291skip_directory_part (filename)
292     char *filename;
293{
294  int i = strlen (filename) - 1;
295
296  while (i && !IS_SLASH (filename[i]))
297    i--;
298  if (IS_SLASH (filename[i]))
299    i++;
300  else if (filename[i] && HAVE_DRIVE (filename))
301    i = 2;
302
303  return i;
304}
305
306char *
307filename_non_directory (name)
308     char *name;
309{
310  return xstrdup (name + skip_directory_part (name));
311}
312
313/* Return just the simple part of the filename; i.e. the
314   filename without the path information, or extensions.
315   This conses up a new string. */
316char *
317filename_part (filename)
318     char *filename;
319{
320  char *basename = filename_non_directory (filename);
321
322#ifdef REMOVE_OUTPUT_EXTENSIONS
323  /* See if there is an extension to remove.  If so, remove it. */
324  {
325    char *temp;
326
327    temp = strrchr (basename, '.');
328    if (temp)
329      *temp = 0;
330  }
331#endif /* REMOVE_OUTPUT_EXTENSIONS */
332  return basename;
333}
334
335/* Return the pathname part of filename.  This can be NULL. */
336char *
337pathname_part (filename)
338     char *filename;
339{
340  char *expand_filename ();
341  char *result = NULL;
342  int i;
343
344  filename = expand_filename (filename, "");
345
346  i = skip_directory_part (filename);
347  if (i)
348    {
349      result = xmalloc (1 + i);
350      strncpy (result, filename, i);
351      result[i] = 0;
352    }
353  free (filename);
354  return result;
355}
356
357/* Return the expansion of FILENAME. */
358char *
359expand_filename (filename, input_name)
360     char *filename, *input_name;
361{
362  int i;
363  char *full_pathname ();
364
365  if (filename)
366    {
367      filename = full_pathname (filename);
368      if (IS_ABSOLUTE (filename)
369          || (*filename == '.' &&
370              (IS_SLASH (filename[1]) ||
371               (filename[1] == '.' && IS_SLASH (filename[2])))))
372        return filename;
373    }
374  else
375    {
376      filename = filename_non_directory (input_name);
377
378      if (!*filename)
379        {
380          free (filename);
381          filename = xstrdup ("noname.texi");
382        }
383
384      for (i = strlen (filename) - 1; i; i--)
385        if (filename[i] == '.')
386          break;
387
388      if (!i)
389        i = strlen (filename);
390
391      if (i + 6 > (strlen (filename)))
392        filename = xrealloc (filename, i + 6);
393      strcpy (filename + i, html ? ".html" : ".info");
394      return filename;
395    }
396
397  if (IS_ABSOLUTE (input_name))
398    {
399      /* Make it so that relative names work. */
400      char *result;
401     
402      i = strlen (input_name) - 1;
403
404      result = xmalloc (1 + strlen (input_name) + strlen (filename));
405      strcpy (result, input_name);
406
407      while (!IS_SLASH (result[i]) && i)
408        i--;
409      if (IS_SLASH (result[i]))
410        i++;
411
412      strcpy (&result[i], filename);
413      free (filename);
414      return result;
415    }
416  return filename;
417}
418
419/* Return the full path to FILENAME. */
420char *
421full_pathname (filename)
422     char *filename;
423{
424  int initial_character;
425  char *result;
426
427  /* No filename given? */
428  if (!filename || !*filename)
429    return xstrdup ("");
430 
431  /* Already absolute? */
432  if (IS_ABSOLUTE (filename) ||
433      (*filename == '.' &&
434       (IS_SLASH (filename[1]) ||
435        (filename[1] == '.' && IS_SLASH (filename[2])))))
436    return xstrdup (filename);
437
438  initial_character = *filename;
439  if (initial_character != '~')
440    {
441      char *localdir = xmalloc (1025);
442#ifdef HAVE_GETCWD
443      if (!getcwd (localdir, 1024))
444#else
445      if (!getwd (localdir))
446#endif
447        {
448          fprintf (stderr, _("%s: getwd: %s, %s\n"),
449                   progname, filename, localdir);
450          xexit (1);
451        }
452
453      strcat (localdir, "/");
454      strcat (localdir, filename);
455      result = xstrdup (localdir);
456      free (localdir);
457    }
458  else
459    { /* Does anybody know why WIN32 doesn't want to support $HOME?
460         If the reason is they don't have getpwnam, they should
461         only disable the else clause below.  */
462#ifndef WIN32
463      if (IS_SLASH (filename[1]))
464        {
465          /* Return the concatenation of the environment variable HOME
466             and the rest of the string. */
467          char *temp_home;
468
469          temp_home = (char *) getenv ("HOME");
470          result = xmalloc (strlen (&filename[1])
471                                    + 1
472                                    + temp_home ? strlen (temp_home)
473                                    : 0);
474          *result = 0;
475
476          if (temp_home)
477            strcpy (result, temp_home);
478
479          strcat (result, &filename[1]);
480        }
481      else
482        {
483          struct passwd *user_entry;
484          int i, c;
485          char *username = xmalloc (257);
486
487          for (i = 1; (c = filename[i]); i++)
488            {
489              if (IS_SLASH (c))
490                break;
491              else
492                username[i - 1] = c;
493            }
494          if (c)
495            username[i - 1] = 0;
496
497          user_entry = getpwnam (username);
498
499          if (!user_entry)
500            return xstrdup (filename);
501
502          result = xmalloc (1 + strlen (user_entry->pw_dir)
503                                    + strlen (&filename[i]));
504          strcpy (result, user_entry->pw_dir);
505          strcat (result, &filename[i]);
506        }
507#endif /* not WIN32 */
508    }
509  return result;
510}
511
512char *
513output_name_from_input_name (name)
514     char *name;
515{
516  return expand_filename (NULL, name);
517}
518
519
520/* Modify the file name FNAME so that it fits the limitations of the
521   underlying filesystem.  In particular, truncate the file name as it
522   would be truncated by the filesystem.  We assume the result can
523   never be longer than the original, otherwise we couldn't be sure we
524   have enough space in the original string to modify it in place.  */
525char *
526normalize_filename (fname)
527     char *fname;
528{
529  int maxlen;
530  char orig[PATH_MAX + 1];
531  int i;
532  char *lastdot, *p;
533
534#ifdef _PC_NAME_MAX
535  maxlen = pathconf (fname, _PC_NAME_MAX);
536  if (maxlen < 1)
537#endif
538    maxlen = PATH_MAX;
539
540  i = skip_directory_part (fname);
541  if (fname[i] == '\0')
542    return fname;       /* only a directory name -- don't modify */
543  strcpy (orig, fname + i);
544
545  switch (maxlen)
546    {
547      case 12:  /* MS-DOS 8+3 filesystem */
548        if (orig[0] == '.')     /* leading dots are not allowed */
549          orig[0] = '_';
550        lastdot = strrchr (orig, '.');
551        if (!lastdot)
552          lastdot = orig + strlen (orig);
553        strncpy (fname + i, orig, lastdot - orig);
554        for (p = fname + i;
555             p < fname + i + (lastdot - orig) && p < fname + i + 8;
556             p++)
557          if (*p == '.')
558            *p = '_';
559        *p = '\0';
560        if (*lastdot == '.')
561          strncat (fname + i, lastdot, 4);
562        break;
563      case 14:  /* old Unix systems with 14-char limitation */
564        strcpy (fname + i, orig);
565        if (strlen (fname + i) > 14)
566          fname[i + 14] = '\0';
567        break;
568      default:
569        strcpy (fname + i, orig);
570        if (strlen (fname) > maxlen - 1)
571          fname[maxlen - 1] = '\0';
572        break;
573    }
574
575  return fname;
576}
Note: See TracBrowser for help on using the repository browser.