1 /* files.c -- file-related functions for makeinfo.
2 $Id: files.c,v 1.8 2007/07/01 21:20:32 karl Exp $
4 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007
5 Free Software Foundation, Inc.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
28 FSTACK *filestack = NULL;
30 static int node_filename_stack_index = 0;
31 static int node_filename_stack_size = 0;
32 static char **node_filename_stack = NULL;
34 /* Looking for include files. */
36 /* Given a string containing units of information separated by colons,
37 return the next one pointed to by INDEX, or NULL if there are no more.
38 Advance INDEX to the character after the colon. */
40 extract_colon_unit (char *string, int *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 (char *filename, char *path, struct stat *finfo)
89 int result, index = 0;
94 /* Handle absolute pathnames. */
95 if (IS_ABSOLUTE (filename)
97 && (IS_SLASH (filename[1])
98 || (filename[1] == '.' && IS_SLASH (filename[2])))))
100 if (stat (filename, finfo) == 0)
101 return xstrdup (filename);
106 while ((dir = extract_colon_unit (path, &index)))
116 fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
117 sprintf (fullpath, "%s/%s", dir, filename);
120 result = stat (fullpath, finfo);
130 /* Prepend and append new paths to include_files_path. */
132 prepend_to_include_path (char *path)
134 if (!include_files_path)
136 include_files_path = xstrdup (path);
137 include_files_path = xrealloc (include_files_path,
138 strlen (include_files_path) + 3); /* 3 for ":.\0" */
139 strcat (strcat (include_files_path, PATH_SEP), ".");
143 char *tmp = xstrdup (include_files_path);
144 include_files_path = xrealloc (include_files_path,
145 strlen (include_files_path) + strlen (path) + 2); /* 2 for ":\0" */
146 strcpy (include_files_path, path);
147 strcat (include_files_path, PATH_SEP);
148 strcat (include_files_path, tmp);
154 append_to_include_path (char *path)
156 if (!include_files_path)
157 include_files_path = xstrdup (".");
159 include_files_path = (char *) xrealloc (include_files_path,
160 2 + strlen (include_files_path) + strlen (path));
161 strcat (include_files_path, PATH_SEP);
162 strcat (include_files_path, path);
165 /* Remove the first path from the include_files_path. */
167 pop_path_from_include_path (void)
172 if (include_files_path)
173 for (i = 0; i < strlen (include_files_path)
174 && include_files_path[i] != ':'; i++);
176 /* Advance include_files_path to the next char from ':' */
177 tmp = (char *) xmalloc (strlen (include_files_path) - i);
178 strcpy (tmp, (char *) include_files_path + i + 1);
180 free (include_files_path);
181 include_files_path = tmp;
184 /* Find and load the file named FILENAME. Return a pointer to
185 the loaded file, or NULL if it can't be loaded. If USE_PATH is zero,
186 just look for the given file (this is used in handle_delayed_writes),
187 else search along include_files_path. */
190 find_and_load (char *filename, int use_path)
192 struct stat fileinfo;
194 int file = -1, count = 0;
195 char *fullpath, *result;
196 int n, bytes_to_read;
198 result = fullpath = NULL;
201 = get_file_info_in_path (filename, use_path ? include_files_path : NULL,
208 file_size = (long) fileinfo.st_size;
210 file = open (filename, O_RDONLY);
214 /* Load the file, with enough room for a newline and a null. */
215 result = xmalloc (file_size + 2);
217 /* VMS stat lies about the st_size value. The actual number of
218 readable bytes is always less than this value. The arcane
219 mysteries of VMS/RMS are too much to probe, so this hack
220 suffices to make things work. It's also needed on Cygwin. And so
221 we might as well use it everywhere. */
222 bytes_to_read = file_size;
223 while ((n = read (file, result + count, bytes_to_read)) > 0)
228 if (0 < count && count < file_size)
229 result = xrealloc (result, count + 2); /* why waste the slack? */
246 /* Set the globals to the new file. */
248 input_text_length = count;
249 input_filename = fullpath;
250 node_filename = xstrdup (fullpath);
251 input_text_offset = 0;
253 /* Not strictly necessary. This magic prevents read_token () from doing
254 extra unnecessary work each time it is called (that is a lot of times).
255 INPUT_TEXT_LENGTH is one past the actual end of the text. */
256 input_text[input_text_length] = '\n';
257 /* This, on the other hand, is always necessary. */
258 input_text[input_text_length+1] = 0;
262 /* Pushing and popping files. */
264 push_node_filename (void)
266 if (node_filename_stack_index + 1 > node_filename_stack_size)
267 node_filename_stack = xrealloc
268 (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
270 node_filename_stack[node_filename_stack_index] = node_filename;
271 node_filename_stack_index++;
275 pop_node_filename (void)
277 node_filename = node_filename_stack[--node_filename_stack_index];
280 /* Save the state of the current input file. */
284 FSTACK *newstack = xmalloc (sizeof (FSTACK));
285 newstack->filename = input_filename;
286 newstack->text = input_text;
287 newstack->size = input_text_length;
288 newstack->offset = input_text_offset;
289 newstack->line_number = line_number;
290 newstack->next = filestack;
292 filestack = newstack;
293 push_node_filename ();
296 /* Make the current file globals be what is on top of the file stack. */
300 FSTACK *tos = filestack;
303 abort (); /* My fault. I wonder what I did? */
305 if (macro_expansion_output_stream)
307 maybe_write_itext (input_text, input_text_offset);
308 forget_itext (input_text);
312 filestack = filestack->next;
314 /* Make sure that commands with braces have been satisfied. */
315 if (!executing_string && !me_executing_string)
318 /* Get the top of the stack into the globals. */
319 input_filename = tos->filename;
320 input_text = tos->text;
321 input_text_length = tos->size;
322 input_text_offset = tos->offset;
323 line_number = tos->line_number;
326 /* Go back to the (now) current node. */
327 pop_node_filename ();
330 /* Flush all open files on the file stack. */
332 flush_file_stack (void)
336 char *fname = input_filename;
337 char *text = input_text;
344 /* Return the index of the first character in the filename
345 which is past all the leading directory characters. */
347 skip_directory_part (char *filename)
349 int i = strlen (filename) - 1;
351 while (i && !IS_SLASH (filename[i]))
353 if (IS_SLASH (filename[i]))
355 else if (filename[i] && HAVE_DRIVE (filename))
362 filename_non_directory (char *name)
364 return xstrdup (name + skip_directory_part (name));
367 /* Return just the simple part of the filename; i.e. the
368 filename without the path information, or extensions.
369 This conses up a new string. */
371 filename_part (char *filename)
373 char *basename = filename_non_directory (filename);
375 #ifdef REMOVE_OUTPUT_EXTENSIONS
376 /* See if there is an extension to remove. If so, remove it. */
378 char *temp = strrchr (basename, '.');
382 #endif /* REMOVE_OUTPUT_EXTENSIONS */
386 /* Return the pathname part of filename. This can be NULL. */
388 pathname_part (char *filename)
393 filename = expand_filename (filename, "");
395 i = skip_directory_part (filename);
398 result = xmalloc (1 + i);
399 strncpy (result, filename, i);
406 /* Return the full path to FILENAME. */
408 full_pathname (char *filename)
410 int initial_character;
413 /* No filename given? */
414 if (!filename || !*filename)
417 /* Already absolute? */
418 if (IS_ABSOLUTE (filename) ||
420 (IS_SLASH (filename[1]) ||
421 (filename[1] == '.' && IS_SLASH (filename[2])))))
422 return xstrdup (filename);
424 initial_character = *filename;
425 if (initial_character != '~')
427 char *localdir = xmalloc (1025);
429 if (!getcwd (localdir, 1024))
431 if (!getwd (localdir))
434 fprintf (stderr, _("%s: getwd: %s, %s\n"),
435 progname, filename, localdir);
439 strcat (localdir, "/");
440 strcat (localdir, filename);
441 result = xstrdup (localdir);
445 { /* Does anybody know why WIN32 doesn't want to support $HOME?
446 If the reason is they don't have getpwnam, they should
447 only disable the else clause below. */
449 if (IS_SLASH (filename[1]))
451 /* Return the concatenation of the environment variable HOME
452 and the rest of the string. */
455 temp_home = (char *) getenv ("HOME");
456 result = xmalloc (strlen (&filename[1])
458 + temp_home ? strlen (temp_home)
463 strcpy (result, temp_home);
465 strcat (result, &filename[1]);
469 struct passwd *user_entry;
471 char *username = xmalloc (257);
473 for (i = 1; (c = filename[i]); i++)
483 user_entry = getpwnam (username);
486 return xstrdup (filename);
488 result = xmalloc (1 + strlen (user_entry->pw_dir)
489 + strlen (&filename[i]));
490 strcpy (result, user_entry->pw_dir);
491 strcat (result, &filename[i]);
493 #endif /* not WIN32 */
498 /* Return the expansion of FILENAME. */
500 expand_filename (char *filename, char *input_name)
506 filename = full_pathname (filename);
507 if (IS_ABSOLUTE (filename)
508 || (*filename == '.' &&
509 (IS_SLASH (filename[1]) ||
510 (filename[1] == '.' && IS_SLASH (filename[2])))))
515 filename = filename_non_directory (input_name);
520 filename = xstrdup ("noname.texi");
523 for (i = strlen (filename) - 1; i; i--)
524 if (filename[i] == '.')
528 i = strlen (filename);
530 if (i + 6 > (strlen (filename)))
531 filename = xrealloc (filename, i + 6);
532 strcpy (filename + i, html ? ".html" : ".info");
536 if (IS_ABSOLUTE (input_name))
538 /* Make it so that relative names work. */
541 i = strlen (input_name) - 1;
543 result = xmalloc (1 + strlen (input_name) + strlen (filename));
544 strcpy (result, input_name);
546 while (!IS_SLASH (result[i]) && i)
548 if (IS_SLASH (result[i]))
551 strcpy (&result[i], filename);
559 output_name_from_input_name (char *name)
561 return expand_filename (NULL, name);
565 /* Modify the file name FNAME so that it fits the limitations of the
566 underlying filesystem. In particular, truncate the file name as it
567 would be truncated by the filesystem. We assume the result can
568 never be longer than the original, otherwise we couldn't be sure we
569 have enough space in the original string to modify it in place. */
571 normalize_filename (char *fname)
574 char orig[PATH_MAX + 1];
579 maxlen = pathconf (fname, _PC_NAME_MAX);
584 i = skip_directory_part (fname);
585 if (fname[i] == '\0')
586 return fname; /* only a directory name -- don't modify */
587 strcpy (orig, fname + i);
591 case 12: /* MS-DOS 8+3 filesystem */
592 if (orig[0] == '.') /* leading dots are not allowed */
594 lastdot = strrchr (orig, '.');
596 lastdot = orig + strlen (orig);
597 strncpy (fname + i, orig, lastdot - orig);
599 p < fname + i + (lastdot - orig) && p < fname + i + 8;
605 strncat (fname + i, lastdot, 4);
607 case 14: /* old Unix systems with 14-char limitation */
608 strcpy (fname + i, orig);
609 if (strlen (fname + i) > 14)
610 fname[i + 14] = '\0';
613 strcpy (fname + i, orig);
614 if (strlen (fname) > maxlen - 1)
615 fname[maxlen - 1] = '\0';
622 /* Delayed writing functions. A few of the commands
623 needs to be handled at the end, namely @contents,
624 @shortcontents, @printindex and @listoffloats.
625 These functions take care of that. */
626 static DELAYED_WRITE *delayed_writes = NULL;
627 int handling_delayed_writes = 0;
630 register_delayed_write (char *delayed_command)
634 if (!current_output_filename || !*current_output_filename)
636 /* Cannot register if we don't know what the output file is. */
637 warning (_("`%s' omitted before output filename"), delayed_command);
641 if (STREQ (current_output_filename, "-"))
643 /* Do not register a new write if the output file is not seekable.
644 Let the user know about it first, though. */
645 warning (_("`%s' omitted since writing to stdout"), delayed_command);
649 /* Don't complain if the user is writing /dev/null, since surely they
650 don't care, but don't register the delayed write, either. */
651 if (FILENAME_CMP (current_output_filename, NULL_DEVICE) == 0
652 || FILENAME_CMP (current_output_filename, ALSO_NULL_DEVICE) == 0)
655 /* We need the HTML header in the output,
656 to get a proper output_position. */
657 if (!executing_string && html)
659 /* Get output_position updated. */
662 new = xmalloc (sizeof (DELAYED_WRITE));
663 new->command = xstrdup (delayed_command);
664 new->filename = xstrdup (current_output_filename);
665 new->input_filename = xstrdup (input_filename);
666 new->position = output_position;
667 new->calling_line = line_number;
668 new->node = current_node ? xstrdup (current_node): "";
670 new->node_order = node_order;
671 new->index_order = index_counter;
673 new->next = delayed_writes;
674 delayed_writes = new;
678 handle_delayed_writes (void)
680 DELAYED_WRITE *temp = (DELAYED_WRITE *) reverse_list
681 ((GENERIC_LIST *) delayed_writes);
682 int position_shift_amount, line_number_shift_amount;
685 handling_delayed_writes = 1;
689 delayed_buf = find_and_load (temp->filename, 0);
691 if (output_paragraph_offset > 0)
693 error (_("Output buffer not empty."));
699 fs_error (temp->filename);
703 output_stream = fopen (temp->filename, "w");
706 fs_error (temp->filename);
710 if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position)
712 fs_error (temp->filename);
717 int output_position_at_start = output_position;
718 int line_number_at_start = output_line_number;
720 /* In order to make warnings and errors
721 refer to the correct line number. */
722 input_filename = temp->input_filename;
723 line_number = temp->calling_line;
725 execute_string ("%s", temp->command);
728 /* Since the output file is modified, following delayed writes
729 need to be updated by this amount. */
730 position_shift_amount = output_position - output_position_at_start;
731 line_number_shift_amount = output_line_number - line_number_at_start;
734 if (fwrite (delayed_buf + temp->position, 1,
735 input_text_length - temp->position, output_stream)
736 != input_text_length - temp->position
737 || fclose (output_stream) != 0)
738 fs_error (temp->filename);
740 /* Done with the buffer. */
743 /* Update positions in tag table for nodes that are defined after
744 the line this delayed write is registered. */
748 for (node = tag_table; node; node = node->next_ent)
749 if (node->order > temp->node_order)
750 node->position += position_shift_amount;
753 /* Something similar for the line numbers in all of the defined
757 for (i = 0; i < defined_indices; i++)
758 if (name_index_alist[i])
760 char *name = ((INDEX_ALIST *) name_index_alist[i])->name;
762 for (index = index_list (name); index; index = index->next)
763 if ((no_headers || STREQ (index->node, temp->node))
764 && index->entry_number > temp->index_order)
765 index->output_line += line_number_shift_amount;
769 /* Shift remaining delayed positions
770 by the length of this write. */
772 DELAYED_WRITE *future_write = temp->next;
775 if (STREQ (temp->filename, future_write->filename))
776 future_write->position += position_shift_amount;
777 future_write = future_write->next;