1 /* files.c -- file-related functions for makeinfo.
2 $Id: files.c,v 1.5 2004/07/27 00:06:31 karl Exp $
4 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
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 2, or (at your option)
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, write to the Free Software Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
29 FSTACK
*filestack
= NULL
;
31 static int node_filename_stack_index
= 0;
32 static int node_filename_stack_size
= 0;
33 static char **node_filename_stack
= NULL
;
35 /* Looking for include files. */
37 /* Given a string containing units of information separated by colons,
38 return the next one pointed to by INDEX, or NULL if there are no more.
39 Advance INDEX to the character after the colon. */
41 extract_colon_unit (char *string
, int *index
)
44 int path_sep_char
= PATH_SEP
[0];
47 if (!string
|| (i
>= strlen (string
)))
50 /* Each call to this routine leaves the index pointing at a colon if
51 there is more to the path. If i > 0, then increment past the
52 `:'. If i == 0, then the path has a leading colon. Trailing colons
53 are handled OK by the `else' part of the if statement; an empty
54 string is returned in that case. */
55 if (i
&& string
[i
] == path_sep_char
)
59 while (string
[i
] && string
[i
] != path_sep_char
) i
++;
67 /* Return "" in the case of a trailing `:'. */
74 value
= xmalloc (1 + (i
- start
));
75 memcpy (value
, &string
[start
], (i
- start
));
76 value
[i
- start
] = 0;
82 /* Return the full pathname for FILENAME by searching along PATH.
83 When found, return the stat () info for FILENAME in FINFO.
84 If PATH is NULL, only the current directory is searched.
85 If the file could not be found, return a NULL pointer. */
87 get_file_info_in_path (char *filename
, char *path
, struct stat
*finfo
)
90 int result
, index
= 0;
95 /* Handle absolute pathnames. */
96 if (IS_ABSOLUTE (filename
)
98 && (IS_SLASH (filename
[1])
99 || (filename
[1] == '.' && IS_SLASH (filename
[2])))))
101 if (stat (filename
, finfo
) == 0)
102 return xstrdup (filename
);
107 while ((dir
= extract_colon_unit (path
, &index
)))
117 fullpath
= xmalloc (2 + strlen (dir
) + strlen (filename
));
118 sprintf (fullpath
, "%s/%s", dir
, filename
);
121 result
= stat (fullpath
, finfo
);
131 /* Prepend and append new paths to include_files_path. */
133 prepend_to_include_path (char *path
)
135 if (!include_files_path
)
137 include_files_path
= xstrdup (path
);
138 include_files_path
= xrealloc (include_files_path
,
139 strlen (include_files_path
) + 3); /* 3 for ":.\0" */
140 strcat (strcat (include_files_path
, PATH_SEP
), ".");
144 char *tmp
= xstrdup (include_files_path
);
145 include_files_path
= xrealloc (include_files_path
,
146 strlen (include_files_path
) + strlen (path
) + 2); /* 2 for ":\0" */
147 strcpy (include_files_path
, path
);
148 strcat (include_files_path
, PATH_SEP
);
149 strcat (include_files_path
, tmp
);
155 append_to_include_path (char *path
)
157 if (!include_files_path
)
158 include_files_path
= xstrdup (".");
160 include_files_path
= (char *) xrealloc (include_files_path
,
161 2 + strlen (include_files_path
) + strlen (path
));
162 strcat (include_files_path
, PATH_SEP
);
163 strcat (include_files_path
, path
);
166 /* Remove the first path from the include_files_path. */
168 pop_path_from_include_path (void)
173 if (include_files_path
)
174 for (i
= 0; i
< strlen (include_files_path
)
175 && include_files_path
[i
] != ':'; i
++);
177 /* Advance include_files_path to the next char from ':' */
178 tmp
= (char *) xmalloc (strlen (include_files_path
) - i
);
179 strcpy (tmp
, (char *) include_files_path
+ i
+ 1);
181 free (include_files_path
);
182 include_files_path
= tmp
;
185 /* Find and load the file named FILENAME. Return a pointer to
186 the loaded file, or NULL if it can't be loaded. If USE_PATH is zero,
187 just look for the given file (this is used in handle_delayed_writes),
188 else search along include_files_path. */
191 find_and_load (char *filename
, int use_path
)
193 struct stat fileinfo
;
195 int file
= -1, count
= 0;
196 char *fullpath
, *result
;
197 int n
, bytes_to_read
;
199 result
= fullpath
= NULL
;
202 = get_file_info_in_path (filename
, use_path
? include_files_path
: NULL
,
209 file_size
= (long) fileinfo
.st_size
;
211 file
= open (filename
, O_RDONLY
);
215 /* Load the file, with enough room for a newline and a null. */
216 result
= xmalloc (file_size
+ 2);
218 /* VMS stat lies about the st_size value. The actual number of
219 readable bytes is always less than this value. The arcane
220 mysteries of VMS/RMS are too much to probe, so this hack
221 suffices to make things work. It's also needed on Cygwin. And so
222 we might as well use it everywhere. */
223 bytes_to_read
= file_size
;
224 while ((n
= read (file
, result
+ count
, bytes_to_read
)) > 0)
229 if (0 < count
&& count
< file_size
)
230 result
= xrealloc (result
, count
+ 2); /* why waste the slack? */
247 /* Set the globals to the new file. */
249 input_text_length
= count
;
250 input_filename
= fullpath
;
251 node_filename
= xstrdup (fullpath
);
252 input_text_offset
= 0;
254 /* Not strictly necessary. This magic prevents read_token () from doing
255 extra unnecessary work each time it is called (that is a lot of times).
256 INPUT_TEXT_LENGTH is one past the actual end of the text. */
257 input_text
[input_text_length
] = '\n';
258 /* This, on the other hand, is always necessary. */
259 input_text
[input_text_length
+1] = 0;
263 /* Pushing and popping files. */
265 push_node_filename (void)
267 if (node_filename_stack_index
+ 1 > node_filename_stack_size
)
268 node_filename_stack
= xrealloc
269 (node_filename_stack
, (node_filename_stack_size
+= 10) * sizeof (char *));
271 node_filename_stack
[node_filename_stack_index
] = node_filename
;
272 node_filename_stack_index
++;
276 pop_node_filename (void)
278 node_filename
= node_filename_stack
[--node_filename_stack_index
];
281 /* Save the state of the current input file. */
285 FSTACK
*newstack
= xmalloc (sizeof (FSTACK
));
286 newstack
->filename
= input_filename
;
287 newstack
->text
= input_text
;
288 newstack
->size
= input_text_length
;
289 newstack
->offset
= input_text_offset
;
290 newstack
->line_number
= line_number
;
291 newstack
->next
= filestack
;
293 filestack
= newstack
;
294 push_node_filename ();
297 /* Make the current file globals be what is on top of the file stack. */
301 FSTACK
*tos
= filestack
;
304 abort (); /* My fault. I wonder what I did? */
306 if (macro_expansion_output_stream
)
308 maybe_write_itext (input_text
, input_text_offset
);
309 forget_itext (input_text
);
313 filestack
= filestack
->next
;
315 /* Make sure that commands with braces have been satisfied. */
316 if (!executing_string
&& !me_executing_string
)
319 /* Get the top of the stack into the globals. */
320 input_filename
= tos
->filename
;
321 input_text
= tos
->text
;
322 input_text_length
= tos
->size
;
323 input_text_offset
= tos
->offset
;
324 line_number
= tos
->line_number
;
327 /* Go back to the (now) current node. */
328 pop_node_filename ();
331 /* Flush all open files on the file stack. */
333 flush_file_stack (void)
337 char *fname
= input_filename
;
338 char *text
= input_text
;
345 /* Return the index of the first character in the filename
346 which is past all the leading directory characters. */
348 skip_directory_part (char *filename
)
350 int i
= strlen (filename
) - 1;
352 while (i
&& !IS_SLASH (filename
[i
]))
354 if (IS_SLASH (filename
[i
]))
356 else if (filename
[i
] && HAVE_DRIVE (filename
))
363 filename_non_directory (char *name
)
365 return xstrdup (name
+ skip_directory_part (name
));
368 /* Return just the simple part of the filename; i.e. the
369 filename without the path information, or extensions.
370 This conses up a new string. */
372 filename_part (char *filename
)
374 char *basename
= filename_non_directory (filename
);
376 #ifdef REMOVE_OUTPUT_EXTENSIONS
377 /* See if there is an extension to remove. If so, remove it. */
379 char *temp
= strrchr (basename
, '.');
383 #endif /* REMOVE_OUTPUT_EXTENSIONS */
387 /* Return the pathname part of filename. This can be NULL. */
389 pathname_part (char *filename
)
394 filename
= expand_filename (filename
, "");
396 i
= skip_directory_part (filename
);
399 result
= xmalloc (1 + i
);
400 strncpy (result
, filename
, i
);
407 /* Return the full path to FILENAME. */
409 full_pathname (char *filename
)
411 int initial_character
;
414 /* No filename given? */
415 if (!filename
|| !*filename
)
418 /* Already absolute? */
419 if (IS_ABSOLUTE (filename
) ||
421 (IS_SLASH (filename
[1]) ||
422 (filename
[1] == '.' && IS_SLASH (filename
[2])))))
423 return xstrdup (filename
);
425 initial_character
= *filename
;
426 if (initial_character
!= '~')
428 char *localdir
= xmalloc (1025);
430 if (!getcwd (localdir
, 1024))
432 if (!getwd (localdir
))
435 fprintf (stderr
, _("%s: getwd: %s, %s\n"),
436 progname
, filename
, localdir
);
440 strcat (localdir
, "/");
441 strcat (localdir
, filename
);
442 result
= xstrdup (localdir
);
446 { /* Does anybody know why WIN32 doesn't want to support $HOME?
447 If the reason is they don't have getpwnam, they should
448 only disable the else clause below. */
450 if (IS_SLASH (filename
[1]))
452 /* Return the concatenation of the environment variable HOME
453 and the rest of the string. */
456 temp_home
= (char *) getenv ("HOME");
457 result
= xmalloc (strlen (&filename
[1])
459 + temp_home
? strlen (temp_home
)
464 strcpy (result
, temp_home
);
466 strcat (result
, &filename
[1]);
470 struct passwd
*user_entry
;
472 char *username
= xmalloc (257);
474 for (i
= 1; (c
= filename
[i
]); i
++)
484 user_entry
= getpwnam (username
);
487 return xstrdup (filename
);
489 result
= xmalloc (1 + strlen (user_entry
->pw_dir
)
490 + strlen (&filename
[i
]));
491 strcpy (result
, user_entry
->pw_dir
);
492 strcat (result
, &filename
[i
]);
494 #endif /* not WIN32 */
499 /* Return the expansion of FILENAME. */
501 expand_filename (char *filename
, char *input_name
)
507 filename
= full_pathname (filename
);
508 if (IS_ABSOLUTE (filename
)
509 || (*filename
== '.' &&
510 (IS_SLASH (filename
[1]) ||
511 (filename
[1] == '.' && IS_SLASH (filename
[2])))))
516 filename
= filename_non_directory (input_name
);
521 filename
= xstrdup ("noname.texi");
524 for (i
= strlen (filename
) - 1; i
; i
--)
525 if (filename
[i
] == '.')
529 i
= strlen (filename
);
531 if (i
+ 6 > (strlen (filename
)))
532 filename
= xrealloc (filename
, i
+ 6);
533 strcpy (filename
+ i
, html
? ".html" : ".info");
537 if (IS_ABSOLUTE (input_name
))
539 /* Make it so that relative names work. */
542 i
= strlen (input_name
) - 1;
544 result
= xmalloc (1 + strlen (input_name
) + strlen (filename
));
545 strcpy (result
, input_name
);
547 while (!IS_SLASH (result
[i
]) && i
)
549 if (IS_SLASH (result
[i
]))
552 strcpy (&result
[i
], filename
);
560 output_name_from_input_name (char *name
)
562 return expand_filename (NULL
, name
);
566 /* Modify the file name FNAME so that it fits the limitations of the
567 underlying filesystem. In particular, truncate the file name as it
568 would be truncated by the filesystem. We assume the result can
569 never be longer than the original, otherwise we couldn't be sure we
570 have enough space in the original string to modify it in place. */
572 normalize_filename (char *fname
)
575 char orig
[PATH_MAX
+ 1];
580 maxlen
= pathconf (fname
, _PC_NAME_MAX
);
585 i
= skip_directory_part (fname
);
586 if (fname
[i
] == '\0')
587 return fname
; /* only a directory name -- don't modify */
588 strcpy (orig
, fname
+ i
);
592 case 12: /* MS-DOS 8+3 filesystem */
593 if (orig
[0] == '.') /* leading dots are not allowed */
595 lastdot
= strrchr (orig
, '.');
597 lastdot
= orig
+ strlen (orig
);
598 strncpy (fname
+ i
, orig
, lastdot
- orig
);
600 p
< fname
+ i
+ (lastdot
- orig
) && p
< fname
+ i
+ 8;
606 strncat (fname
+ i
, lastdot
, 4);
608 case 14: /* old Unix systems with 14-char limitation */
609 strcpy (fname
+ i
, orig
);
610 if (strlen (fname
+ i
) > 14)
611 fname
[i
+ 14] = '\0';
614 strcpy (fname
+ i
, orig
);
615 if (strlen (fname
) > maxlen
- 1)
616 fname
[maxlen
- 1] = '\0';
623 /* Delayed writing functions. A few of the commands
624 needs to be handled at the end, namely @contents,
625 @shortcontents, @printindex and @listoffloats.
626 These functions take care of that. */
627 static DELAYED_WRITE
*delayed_writes
= NULL
;
628 int handling_delayed_writes
= 0;
631 register_delayed_write (char *delayed_command
)
635 if (!current_output_filename
|| !*current_output_filename
)
637 /* Cannot register if we don't know what the output file is. */
638 warning (_("`%s' omitted before output filename"), delayed_command
);
642 if (STREQ (current_output_filename
, "-"))
644 /* Do not register a new write if the output file is not seekable.
645 Let the user know about it first, though. */
646 warning (_("`%s' omitted since writing to stdout"), delayed_command
);
650 /* Don't complain if the user is writing /dev/null, since surely they
651 don't care, but don't register the delayed write, either. */
652 if (FILENAME_CMP (current_output_filename
, NULL_DEVICE
) == 0
653 || FILENAME_CMP (current_output_filename
, ALSO_NULL_DEVICE
) == 0)
656 /* We need the HTML header in the output,
657 to get a proper output_position. */
658 if (!executing_string
&& html
)
660 /* Get output_position updated. */
663 new = xmalloc (sizeof (DELAYED_WRITE
));
664 new->command
= xstrdup (delayed_command
);
665 new->filename
= xstrdup (current_output_filename
);
666 new->input_filename
= xstrdup (input_filename
);
667 new->position
= output_position
;
668 new->calling_line
= line_number
;
669 new->node
= current_node
? xstrdup (current_node
): "";
671 new->node_order
= node_order
;
672 new->index_order
= index_counter
;
674 new->next
= delayed_writes
;
675 delayed_writes
= new;
679 handle_delayed_writes (void)
681 DELAYED_WRITE
*temp
= (DELAYED_WRITE
*) reverse_list
682 ((GENERIC_LIST
*) delayed_writes
);
683 int position_shift_amount
, line_number_shift_amount
;
686 handling_delayed_writes
= 1;
690 delayed_buf
= find_and_load (temp
->filename
, 0);
692 if (output_paragraph_offset
> 0)
694 error (_("Output buffer not empty."));
700 fs_error (temp
->filename
);
704 output_stream
= fopen (temp
->filename
, "w");
707 fs_error (temp
->filename
);
711 if (fwrite (delayed_buf
, 1, temp
->position
, output_stream
) != temp
->position
)
713 fs_error (temp
->filename
);
718 int output_position_at_start
= output_position
;
719 int line_number_at_start
= output_line_number
;
721 /* In order to make warnings and errors
722 refer to the correct line number. */
723 input_filename
= temp
->input_filename
;
724 line_number
= temp
->calling_line
;
726 execute_string ("%s", temp
->command
);
729 /* Since the output file is modified, following delayed writes
730 need to be updated by this amount. */
731 position_shift_amount
= output_position
- output_position_at_start
;
732 line_number_shift_amount
= output_line_number
- line_number_at_start
;
735 if (fwrite (delayed_buf
+ temp
->position
, 1,
736 input_text_length
- temp
->position
, output_stream
)
737 != input_text_length
- temp
->position
738 || fclose (output_stream
) != 0)
739 fs_error (temp
->filename
);
741 /* Done with the buffer. */
744 /* Update positions in tag table for nodes that are defined after
745 the line this delayed write is registered. */
749 for (node
= tag_table
; node
; node
= node
->next_ent
)
750 if (node
->order
> temp
->node_order
)
751 node
->position
+= position_shift_amount
;
754 /* Something similar for the line numbers in all of the defined
758 for (i
= 0; i
< defined_indices
; i
++)
759 if (name_index_alist
[i
])
761 char *name
= ((INDEX_ALIST
*) name_index_alist
[i
])->name
;
763 for (index
= index_list (name
); index
; index
= index
->next
)
764 if ((no_headers
|| STREQ (index
->node
, temp
->node
))
765 && index
->entry_number
> temp
->index_order
)
766 index
->output_line
+= line_number_shift_amount
;
770 /* Shift remaining delayed positions
771 by the length of this write. */
773 DELAYED_WRITE
*future_write
= temp
->next
;
776 if (STREQ (temp
->filename
, future_write
->filename
))
777 future_write
->position
+= position_shift_amount
;
778 future_write
= future_write
->next
;