1 /* files.c -- file-related functions for makeinfo.
2 $Id: files.c,v 1.10 2002/01/16 15:52:45 karl Exp $
4 Copyright (C) 1998, 99, 2000, 01, 02 Free Software Foundation, Inc.
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)
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.
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. */
25 FSTACK *filestack = NULL;
27 static int node_filename_stack_index = 0;
28 static int node_filename_stack_size = 0;
29 static char **node_filename_stack = NULL;
32 /* Looking for include files. */
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. */
38 extract_colon_unit (string, index)
43 int path_sep_char = PATH_SEP[0];
46 if (!string || (i >= strlen (string)))
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)
58 while (string[i] && string[i] != path_sep_char) i++;
66 /* Return "" in the case of a trailing `:'. */
73 value = xmalloc (1 + (i - start));
74 memcpy (value, &string[start], (i - start));
75 value [i - start] = 0;
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. */
86 get_file_info_in_path (filename, path, finfo)
87 char *filename, *path;
91 int result, index = 0;
96 /* Handle absolute pathnames. */
97 if (IS_ABSOLUTE (filename)
99 && (IS_SLASH (filename[1])
100 || (filename[1] == '.' && IS_SLASH (filename[2])))))
102 if (stat (filename, finfo) == 0)
103 return xstrdup (filename);
108 while ((dir = extract_colon_unit (path, &index)))
118 fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
119 sprintf (fullpath, "%s/%s", dir, filename);
122 result = stat (fullpath, finfo);
132 /* Find and load the file named FILENAME. Return a pointer to
133 the loaded file, or NULL if it can't be loaded. */
135 find_and_load (filename)
138 struct stat fileinfo;
140 int file = -1, count = 0;
141 char *fullpath, *result;
142 int n, bytes_to_read;
144 result = fullpath = NULL;
146 fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
152 file_size = (long) fileinfo.st_size;
154 file = open (filename, O_RDONLY);
158 /* Load the file, with enough room for a newline and a null. */
159 result = xmalloc (file_size + 2);
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)
172 if (0 < count && count < file_size)
173 result = xrealloc (result, count + 2); /* why waste the slack? */
190 /* Set the globals to the new file. */
192 input_text_length = count;
193 input_filename = fullpath;
194 node_filename = xstrdup (fullpath);
195 input_text_offset = 0;
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;
206 /* Pushing and popping files. */
208 push_node_filename ()
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 *));
214 node_filename_stack[node_filename_stack_index] = node_filename;
215 node_filename_stack_index++;
221 node_filename = node_filename_stack[--node_filename_stack_index];
224 /* Save the state of the current input file. */
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;
236 filestack = newstack;
237 push_node_filename ();
240 /* Make the current file globals be what is on top of the file stack. */
244 FSTACK *tos = filestack;
247 abort (); /* My fault. I wonder what I did? */
249 if (macro_expansion_output_stream)
251 maybe_write_itext (input_text, input_text_offset);
252 forget_itext (input_text);
256 filestack = filestack->next;
258 /* Make sure that commands with braces have been satisfied. */
259 if (!executing_string && !me_executing_string)
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;
270 /* Go back to the (now) current node. */
271 pop_node_filename ();
274 /* Flush all open files on the file stack. */
280 char *fname = input_filename;
281 char *text = input_text;
288 /* Return the index of the first character in the filename
289 which is past all the leading directory characters. */
291 skip_directory_part (filename)
294 int i = strlen (filename) - 1;
296 while (i && !IS_SLASH (filename[i]))
298 if (IS_SLASH (filename[i]))
300 else if (filename[i] && HAVE_DRIVE (filename))
307 filename_non_directory (name)
310 return xstrdup (name + skip_directory_part (name));
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. */
317 filename_part (filename)
320 char *basename = filename_non_directory (filename);
322 #ifdef REMOVE_OUTPUT_EXTENSIONS
323 /* See if there is an extension to remove. If so, remove it. */
327 temp = strrchr (basename, '.');
331 #endif /* REMOVE_OUTPUT_EXTENSIONS */
335 /* Return the pathname part of filename. This can be NULL. */
337 pathname_part (filename)
340 char *expand_filename ();
344 filename = expand_filename (filename, "");
346 i = skip_directory_part (filename);
349 result = xmalloc (1 + i);
350 strncpy (result, filename, i);
357 /* Return the expansion of FILENAME. */
359 expand_filename (filename, input_name)
360 char *filename, *input_name;
363 char *full_pathname ();
367 filename = full_pathname (filename);
368 if (IS_ABSOLUTE (filename)
369 || (*filename == '.' &&
370 (IS_SLASH (filename[1]) ||
371 (filename[1] == '.' && IS_SLASH (filename[2])))))
376 filename = filename_non_directory (input_name);
381 filename = xstrdup ("noname.texi");
384 for (i = strlen (filename) - 1; i; i--)
385 if (filename[i] == '.')
389 i = strlen (filename);
391 if (i + 6 > (strlen (filename)))
392 filename = xrealloc (filename, i + 6);
393 strcpy (filename + i, html ? ".html" : ".info");
397 if (IS_ABSOLUTE (input_name))
399 /* Make it so that relative names work. */
402 i = strlen (input_name) - 1;
404 result = xmalloc (1 + strlen (input_name) + strlen (filename));
405 strcpy (result, input_name);
407 while (!IS_SLASH (result[i]) && i)
409 if (IS_SLASH (result[i]))
412 strcpy (&result[i], filename);
419 /* Return the full path to FILENAME. */
421 full_pathname (filename)
424 int initial_character;
427 /* No filename given? */
428 if (!filename || !*filename)
431 /* Already absolute? */
432 if (IS_ABSOLUTE (filename) ||
434 (IS_SLASH (filename[1]) ||
435 (filename[1] == '.' && IS_SLASH (filename[2])))))
436 return xstrdup (filename);
438 initial_character = *filename;
439 if (initial_character != '~')
441 char *localdir = xmalloc (1025);
443 if (!getcwd (localdir, 1024))
445 if (!getwd (localdir))
448 fprintf (stderr, _("%s: getwd: %s, %s\n"),
449 progname, filename, localdir);
453 strcat (localdir, "/");
454 strcat (localdir, filename);
455 result = xstrdup (localdir);
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. */
463 if (IS_SLASH (filename[1]))
465 /* Return the concatenation of the environment variable HOME
466 and the rest of the string. */
469 temp_home = (char *) getenv ("HOME");
470 result = xmalloc (strlen (&filename[1])
472 + temp_home ? strlen (temp_home)
477 strcpy (result, temp_home);
479 strcat (result, &filename[1]);
483 struct passwd *user_entry;
485 char *username = xmalloc (257);
487 for (i = 1; (c = filename[i]); i++)
497 user_entry = getpwnam (username);
500 return xstrdup (filename);
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]);
507 #endif /* not WIN32 */
513 output_name_from_input_name (name)
516 return expand_filename (NULL, name);
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. */
526 normalize_filename (fname)
530 char orig[PATH_MAX + 1];
535 maxlen = pathconf (fname, _PC_NAME_MAX);
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);
547 case 12: /* MS-DOS 8+3 filesystem */
548 if (orig[0] == '.') /* leading dots are not allowed */
550 lastdot = strrchr (orig, '.');
552 lastdot = orig + strlen (orig);
553 strncpy (fname + i, orig, lastdot - orig);
555 p < fname + i + (lastdot - orig) && p < fname + i + 8;
561 strncat (fname + i, lastdot, 4);
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';
569 strcpy (fname + i, orig);
570 if (strlen (fname) > maxlen - 1)
571 fname[maxlen - 1] = '\0';