+ free (name_buffer);
+ free (name_array);
+}
+
+/*---------------------------------------------------------------------.
+| Read the next filename from name_file and null-terminate it. Put it |
+| into name_buffer, reallocating and adjusting name_buffer_length if |
+| necessary. Return 0 at end of file, 1 otherwise. |
+`---------------------------------------------------------------------*/
+
+static int
+read_name_from_file (void)
+{
+ int character;
+ size_t counter = 0;
+
+ /* FIXME: getc may be called even if character was EOF the last time here. */
+
+ /* FIXME: This + 2 allocation might serve no purpose. */
+
+ while (character = getc (name_file),
+ character != EOF && character != filename_terminator)
+ {
+ if (counter == name_buffer_length)
+ {
+ name_buffer_length += NAME_FIELD_SIZE;
+ name_buffer = xrealloc (name_buffer, name_buffer_length + 2);
+ }
+ name_buffer[counter++] = character;
+ }
+
+ if (counter == 0 && character == EOF)
+ return 0;
+
+ if (counter == name_buffer_length)
+ {
+ name_buffer_length += NAME_FIELD_SIZE;
+ name_buffer = xrealloc (name_buffer, name_buffer_length + 2);
+ }
+ name_buffer[counter] = '\0';
+
+ return 1;
+}
+
+/*------------------------------------------------------------------------.
+| Get the next name from ARGV or the file of names. Result is in static |
+| storage and can't be relied upon across two calls. |
+| |
+| If CHANGE_DIRS is true, treat a filename of the form "-C" as meaning |
+| that the next filename is the name of a directory to change to. If |
+| `filename_terminator' is NUL, CHANGE_DIRS is effectively always false. |
+`------------------------------------------------------------------------*/
+
+char *
+name_next (int change_dirs)
+{
+ const char *source;
+ char *cursor;
+ int chdir_flag = 0;
+
+ if (filename_terminator == '\0')
+ change_dirs = 0;
+
+ while (1)
+ {
+ /* Get a name, either from file or from saved arguments. */
+
+ if (name_file)
+ {
+ if (!read_name_from_file ())
+ break;
+ }
+ else
+ {
+ if (name_index == names)
+ break;
+
+ source = name_array[name_index++];
+ if (strlen (source) > name_buffer_length)
+ {
+ free (name_buffer);
+ name_buffer_length = strlen (source);
+ name_buffer = xmalloc (name_buffer_length + 2);
+ }
+ strcpy (name_buffer, source);
+ }
+
+ /* Zap trailing slashes. */
+
+ cursor = name_buffer + strlen (name_buffer) - 1;
+ while (cursor > name_buffer && *cursor == '/')
+ *cursor-- = '\0';
+
+ if (chdir_flag)
+ {
+ if (chdir (name_buffer) < 0)
+ FATAL_ERROR ((0, errno, _("Cannot change to directory %s"),
+ name_buffer));
+ chdir_flag = 0;
+ }
+ else if (change_dirs && strcmp (name_buffer, "-C") == 0)
+ chdir_flag = 1;
+ else
+ {
+ unquote_string (name_buffer);
+ return name_buffer;
+ }
+ }
+
+ /* No more names in file. */
+
+ if (name_file && chdir_flag)
+ FATAL_ERROR ((0, 0, _("Missing file name after -C")));
+
+ return NULL;
+}
+
+/*------------------------------.
+| Close the name file, if any. |
+`------------------------------*/
+
+void
+name_close (void)
+{
+ if (name_file != NULL && name_file != stdin)
+ if (fclose (name_file) == EOF)
+ ERROR ((0, errno, "%s", name_buffer));
+}
+
+/*-------------------------------------------------------------------------.
+| Gather names in a list for scanning. Could hash them later if we really |
+| care. |
+| |
+| If the names are already sorted to match the archive, we just read them |
+| one by one. name_gather reads the first one, and it is called by |
+| name_match as appropriate to read the next ones. At EOF, the last name |
+| read is just left in the buffer. This option lets users of small |
+| machines extract an arbitrary number of files by doing "tar t" and |
+| editing down the list of files. |
+`-------------------------------------------------------------------------*/
+
+void
+name_gather (void)
+{
+ /* Buffer able to hold a single name. */
+ static struct name *buffer;
+ static size_t allocated_length = 0;
+
+ char *name;
+
+ if (same_order_option)
+ {
+ if (allocated_length == 0)
+ {
+ allocated_length = sizeof (struct name) + NAME_FIELD_SIZE;
+ buffer = (struct name *) xmalloc (allocated_length);
+ /* FIXME: This memset is overkill, and ugly... */
+ memset (buffer, 0, allocated_length);
+ }
+ name = name_next (0);
+ if (name)
+ {
+ if (strcmp (name, "-C") == 0)
+ {
+ char *copy = xstrdup (name_next (0));
+
+ name = name_next (0);
+ if (!name)
+ FATAL_ERROR ((0, 0, _("Missing file name after -C")));
+ buffer->change_dir = copy;
+ }
+ buffer->length = strlen (name);
+ if (sizeof (struct name) + buffer->length >= allocated_length)
+ {
+ allocated_length = sizeof (struct name) + buffer->length;
+ buffer = (struct name *) xrealloc (buffer, allocated_length);
+ }
+ strncpy (buffer->name, name, (size_t) buffer->length);
+ buffer->name[buffer->length] = 0;
+ buffer->next = NULL;
+ buffer->found = 0;
+
+ /* FIXME: Poorly named globals, indeed... */
+ namelist = buffer;
+ namelast = namelist;
+ }
+ return;
+ }
+
+ /* Non sorted names -- read them all in. */
+
+ while (name = name_next (0), name)
+ addname (name);
+}
+
+/*-----------------------------.
+| Add a name to the namelist. |
+`-----------------------------*/
+
+void
+addname (const char *string)
+{
+ /* FIXME: This is ugly. How is memory managed? */
+ static char *chdir_name = NULL;
+
+ struct name *name;
+ size_t length;
+
+ if (strcmp (string, "-C") == 0)
+ {
+ chdir_name = xstrdup (name_next (0));
+ string = name_next (0);
+ if (!chdir_name)
+ FATAL_ERROR ((0, 0, _("Missing file name after -C")));
+
+ if (chdir_name[0] != '/')
+ {
+ char *path = xmalloc (PATH_MAX);
+
+ /* FIXME: Shouldn't we use xgetcwd? */
+#if HAVE_GETCWD
+ if (!getcwd (path, PATH_MAX))
+ FATAL_ERROR ((0, 0, _("Could not get current directory")));
+#else
+ char *getwd ();
+
+ if (!getwd (path))
+ FATAL_ERROR ((0, 0, _("Could not get current directory: %s"),
+ path));