+void
+name_term (void)
+{
+ 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)
+ {
+ if (name_buffer_length * 2 < name_buffer_length)
+ xalloc_die ();
+ name_buffer_length *= 2;
+ 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)
+ {
+ if (name_buffer_length * 2 < name_buffer_length)
+ xalloc_die ();
+ name_buffer_length *= 2;
+ 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_index == names)
+ {
+ if (! name_file)
+ break;
+ if (! read_name_from_file ())
+ break;
+ }
+ else
+ {
+ size_t source_len;
+ source = name_array[name_index++];
+ source_len = strlen (source);
+ if (name_buffer_length < source_len)
+ {
+ do
+ {
+ name_buffer_length *= 2;
+ if (! name_buffer_length)
+ xalloc_die ();
+ }
+ while (name_buffer_length < source_len);
+
+ free (name_buffer);
+ 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 && ISSLASH (*cursor))
+ *cursor-- = '\0';
+
+ if (chdir_flag)
+ {
+ if (chdir (name_buffer) < 0)
+ chdir_fatal (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 0;
+}
+
+/* Close the name file, if any. */
+void
+name_close (void)
+{
+ if (name_file && name_file != stdin)
+ if (fclose (name_file) != 0)
+ close_error (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_size;
+
+ char const *name;
+
+ if (same_order_option)
+ {
+ static int change_dir;
+
+ if (allocated_size == 0)
+ {
+ allocated_size = offsetof (struct name, name) + NAME_FIELD_SIZE + 1;
+ buffer = xmalloc (allocated_size);
+ /* FIXME: This memset is overkill, and ugly... */
+ memset (buffer, 0, allocated_size);
+ }
+
+ while ((name = name_next (0)) && strcmp (name, "-C") == 0)
+ {
+ char const *dir = name_next (0);
+ if (! dir)
+ FATAL_ERROR ((0, 0, _("Missing file name after -C")));
+ change_dir = chdir_arg (xstrdup (dir));
+ }
+
+ if (name)
+ {
+ size_t needed_size;
+ buffer->length = strlen (name);
+ needed_size = offsetof (struct name, name) + buffer->length + 1;
+ if (allocated_size < needed_size)
+ {
+ do
+ {
+ allocated_size *= 2;
+ if (! allocated_size)
+ xalloc_die ();
+ }
+ while (allocated_size < needed_size);
+
+ buffer = xrealloc (buffer, allocated_size);
+ }
+ buffer->change_dir = change_dir;
+ strcpy (buffer->name, name);
+ buffer->next = 0;
+ buffer->found = 0;
+
+ namelist = buffer;
+ nametail = &namelist->next;
+ }
+ }
+ else
+ {
+ /* Non sorted names -- read them all in. */
+ int change_dir = 0;
+
+ for (;;)
+ {
+ int change_dir0 = change_dir;
+ while ((name = name_next (0)) && strcmp (name, "-C") == 0)
+ {
+ char const *dir = name_next (0);
+ if (! dir)
+ FATAL_ERROR ((0, 0, _("Missing file name after -C")));
+ change_dir = chdir_arg (xstrdup (dir));
+ }
+ if (name)
+ addname (name, change_dir);
+ else
+ {
+ if (change_dir != change_dir0)
+ addname (0, change_dir);
+ break;
+ }
+ }
+ }
+}
+
+/* Add a name to the namelist. */
+struct name *
+addname (char const *string, int change_dir)
+{
+ size_t length = string ? strlen (string) : 0;
+ struct name *name = xmalloc (offsetof (struct name, name) + length + 1);
+
+ if (string)
+ {
+ name->fake = 0;
+ strcpy (name->name, string);
+ }
+ else
+ {
+ name->fake = 1;
+
+ /* FIXME: This initialization (and the byte of memory that it
+ initializes) is probably not needed, but we are currently in
+ bug-fix mode so we'll leave it in for now. */
+ name->name[0] = 0;
+ }
+
+ name->next = 0;
+ name->length = length;
+ name->found = 0;
+ name->regexp = 0; /* assume not a regular expression */
+ name->firstch = 1; /* assume first char is literal */
+ name->change_dir = change_dir;
+ name->dir_contents = 0;
+
+ if (string && is_pattern (string))
+ {
+ name->regexp = 1;
+ if (string[0] == '*' || string[0] == '[' || string[0] == '?')
+ name->firstch = 0;
+ }
+
+ *nametail = name;
+ nametail = &name->next;
+ return name;
+}