123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784 |
- /* files.c -- file-related functions for makeinfo.
- $Id: files.c,v 1.8 2007-07-01 21:20:32 karl Exp $
- Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007
- Free Software Foundation, Inc.
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "system.h"
- #include "files.h"
- #include "html.h"
- #include "index.h"
- #include "macro.h"
- #include "makeinfo.h"
- #include "node.h"
- FSTACK *filestack = NULL;
- static int node_filename_stack_index = 0;
- static int node_filename_stack_size = 0;
- static char **node_filename_stack = NULL;
- /* Looking for include files. */
- /* Given a string containing units of information separated by colons,
- return the next one pointed to by INDEX, or NULL if there are no more.
- Advance INDEX to the character after the colon. */
- static char *
- extract_colon_unit (char *string, int *index)
- {
- int start;
- int path_sep_char = PATH_SEP[0];
- int i = *index;
- if (!string || (i >= strlen (string)))
- return NULL;
- /* Each call to this routine leaves the index pointing at a colon if
- there is more to the path. If i > 0, then increment past the
- `:'. If i == 0, then the path has a leading colon. Trailing colons
- are handled OK by the `else' part of the if statement; an empty
- string is returned in that case. */
- if (i && string[i] == path_sep_char)
- i++;
- start = i;
- while (string[i] && string[i] != path_sep_char) i++;
- *index = i;
- if (i == start)
- {
- if (string[i])
- (*index)++;
- /* Return "" in the case of a trailing `:'. */
- return xstrdup ("");
- }
- else
- {
- char *value;
- value = xmalloc (1 + (i - start));
- memcpy (value, &string[start], (i - start));
- value [i - start] = 0;
- return value;
- }
- }
- /* Return the full pathname for FILENAME by searching along PATH.
- When found, return the stat () info for FILENAME in FINFO.
- If PATH is NULL, only the current directory is searched.
- If the file could not be found, return a NULL pointer. */
- char *
- get_file_info_in_path (char *filename, char *path, struct stat *finfo)
- {
- char *dir;
- int result, index = 0;
- if (path == NULL)
- path = ".";
- /* Handle absolute pathnames. */
- if (IS_ABSOLUTE (filename)
- || (*filename == '.'
- && (IS_SLASH (filename[1])
- || (filename[1] == '.' && IS_SLASH (filename[2])))))
- {
- if (stat (filename, finfo) == 0)
- return xstrdup (filename);
- else
- return NULL;
- }
- while ((dir = extract_colon_unit (path, &index)))
- {
- char *fullpath;
- if (!*dir)
- {
- free (dir);
- dir = xstrdup (".");
- }
- fullpath = xmalloc (2 + strlen (dir) + strlen (filename));
- sprintf (fullpath, "%s/%s", dir, filename);
- free (dir);
- result = stat (fullpath, finfo);
- if (result == 0)
- return fullpath;
- else
- free (fullpath);
- }
- return NULL;
- }
- /* Prepend and append new paths to include_files_path. */
- void
- prepend_to_include_path (char *path)
- {
- if (!include_files_path)
- {
- include_files_path = xstrdup (path);
- include_files_path = xrealloc (include_files_path,
- strlen (include_files_path) + 3); /* 3 for ":.\0" */
- strcat (strcat (include_files_path, PATH_SEP), ".");
- }
- else
- {
- char *tmp = xstrdup (include_files_path);
- include_files_path = xrealloc (include_files_path,
- strlen (include_files_path) + strlen (path) + 2); /* 2 for ":\0" */
- strcpy (include_files_path, path);
- strcat (include_files_path, PATH_SEP);
- strcat (include_files_path, tmp);
- free (tmp);
- }
- }
- void
- append_to_include_path (char *path)
- {
- if (!include_files_path)
- include_files_path = xstrdup (".");
- include_files_path = (char *) xrealloc (include_files_path,
- 2 + strlen (include_files_path) + strlen (path));
- strcat (include_files_path, PATH_SEP);
- strcat (include_files_path, path);
- }
- /* Remove the first path from the include_files_path. */
- void
- pop_path_from_include_path (void)
- {
- int i = 0;
- char *tmp;
- if (include_files_path)
- for (i = 0; i < strlen (include_files_path)
- && include_files_path[i] != ':'; i++);
- /* Advance include_files_path to the next char from ':' */
- tmp = (char *) xmalloc (strlen (include_files_path) - i);
- strcpy (tmp, (char *) include_files_path + i + 1);
- free (include_files_path);
- include_files_path = tmp;
- }
- /* Find and load the file named FILENAME. Return a pointer to
- the loaded file, or NULL if it can't be loaded. If USE_PATH is zero,
- just look for the given file (this is used in handle_delayed_writes),
- else search along include_files_path. */
- char *
- find_and_load (char *filename, int use_path)
- {
- struct stat fileinfo;
- long file_size;
- int file = -1, count = 0;
- char *fullpath, *result;
- int n, bytes_to_read;
- result = fullpath = NULL;
- fullpath
- = get_file_info_in_path (filename, use_path ? include_files_path : NULL,
- &fileinfo);
- if (!fullpath)
- goto error_exit;
- filename = fullpath;
- file_size = (long) fileinfo.st_size;
- file = open (filename, O_RDONLY);
- if (file < 0)
- goto error_exit;
- /* Load the file, with enough room for a newline and a null. */
- result = xmalloc (file_size + 2);
- /* VMS stat lies about the st_size value. The actual number of
- readable bytes is always less than this value. The arcane
- mysteries of VMS/RMS are too much to probe, so this hack
- suffices to make things work. It's also needed on Cygwin. And so
- we might as well use it everywhere. */
- bytes_to_read = file_size;
- while ((n = read (file, result + count, bytes_to_read)) > 0)
- {
- count += n;
- bytes_to_read -= n;
- }
- if (0 < count && count < file_size)
- result = xrealloc (result, count + 2); /* why waste the slack? */
- else if (n == -1)
- error_exit:
- {
- if (result)
- free (result);
- if (fullpath)
- free (fullpath);
- if (file != -1)
- close (file);
- return NULL;
- }
- close (file);
- /* Set the globals to the new file. */
- input_text = result;
- input_text_length = count;
- input_filename = fullpath;
- node_filename = xstrdup (fullpath);
- input_text_offset = 0;
- line_number = 1;
- /* Not strictly necessary. This magic prevents read_token () from doing
- extra unnecessary work each time it is called (that is a lot of times).
- INPUT_TEXT_LENGTH is one past the actual end of the text. */
- input_text[input_text_length] = '\n';
- /* This, on the other hand, is always necessary. */
- input_text[input_text_length+1] = 0;
- return result;
- }
- /* Pushing and popping files. */
- static void
- push_node_filename (void)
- {
- if (node_filename_stack_index + 1 > node_filename_stack_size)
- node_filename_stack = xrealloc
- (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *));
- node_filename_stack[node_filename_stack_index] = node_filename;
- node_filename_stack_index++;
- }
- static void
- pop_node_filename (void)
- {
- node_filename = node_filename_stack[--node_filename_stack_index];
- }
- /* Save the state of the current input file. */
- void
- pushfile (void)
- {
- FSTACK *newstack = xmalloc (sizeof (FSTACK));
- newstack->filename = input_filename;
- newstack->text = input_text;
- newstack->size = input_text_length;
- newstack->offset = input_text_offset;
- newstack->line_number = line_number;
- newstack->next = filestack;
- filestack = newstack;
- push_node_filename ();
- }
- /* Make the current file globals be what is on top of the file stack. */
- void
- popfile (void)
- {
- FSTACK *tos = filestack;
- if (!tos)
- abort (); /* My fault. I wonder what I did? */
- if (macro_expansion_output_stream)
- {
- maybe_write_itext (input_text, input_text_offset);
- forget_itext (input_text);
- }
- /* Pop the stack. */
- filestack = filestack->next;
- /* Make sure that commands with braces have been satisfied. */
- if (!executing_string && !me_executing_string)
- discard_braces ();
- /* Get the top of the stack into the globals. */
- input_filename = tos->filename;
- input_text = tos->text;
- input_text_length = tos->size;
- input_text_offset = tos->offset;
- line_number = tos->line_number;
- free (tos);
- /* Go back to the (now) current node. */
- pop_node_filename ();
- }
- /* Flush all open files on the file stack. */
- void
- flush_file_stack (void)
- {
- while (filestack)
- {
- char *fname = input_filename;
- char *text = input_text;
- popfile ();
- free (fname);
- free (text);
- }
- }
- /* Return the index of the first character in the filename
- which is past all the leading directory characters. */
- static int
- skip_directory_part (char *filename)
- {
- int i = strlen (filename) - 1;
- while (i && !IS_SLASH (filename[i]))
- i--;
- if (IS_SLASH (filename[i]))
- i++;
- else if (filename[i] && HAVE_DRIVE (filename))
- i = 2;
- return i;
- }
- static char *
- filename_non_directory (char *name)
- {
- return xstrdup (name + skip_directory_part (name));
- }
- /* Return just the simple part of the filename; i.e. the
- filename without the path information, or extensions.
- This conses up a new string. */
- char *
- filename_part (char *filename)
- {
- char *basename = filename_non_directory (filename);
- #ifdef REMOVE_OUTPUT_EXTENSIONS
- /* See if there is an extension to remove. If so, remove it. */
- {
- char *temp = strrchr (basename, '.');
- if (temp)
- *temp = 0;
- }
- #endif /* REMOVE_OUTPUT_EXTENSIONS */
- return basename;
- }
- /* Return the pathname part of filename. This can be NULL. */
- char *
- pathname_part (char *filename)
- {
- char *result = NULL;
- int i;
- filename = expand_filename (filename, "");
- i = skip_directory_part (filename);
- if (i)
- {
- result = xmalloc (1 + i);
- strncpy (result, filename, i);
- result[i] = 0;
- }
- free (filename);
- return result;
- }
- /* Return the full path to FILENAME. */
- static char *
- full_pathname (char *filename)
- {
- int initial_character;
- char *result;
- /* No filename given? */
- if (!filename || !*filename)
- return xstrdup ("");
-
- /* Already absolute? */
- if (IS_ABSOLUTE (filename) ||
- (*filename == '.' &&
- (IS_SLASH (filename[1]) ||
- (filename[1] == '.' && IS_SLASH (filename[2])))))
- return xstrdup (filename);
- initial_character = *filename;
- if (initial_character != '~')
- {
- char *localdir = xmalloc (1025);
- #ifdef HAVE_GETCWD
- if (!getcwd (localdir, 1024))
- #else
- if (!getwd (localdir))
- #endif
- {
- fprintf (stderr, _("%s: getwd: %s, %s\n"),
- progname, filename, localdir);
- xexit (1);
- }
- strcat (localdir, "/");
- strcat (localdir, filename);
- result = xstrdup (localdir);
- free (localdir);
- }
- else
- { /* Does anybody know why WIN32 doesn't want to support $HOME?
- If the reason is they don't have getpwnam, they should
- only disable the else clause below. */
- #ifndef WIN32
- if (IS_SLASH (filename[1]))
- {
- /* Return the concatenation of the environment variable HOME
- and the rest of the string. */
- char *temp_home;
- temp_home = (char *) getenv ("HOME");
- result = xmalloc (strlen (&filename[1])
- + 1
- + temp_home ? strlen (temp_home)
- : 0);
- *result = 0;
- if (temp_home)
- strcpy (result, temp_home);
- strcat (result, &filename[1]);
- }
- else
- {
- struct passwd *user_entry;
- int i, c;
- char *username = xmalloc (257);
- for (i = 1; (c = filename[i]); i++)
- {
- if (IS_SLASH (c))
- break;
- else
- username[i - 1] = c;
- }
- if (c)
- username[i - 1] = 0;
- user_entry = getpwnam (username);
- if (!user_entry)
- return xstrdup (filename);
- result = xmalloc (1 + strlen (user_entry->pw_dir)
- + strlen (&filename[i]));
- strcpy (result, user_entry->pw_dir);
- strcat (result, &filename[i]);
- }
- #endif /* not WIN32 */
- }
- return result;
- }
- /* Return the expansion of FILENAME. */
- char *
- expand_filename (char *filename, char *input_name)
- {
- int i;
- if (filename)
- {
- filename = full_pathname (filename);
- if (IS_ABSOLUTE (filename)
- || (*filename == '.' &&
- (IS_SLASH (filename[1]) ||
- (filename[1] == '.' && IS_SLASH (filename[2])))))
- return filename;
- }
- else
- {
- filename = filename_non_directory (input_name);
- if (!*filename)
- {
- free (filename);
- filename = xstrdup ("noname.texi");
- }
- for (i = strlen (filename) - 1; i; i--)
- if (filename[i] == '.')
- break;
- if (!i)
- i = strlen (filename);
- if (i + 6 > (strlen (filename)))
- filename = xrealloc (filename, i + 6);
- strcpy (filename + i, html ? ".html" : ".info");
- return filename;
- }
- if (IS_ABSOLUTE (input_name))
- {
- /* Make it so that relative names work. */
- char *result;
-
- i = strlen (input_name) - 1;
- result = xmalloc (1 + strlen (input_name) + strlen (filename));
- strcpy (result, input_name);
- while (!IS_SLASH (result[i]) && i)
- i--;
- if (IS_SLASH (result[i]))
- i++;
- strcpy (&result[i], filename);
- free (filename);
- return result;
- }
- return filename;
- }
- char *
- output_name_from_input_name (char *name)
- {
- return expand_filename (NULL, name);
- }
- /* Modify the file name FNAME so that it fits the limitations of the
- underlying filesystem. In particular, truncate the file name as it
- would be truncated by the filesystem. We assume the result can
- never be longer than the original, otherwise we couldn't be sure we
- have enough space in the original string to modify it in place. */
- char *
- normalize_filename (char *fname)
- {
- int maxlen;
- char orig[PATH_MAX + 1];
- int i;
- char *lastdot, *p;
- #ifdef _PC_NAME_MAX
- maxlen = pathconf (fname, _PC_NAME_MAX);
- if (maxlen < 1)
- #endif
- maxlen = PATH_MAX;
- i = skip_directory_part (fname);
- if (fname[i] == '\0')
- return fname; /* only a directory name -- don't modify */
- strcpy (orig, fname + i);
- switch (maxlen)
- {
- case 12: /* MS-DOS 8+3 filesystem */
- if (orig[0] == '.') /* leading dots are not allowed */
- orig[0] = '_';
- lastdot = strrchr (orig, '.');
- if (!lastdot)
- lastdot = orig + strlen (orig);
- strncpy (fname + i, orig, lastdot - orig);
- for (p = fname + i;
- p < fname + i + (lastdot - orig) && p < fname + i + 8;
- p++)
- if (*p == '.')
- *p = '_';
- *p = '\0';
- if (*lastdot == '.')
- strncat (fname + i, lastdot, 4);
- break;
- case 14: /* old Unix systems with 14-char limitation */
- strcpy (fname + i, orig);
- if (strlen (fname + i) > 14)
- fname[i + 14] = '\0';
- break;
- default:
- strcpy (fname + i, orig);
- if (strlen (fname) > maxlen - 1)
- fname[maxlen - 1] = '\0';
- break;
- }
- return fname;
- }
- /* Delayed writing functions. A few of the commands
- needs to be handled at the end, namely @contents,
- @shortcontents, @printindex and @listoffloats.
- These functions take care of that. */
- static DELAYED_WRITE *delayed_writes = NULL;
- int handling_delayed_writes = 0;
- void
- register_delayed_write (char *delayed_command)
- {
- DELAYED_WRITE *new;
- if (!current_output_filename || !*current_output_filename)
- {
- /* Cannot register if we don't know what the output file is. */
- warning (_("`%s' omitted before output filename"), delayed_command);
- return;
- }
- if (STREQ (current_output_filename, "-"))
- {
- /* Do not register a new write if the output file is not seekable.
- Let the user know about it first, though. */
- warning (_("`%s' omitted since writing to stdout"), delayed_command);
- return;
- }
- /* Don't complain if the user is writing /dev/null, since surely they
- don't care, but don't register the delayed write, either. */
- if (FILENAME_CMP (current_output_filename, NULL_DEVICE) == 0
- || FILENAME_CMP (current_output_filename, ALSO_NULL_DEVICE) == 0)
- return;
-
- /* We need the HTML header in the output,
- to get a proper output_position. */
- if (!executing_string && html)
- output_head ();
- /* Get output_position updated. */
- flush_output ();
- new = xmalloc (sizeof (DELAYED_WRITE));
- new->command = xstrdup (delayed_command);
- new->filename = xstrdup (current_output_filename);
- new->input_filename = xstrdup (input_filename);
- new->position = output_position;
- new->calling_line = line_number;
- new->node = current_node ? xstrdup (current_node): "";
- new->node_order = node_order;
- new->index_order = index_counter;
- new->next = delayed_writes;
- delayed_writes = new;
- }
- void
- handle_delayed_writes (void)
- {
- DELAYED_WRITE *temp = (DELAYED_WRITE *) reverse_list
- ((GENERIC_LIST *) delayed_writes);
- int position_shift_amount, line_number_shift_amount;
- char *delayed_buf;
- handling_delayed_writes = 1;
- while (temp)
- {
- delayed_buf = find_and_load (temp->filename, 0);
- if (output_paragraph_offset > 0)
- {
- error (_("Output buffer not empty."));
- return;
- }
- if (!delayed_buf)
- {
- fs_error (temp->filename);
- return;
- }
- output_stream = fopen (temp->filename, "w");
- if (!output_stream)
- {
- fs_error (temp->filename);
- return;
- }
- if (fwrite (delayed_buf, 1, temp->position, output_stream) != temp->position)
- {
- fs_error (temp->filename);
- return;
- }
- {
- int output_position_at_start = output_position;
- int line_number_at_start = output_line_number;
- /* In order to make warnings and errors
- refer to the correct line number. */
- input_filename = temp->input_filename;
- line_number = temp->calling_line;
- execute_string ("%s", temp->command);
- flush_output ();
- /* Since the output file is modified, following delayed writes
- need to be updated by this amount. */
- position_shift_amount = output_position - output_position_at_start;
- line_number_shift_amount = output_line_number - line_number_at_start;
- }
- if (fwrite (delayed_buf + temp->position, 1,
- input_text_length - temp->position, output_stream)
- != input_text_length - temp->position
- || fclose (output_stream) != 0)
- fs_error (temp->filename);
- /* Done with the buffer. */
- free (delayed_buf);
- /* Update positions in tag table for nodes that are defined after
- the line this delayed write is registered. */
- if (!html && !xml)
- {
- TAG_ENTRY *node;
- for (node = tag_table; node; node = node->next_ent)
- if (node->order > temp->node_order)
- node->position += position_shift_amount;
- }
- /* Something similar for the line numbers in all of the defined
- indices. */
- {
- int i;
- for (i = 0; i < defined_indices; i++)
- if (name_index_alist[i])
- {
- char *name = ((INDEX_ALIST *) name_index_alist[i])->name;
- INDEX_ELT *index;
- for (index = index_list (name); index; index = index->next)
- if ((no_headers || STREQ (index->node, temp->node))
- && index->entry_number > temp->index_order)
- index->output_line += line_number_shift_amount;
- }
- }
- /* Shift remaining delayed positions
- by the length of this write. */
- {
- DELAYED_WRITE *future_write = temp->next;
- while (future_write)
- {
- if (STREQ (temp->filename, future_write->filename))
- future_write->position += position_shift_amount;
- future_write = future_write->next;
- }
- }
- temp = temp->next;
- }
- }
|