1 /* $NetBSD: files.c,v 1.8 2008/09/02 08:00:24 christos Exp $ */
3 /* files.c -- file-related functions for makeinfo.
4 Id: files.c,v 1.5 2004/07/27 00:06:31 karl Exp
6 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
31 FSTACK
*filestack
= NULL
;
33 static int node_filename_stack_index
= 0;
34 static int node_filename_stack_size
= 0;
35 static char **node_filename_stack
= NULL
;
37 /* Looking for include files. */
39 /* Given a string containing units of information separated by colons,
40 return the next one pointed to by INDEX, or NULL if there are no more.
41 Advance INDEX to the character after the colon. */
43 extract_colon_unit (char *string
, int *index
)
46 int path_sep_char
= PATH_SEP
[0];
49 if (!string
|| (i
>= strlen (string
)))
52 /* Each call to this routine leaves the index pointing at a colon if
53 there is more to the path. If i > 0, then increment past the
54 `:'. If i == 0, then the path has a leading colon. Trailing colons
55 are handled OK by the `else' part of the if statement; an empty
56 string is returned in that case. */
57 if (i
&& string
[i
] == path_sep_char
)
61 while (string
[i
] && string
[i
] != path_sep_char
) i
++;
69 /* Return "" in the case of a trailing `:'. */
76 value
= xmalloc (1 + (i
- start
));
77 memcpy (value
, &string
[start
], (i
- start
));
78 value
[i
- start
] = 0;
84 /* Return the full pathname for FILENAME by searching along PATH.
85 When found, return the stat () info for FILENAME in FINFO.
86 If PATH is NULL, only the current directory is searched.
87 If the file could not be found, return a NULL pointer. */
89 get_file_info_in_path (char *filename
, char *path
, struct stat
*finfo
)
92 int result
, index
= 0;
97 /* Handle absolute pathnames. */
98 if (IS_ABSOLUTE (filename
)
100 && (IS_SLASH (filename
[1])
101 || (filename
[1] == '.' && IS_SLASH (filename
[2])))))
103 if (stat (filename
, finfo
) == 0)
104 return xstrdup (filename
);
109 while ((dir
= extract_colon_unit (path
, &index
)))
119 fullpath
= xmalloc (2 + strlen (dir
) + strlen (filename
));
120 sprintf (fullpath
, "%s/%s", dir
, filename
);
123 result
= stat (fullpath
, finfo
);
133 /* Prepend and append new paths to include_files_path. */
135 prepend_to_include_path (char *path
)
137 if (!include_files_path
)
139 include_files_path
= xstrdup (path
);
140 include_files_path
= xrealloc (include_files_path
,
141 strlen (include_files_path
) + 3); /* 3 for ":.\0" */
142 strcat (strcat (include_files_path
, PATH_SEP
), ".");
146 char *tmp
= xstrdup (include_files_path
);
147 include_files_path
= xrealloc (include_files_path
,
148 strlen (include_files_path
) + strlen (path
) + 2); /* 2 for ":\0" */
149 strcpy (include_files_path
, path
);
150 strcat (include_files_path
, PATH_SEP
);
151 strcat (include_files_path
, tmp
);
157 append_to_include_path (char *path
)
159 if (!include_files_path
)
160 include_files_path
= xstrdup (".");
162 include_files_path
= (char *) xrealloc (include_files_path
,
163 2 + strlen (include_files_path
) + strlen (path
));
164 strcat (include_files_path
, PATH_SEP
);
165 strcat (include_files_path
, path
);
168 /* Remove the first path from the include_files_path. */
170 pop_path_from_include_path (void)
175 if (include_files_path
)
176 for (i
= 0; i
< strlen (include_files_path
)
177 && include_files_path
[i
] != ':'; i
++);
179 /* Advance include_files_path to the next char from ':' */
180 tmp
= (char *) xmalloc (strlen (include_files_path
) - i
);
181 strcpy (tmp
, (char *) include_files_path
+ i
+ 1);
183 free (include_files_path
);
184 include_files_path
= tmp
;
187 /* Find and load the file named FILENAME. Return a pointer to
188 the loaded file, or NULL if it can't be loaded. If USE_PATH is zero,
189 just look for the given file (this is used in handle_delayed_writes),
190 else search along include_files_path. */
193 find_and_load (char *filename
, int use_path
)
195 struct stat fileinfo
;
197 int file
= -1, count
= 0;
198 char *fullpath
, *result
;
199 int n
, bytes_to_read
;
201 result
= fullpath
= NULL
;
204 = get_file_info_in_path (filename
, use_path
? include_files_path
: NULL
,
211 file_size
= (long) fileinfo
.st_size
;
213 file
= open (filename
, O_RDONLY
);
217 /* Load the file, with enough room for a newline and a null. */
218 result
= xmalloc (file_size
+ 2);
220 /* VMS stat lies about the st_size value. The actual number of
221 readable bytes is always less than this value. The arcane
222 mysteries of VMS/RMS are too much to probe, so this hack
223 suffices to make things work. It's also needed on Cygwin. And so
224 we might as well use it everywhere. */
225 bytes_to_read
= file_size
;
226 while ((n
= read (file
, result
+ count
, bytes_to_read
)) > 0)
231 if (0 < count
&& count
< file_size
)
232 result
= xrealloc (result
, count
+ 2); /* why waste the slack? */
249 /* Set the globals to the new file. */
251 input_text_length
= count
;
252 input_filename
= fullpath
;
253 node_filename
= xstrdup (fullpath
);
254 input_text_offset
= 0;
256 /* Not strictly necessary. This magic prevents read_token () from doing
257 extra unnecessary work each time it is called (that is a lot of times).
258 INPUT_TEXT_LENGTH is one past the actual end of the text. */
259 input_text
[input_text_length
] = '\n';
260 /* This, on the other hand, is always necessary. */
261 input_text
[input_text_length
+1] = 0;
265 /* Pushing and popping files. */
267 push_node_filename (void)
269 if (node_filename_stack_index
+ 1 > node_filename_stack_size
)
270 node_filename_stack
= xrealloc
271 (node_filename_stack
, (node_filename_stack_size
+= 10) * sizeof (char *));
273 node_filename_stack
[node_filename_stack_index
] = node_filename
;
274 node_filename_stack_index
++;
278 pop_node_filename (void)
280 node_filename
= node_filename_stack
[--node_filename_stack_index
];
283 /* Save the state of the current input file. */
287 FSTACK
*newstack
= xmalloc (sizeof (FSTACK
));
288 newstack
->filename
= input_filename
;
289 newstack
->text
= input_text
;
290 newstack
->size
= input_text_length
;
291 newstack
->offset
= input_text_offset
;
292 newstack
->line_number
= line_number
;
293 newstack
->next
= filestack
;
295 filestack
= newstack
;
296 push_node_filename ();
299 /* Make the current file globals be what is on top of the file stack. */
303 FSTACK
*tos
= filestack
;
306 abort (); /* My fault. I wonder what I did? */
308 if (macro_expansion_output_stream
)
310 maybe_write_itext (input_text
, input_text_offset
);
311 forget_itext (input_text
);
315 filestack
= filestack
->next
;
317 /* Make sure that commands with braces have been satisfied. */
318 if (!executing_string
&& !me_executing_string
)
321 /* Get the top of the stack into the globals. */
322 input_filename
= tos
->filename
;
323 input_text
= tos
->text
;
324 input_text_length
= tos
->size
;
325 input_text_offset
= tos
->offset
;
326 line_number
= tos
->line_number
;
329 /* Go back to the (now) current node. */
330 pop_node_filename ();
333 /* Flush all open files on the file stack. */
335 flush_file_stack (void)
339 char *fname
= input_filename
;
340 char *text
= input_text
;
347 /* Return the index of the first character in the filename
348 which is past all the leading directory characters. */
350 skip_directory_part (char *filename
)
352 int i
= strlen (filename
) - 1;
354 while (i
&& !IS_SLASH (filename
[i
]))
356 if (IS_SLASH (filename
[i
]))
358 else if (filename
[i
] && HAVE_DRIVE (filename
))
365 filename_non_directory (char *name
)
367 return xstrdup (name
+ skip_directory_part (name
));
370 /* Return just the simple part of the filename; i.e. the
371 filename without the path information, or extensions.
372 This conses up a new string. */
374 filename_part (char *filename
)
376 char *basename
= filename_non_directory (filename
);
378 #ifdef REMOVE_OUTPUT_EXTENSIONS
379 /* See if there is an extension to remove. If so, remove it. */
381 char *temp
= strrchr (basename
, '.');
385 #endif /* REMOVE_OUTPUT_EXTENSIONS */
389 /* Return the pathname part of filename. This can be NULL. */
391 pathname_part (char *filename
)
396 filename
= expand_filename (filename
, "");
398 i
= skip_directory_part (filename
);
401 result
= xmalloc (1 + i
);
402 strncpy (result
, filename
, i
);
409 /* Return the full path to FILENAME. */
411 full_pathname (char *filename
)
413 int initial_character
;
416 /* No filename given? */
417 if (!filename
|| !*filename
)
420 /* Already absolute? */
421 if (IS_ABSOLUTE (filename
) ||
423 (IS_SLASH (filename
[1]) ||
424 (filename
[1] == '.' && IS_SLASH (filename
[2])))))
425 return xstrdup (filename
);
427 initial_character
= *filename
;
428 if (initial_character
!= '~')
430 char *localdir
= xmalloc (1025);
432 if (!getcwd (localdir
, 1024))
434 if (!getwd (localdir
))
437 fprintf (stderr
, _("%s: getwd: %s, %s\n"),
438 progname
, filename
, localdir
);
442 strcat (localdir
, "/");
443 strcat (localdir
, filename
);
444 result
= xstrdup (localdir
);
448 { /* Does anybody know why WIN32 doesn't want to support $HOME?
449 If the reason is they don't have getpwnam, they should
450 only disable the else clause below. */
452 if (IS_SLASH (filename
[1]))
454 /* Return the concatenation of the environment variable HOME
455 and the rest of the string. */
458 temp_home
= (char *) getenv ("HOME");
459 result
= xmalloc (strlen (&filename
[1])
461 + temp_home
? strlen (temp_home
)
466 strcpy (result
, temp_home
);
468 strcat (result
, &filename
[1]);
472 struct passwd
*user_entry
;
474 char *username
= xmalloc (257);
476 for (i
= 1; (c
= filename
[i
]); i
++)
486 user_entry
= getpwnam (username
);
489 return xstrdup (filename
);
491 result
= xmalloc (1 + strlen (user_entry
->pw_dir
)
492 + strlen (&filename
[i
]));
493 strcpy (result
, user_entry
->pw_dir
);
494 strcat (result
, &filename
[i
]);
496 #endif /* not WIN32 */
501 /* Return the expansion of FILENAME. */
503 expand_filename (char *filename
, char *input_name
)
509 filename
= full_pathname (filename
);
510 if (IS_ABSOLUTE (filename
)
511 || (*filename
== '.' &&
512 (IS_SLASH (filename
[1]) ||
513 (filename
[1] == '.' && IS_SLASH (filename
[2])))))
518 filename
= filename_non_directory (input_name
);
523 filename
= xstrdup ("noname.texi");
526 for (i
= strlen (filename
) - 1; i
; i
--)
527 if (filename
[i
] == '.')
531 i
= strlen (filename
);
533 if (i
+ 6 > (strlen (filename
)))
534 filename
= xrealloc (filename
, i
+ 6);
535 strcpy (filename
+ i
, html
? ".html" : ".info");
539 if (IS_ABSOLUTE (input_name
))
541 /* Make it so that relative names work. */
544 i
= strlen (input_name
) - 1;
546 result
= xmalloc (1 + strlen (input_name
) + strlen (filename
));
547 strcpy (result
, input_name
);
549 while (!IS_SLASH (result
[i
]) && i
)
551 if (IS_SLASH (result
[i
]))
554 strcpy (&result
[i
], filename
);
562 output_name_from_input_name (char *name
)
564 return expand_filename (NULL
, name
);
568 /* Modify the file name FNAME so that it fits the limitations of the
569 underlying filesystem. In particular, truncate the file name as it
570 would be truncated by the filesystem. We assume the result can
571 never be longer than the original, otherwise we couldn't be sure we
572 have enough space in the original string to modify it in place. */
574 normalize_filename (char *fname
)
577 char orig
[PATH_MAX
+ 1];
582 maxlen
= pathconf (fname
, _PC_NAME_MAX
);
587 i
= skip_directory_part (fname
);
588 if (fname
[i
] == '\0')
589 return fname
; /* only a directory name -- don't modify */
590 strcpy (orig
, fname
+ i
);
594 case 12: /* MS-DOS 8+3 filesystem */
595 if (orig
[0] == '.') /* leading dots are not allowed */
597 lastdot
= strrchr (orig
, '.');
599 lastdot
= orig
+ strlen (orig
);
600 strncpy (fname
+ i
, orig
, lastdot
- orig
);
602 p
< fname
+ i
+ (lastdot
- orig
) && p
< fname
+ i
+ 8;
608 strncat (fname
+ i
, lastdot
, 4);
610 case 14: /* old Unix systems with 14-char limitation */
611 strcpy (fname
+ i
, orig
);
612 if (strlen (fname
+ i
) > 14)
613 fname
[i
+ 14] = '\0';
616 strcpy (fname
+ i
, orig
);
617 if (strlen (fname
) > maxlen
- 1)
618 fname
[maxlen
- 1] = '\0';
625 /* Delayed writing functions. A few of the commands
626 needs to be handled at the end, namely @contents,
627 @shortcontents, @printindex and @listoffloats.
628 These functions take care of that. */
629 static DELAYED_WRITE
*delayed_writes
= NULL
;
630 int handling_delayed_writes
= 0;
633 register_delayed_write (char *delayed_command
)
637 if (!current_output_filename
|| !*current_output_filename
)
639 /* Cannot register if we don't know what the output file is. */
640 warning (_("`%s' omitted before output filename"), delayed_command
);
644 if (STREQ (current_output_filename
, "-"))
646 /* Do not register a new write if the output file is not seekable.
647 Let the user know about it first, though. */
648 warning (_("`%s' omitted since writing to stdout"), delayed_command
);
652 /* Don't complain if the user is writing /dev/null, since surely they
653 don't care, but don't register the delayed write, either. */
654 if (FILENAME_CMP (current_output_filename
, NULL_DEVICE
) == 0
655 || FILENAME_CMP (current_output_filename
, ALSO_NULL_DEVICE
) == 0)
658 /* We need the HTML header in the output,
659 to get a proper output_position. */
660 if (!executing_string
&& html
)
662 /* Get output_position updated. */
665 new = xmalloc (sizeof (DELAYED_WRITE
));
666 new->command
= xstrdup (delayed_command
);
667 new->filename
= xstrdup (current_output_filename
);
668 new->input_filename
= xstrdup (input_filename
);
669 new->position
= output_position
;
670 new->calling_line
= line_number
;
671 new->node
= current_node
? xstrdup (current_node
): "";
673 new->node_order
= node_order
;
674 new->index_order
= index_counter
;
676 new->next
= delayed_writes
;
677 delayed_writes
= new;
681 handle_delayed_writes (void)
683 DELAYED_WRITE
*temp
= (DELAYED_WRITE
*) reverse_list
684 ((GENERIC_LIST
*) delayed_writes
);
685 int position_shift_amount
, line_number_shift_amount
;
688 handling_delayed_writes
= 1;
692 delayed_buf
= find_and_load (temp
->filename
, 0);
694 if (output_paragraph_offset
> 0)
696 error (_("Output buffer not empty."));
702 fs_error (temp
->filename
);
706 output_stream
= fopen (temp
->filename
, "w");
709 fs_error (temp
->filename
);
713 if (fwrite (delayed_buf
, 1, temp
->position
, output_stream
) != temp
->position
)
715 fs_error (temp
->filename
);
720 int output_position_at_start
= output_position
;
721 int line_number_at_start
= output_line_number
;
723 /* In order to make warnings and errors
724 refer to the correct line number. */
725 input_filename
= temp
->input_filename
;
726 line_number
= temp
->calling_line
;
728 execute_string ("%s", temp
->command
);
731 /* Since the output file is modified, following delayed writes
732 need to be updated by this amount. */
733 position_shift_amount
= output_position
- output_position_at_start
;
734 line_number_shift_amount
= output_line_number
- line_number_at_start
;
737 if (fwrite (delayed_buf
+ temp
->position
, 1,
738 input_text_length
- temp
->position
, output_stream
)
739 != input_text_length
- temp
->position
740 || fclose (output_stream
) != 0)
741 fs_error (temp
->filename
);
743 /* Done with the buffer. */
746 /* Update positions in tag table for nodes that are defined after
747 the line this delayed write is registered. */
751 for (node
= tag_table
; node
; node
= node
->next_ent
)
752 if (node
->order
> temp
->node_order
)
753 node
->position
+= position_shift_amount
;
756 /* Something similar for the line numbers in all of the defined
760 for (i
= 0; i
< defined_indices
; i
++)
761 if (name_index_alist
[i
])
763 char *name
= ((INDEX_ALIST
*) name_index_alist
[i
])->name
;
765 for (index
= index_list (name
); index
; index
= index
->next
)
766 if ((no_headers
|| STREQ (index
->node
, temp
->node
))
767 && index
->entry_number
> temp
->index_order
)
768 index
->output_line
+= line_number_shift_amount
;
772 /* Shift remaining delayed positions
773 by the length of this write. */
775 DELAYED_WRITE
*future_write
= temp
->next
;
778 if (STREQ (temp
->filename
, future_write
->filename
))
779 future_write
->position
+= position_shift_amount
;
780 future_write
= future_write
->next
;