No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / texinfo / makeinfo / files.c
blob2228cb904fed53473c9b8fa2f919095901eb4567
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
7 Foundation, Inc.
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)
12 any later version.
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. */
23 #include "system.h"
24 #include "files.h"
25 #include "html.h"
26 #include "index.h"
27 #include "macro.h"
28 #include "makeinfo.h"
29 #include "node.h"
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. */
42 static char *
43 extract_colon_unit (char *string, int *index)
45 int start;
46 int path_sep_char = PATH_SEP[0];
47 int i = *index;
49 if (!string || (i >= strlen (string)))
50 return NULL;
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)
58 i++;
60 start = i;
61 while (string[i] && string[i] != path_sep_char) i++;
62 *index = i;
64 if (i == start)
66 if (string[i])
67 (*index)++;
69 /* Return "" in the case of a trailing `:'. */
70 return xstrdup ("");
72 else
74 char *value;
76 value = xmalloc (1 + (i - start));
77 memcpy (value, &string[start], (i - start));
78 value [i - start] = 0;
80 return value;
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. */
88 char *
89 get_file_info_in_path (char *filename, char *path, struct stat *finfo)
91 char *dir;
92 int result, index = 0;
94 if (path == NULL)
95 path = ".";
97 /* Handle absolute pathnames. */
98 if (IS_ABSOLUTE (filename)
99 || (*filename == '.'
100 && (IS_SLASH (filename[1])
101 || (filename[1] == '.' && IS_SLASH (filename[2])))))
103 if (stat (filename, finfo) == 0)
104 return xstrdup (filename);
105 else
106 return NULL;
109 while ((dir = extract_colon_unit (path, &index)))
111 char *fullpath;
113 if (!*dir)
115 free (dir);
116 dir = xstrdup (".");
119 fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
120 sprintf (fullpath, "%s/%s", dir, filename);
121 free (dir);
123 result = stat (fullpath, finfo);
125 if (result == 0)
126 return fullpath;
127 else
128 free (fullpath);
130 return NULL;
133 /* Prepend and append new paths to include_files_path. */
134 void
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), ".");
144 else
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);
152 free (tmp);
156 void
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. */
169 void
170 pop_path_from_include_path (void)
172 int i = 0;
173 char *tmp;
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. */
192 char *
193 find_and_load (char *filename, int use_path)
195 struct stat fileinfo;
196 long file_size;
197 int file = -1, count = 0;
198 char *fullpath, *result;
199 int n, bytes_to_read;
201 result = fullpath = NULL;
203 fullpath
204 = get_file_info_in_path (filename, use_path ? include_files_path : NULL,
205 &fileinfo);
207 if (!fullpath)
208 goto error_exit;
210 filename = fullpath;
211 file_size = (long) fileinfo.st_size;
213 file = open (filename, O_RDONLY);
214 if (file < 0)
215 goto error_exit;
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)
228 count += n;
229 bytes_to_read -= n;
231 if (0 < count && count < file_size)
232 result = xrealloc (result, count + 2); /* why waste the slack? */
233 else if (n == -1)
234 error_exit:
236 if (result)
237 free (result);
239 if (fullpath)
240 free (fullpath);
242 if (file != -1)
243 close (file);
245 return NULL;
247 close (file);
249 /* Set the globals to the new file. */
250 input_text = result;
251 input_text_length = count;
252 input_filename = fullpath;
253 node_filename = xstrdup (fullpath);
254 input_text_offset = 0;
255 line_number = 1;
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;
262 return result;
265 /* Pushing and popping files. */
266 static void
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++;
277 static void
278 pop_node_filename (void)
280 node_filename = node_filename_stack[--node_filename_stack_index];
283 /* Save the state of the current input file. */
284 void
285 pushfile (void)
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. */
300 void
301 popfile (void)
303 FSTACK *tos = filestack;
305 if (!tos)
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);
314 /* Pop the stack. */
315 filestack = filestack->next;
317 /* Make sure that commands with braces have been satisfied. */
318 if (!executing_string && !me_executing_string)
319 discard_braces ();
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;
327 free (tos);
329 /* Go back to the (now) current node. */
330 pop_node_filename ();
333 /* Flush all open files on the file stack. */
334 void
335 flush_file_stack (void)
337 while (filestack)
339 char *fname = input_filename;
340 char *text = input_text;
341 popfile ();
342 free (fname);
343 free (text);
347 /* Return the index of the first character in the filename
348 which is past all the leading directory characters. */
349 static int
350 skip_directory_part (char *filename)
352 int i = strlen (filename) - 1;
354 while (i && !IS_SLASH (filename[i]))
355 i--;
356 if (IS_SLASH (filename[i]))
357 i++;
358 else if (filename[i] && HAVE_DRIVE (filename))
359 i = 2;
361 return i;
364 static char *
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. */
373 char *
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, '.');
382 if (temp)
383 *temp = 0;
385 #endif /* REMOVE_OUTPUT_EXTENSIONS */
386 return basename;
389 /* Return the pathname part of filename. This can be NULL. */
390 char *
391 pathname_part (char *filename)
393 char *result = NULL;
394 int i;
396 filename = expand_filename (filename, "");
398 i = skip_directory_part (filename);
399 if (i)
401 result = xmalloc (1 + i);
402 strncpy (result, filename, i);
403 result[i] = 0;
405 free (filename);
406 return result;
409 /* Return the full path to FILENAME. */
410 static char *
411 full_pathname (char *filename)
413 int initial_character;
414 char *result;
416 /* No filename given? */
417 if (!filename || !*filename)
418 return xstrdup ("");
420 /* Already absolute? */
421 if (IS_ABSOLUTE (filename) ||
422 (*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);
431 #ifdef HAVE_GETCWD
432 if (!getcwd (localdir, 1024))
433 #else
434 if (!getwd (localdir))
435 #endif
437 fprintf (stderr, _("%s: getwd: %s, %s\n"),
438 progname, filename, localdir);
439 xexit (1);
442 strcat (localdir, "/");
443 strcat (localdir, filename);
444 result = xstrdup (localdir);
445 free (localdir);
447 else
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. */
451 #ifndef WIN32
452 if (IS_SLASH (filename[1]))
454 /* Return the concatenation of the environment variable HOME
455 and the rest of the string. */
456 char *temp_home;
458 temp_home = (char *) getenv ("HOME");
459 result = xmalloc (strlen (&filename[1])
461 + temp_home ? strlen (temp_home)
462 : 0);
463 *result = 0;
465 if (temp_home)
466 strcpy (result, temp_home);
468 strcat (result, &filename[1]);
470 else
472 struct passwd *user_entry;
473 int i, c;
474 char *username = xmalloc (257);
476 for (i = 1; (c = filename[i]); i++)
478 if (IS_SLASH (c))
479 break;
480 else
481 username[i - 1] = c;
483 if (c)
484 username[i - 1] = 0;
486 user_entry = getpwnam (username);
488 if (!user_entry)
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 */
498 return result;
501 /* Return the expansion of FILENAME. */
502 char *
503 expand_filename (char *filename, char *input_name)
505 int i;
507 if (filename)
509 filename = full_pathname (filename);
510 if (IS_ABSOLUTE (filename)
511 || (*filename == '.' &&
512 (IS_SLASH (filename[1]) ||
513 (filename[1] == '.' && IS_SLASH (filename[2])))))
514 return filename;
516 else
518 filename = filename_non_directory (input_name);
520 if (!*filename)
522 free (filename);
523 filename = xstrdup ("noname.texi");
526 for (i = strlen (filename) - 1; i; i--)
527 if (filename[i] == '.')
528 break;
530 if (!i)
531 i = strlen (filename);
533 if (i + 6 > (strlen (filename)))
534 filename = xrealloc (filename, i + 6);
535 strcpy (filename + i, html ? ".html" : ".info");
536 return filename;
539 if (IS_ABSOLUTE (input_name))
541 /* Make it so that relative names work. */
542 char *result;
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)
550 i--;
551 if (IS_SLASH (result[i]))
552 i++;
554 strcpy (&result[i], filename);
555 free (filename);
556 return result;
558 return filename;
561 char *
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. */
573 char *
574 normalize_filename (char *fname)
576 int maxlen;
577 char orig[PATH_MAX + 1];
578 int i;
579 char *lastdot, *p;
581 #ifdef _PC_NAME_MAX
582 maxlen = pathconf (fname, _PC_NAME_MAX);
583 if (maxlen < 1)
584 #endif
585 maxlen = PATH_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);
592 switch (maxlen)
594 case 12: /* MS-DOS 8+3 filesystem */
595 if (orig[0] == '.') /* leading dots are not allowed */
596 orig[0] = '_';
597 lastdot = strrchr (orig, '.');
598 if (!lastdot)
599 lastdot = orig + strlen (orig);
600 strncpy (fname + i, orig, lastdot - orig);
601 for (p = fname + i;
602 p < fname + i + (lastdot - orig) && p < fname + i + 8;
603 p++)
604 if (*p == '.')
605 *p = '_';
606 *p = '\0';
607 if (*lastdot == '.')
608 strncat (fname + i, lastdot, 4);
609 break;
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';
614 break;
615 default:
616 strcpy (fname + i, orig);
617 if (strlen (fname) > maxlen - 1)
618 fname[maxlen - 1] = '\0';
619 break;
622 return fname;
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;
632 void
633 register_delayed_write (char *delayed_command)
635 DELAYED_WRITE *new;
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);
641 return;
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);
649 return;
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)
656 return;
658 /* We need the HTML header in the output,
659 to get a proper output_position. */
660 if (!executing_string && html)
661 html_output_head ();
662 /* Get output_position updated. */
663 flush_output ();
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;
680 void
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;
686 char *delayed_buf;
688 handling_delayed_writes = 1;
690 while (temp)
692 delayed_buf = find_and_load (temp->filename, 0);
694 if (output_paragraph_offset > 0)
696 error (_("Output buffer not empty."));
697 return;
700 if (!delayed_buf)
702 fs_error (temp->filename);
703 return;
706 output_stream = fopen (temp->filename, "w");
707 if (!output_stream)
709 fs_error (temp->filename);
710 return;
713 if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position)
715 fs_error (temp->filename);
716 return;
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);
729 flush_output ();
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. */
744 free (delayed_buf);
746 /* Update positions in tag table for nodes that are defined after
747 the line this delayed write is registered. */
748 if (!html && !xml)
750 TAG_ENTRY *node;
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
757 indices. */
759 int i;
760 for (i = 0; i < defined_indices; i++)
761 if (name_index_alist[i])
763 char *name = ((INDEX_ALIST *) name_index_alist[i])->name;
764 INDEX_ELT *index;
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;
776 while (future_write)
778 if (STREQ (temp->filename, future_write->filename))
779 future_write->position += position_shift_amount;
780 future_write = future_write->next;
784 temp = temp->next;