+uname_to_uid (char uname[UNAME_FIELD_SIZE], uid_t *uidp)
+{
+ struct passwd *passwd;
+
+ if (!cached_uname[0]
+ || uname[0] != cached_uname[0]
+ || strncmp (uname, cached_uname, UNAME_FIELD_SIZE) != 0)
+ {
+ passwd = getpwnam (uname);
+ if (passwd)
+ {
+ cached_uid = passwd->pw_uid;
+ strncpy (cached_uname, uname, UNAME_FIELD_SIZE);
+ }
+ else
+ return 0;
+ }
+ *uidp = cached_uid;
+ return 1;
+}
+
+/*-------------------------------------------------------------------------.
+| Given GNAME, set the corresponding GID and return 1, or else, return 0. |
+`-------------------------------------------------------------------------*/
+
+int
+gname_to_gid (char gname[GNAME_FIELD_SIZE], gid_t *gidp)
+{
+ struct group *group;
+
+ if (!cached_gname[0]
+ || gname[0] != cached_gname[0]
+ || strncmp (gname, cached_gname, GNAME_FIELD_SIZE) != 0)
+ {
+ group = getgrnam (gname);
+ if (group)
+ {
+ cached_gid = group->gr_gid;
+ strncpy (cached_gname, gname, GNAME_FIELD_SIZE);
+ }
+ else
+ return 0;
+ }
+ *gidp = cached_gid;
+ return 1;
+}
+\f
+/* Names from the command call. */
+
+static const char **name_array; /* store an array of names */
+static int allocated_names; /* how big is the array? */
+static int names; /* how many entries does it have? */
+static int name_index = 0; /* how many of the entries have we scanned? */
+
+/*------------------------.
+| Initialize structures. |
+`------------------------*/
+
+void
+init_names (void)
+{
+ allocated_names = 10;
+ name_array = (const char **)
+ xmalloc (sizeof (const char *) * allocated_names);
+ names = 0;
+}
+
+/*--------------------------------------------------------------.
+| Add NAME at end of name_array, reallocating it as necessary. |
+`--------------------------------------------------------------*/
+
+void
+name_add (const char *name)
+{
+ if (names == allocated_names)
+ {
+ allocated_names *= 2;
+ name_array = (const char **)
+ xrealloc (name_array, sizeof (const char *) * allocated_names);
+ }
+ name_array[names++] = name;
+}
+\f
+/* Names from external name file. */
+
+static FILE *name_file; /* file to read names from */
+static char *name_buffer; /* buffer to hold the current file name */
+static size_t name_buffer_length; /* allocated length of name_buffer */
+
+/*---.
+| ? |
+`---*/
+
+/* FIXME: I should better check more closely. It seems at first glance that
+ is_pattern is only used when reading a file, and ignored for all
+ command line arguments. */
+
+static inline int
+is_pattern (const char *string)
+{
+ return strchr (string, '*') || strchr (string, '[') || strchr (string, '?');
+}
+
+/*-----------------------------------------------------------------------.
+| Set up to gather file names for tar. They can either come from a file |
+| or were saved from decoding arguments. |
+`-----------------------------------------------------------------------*/
+
+void
+name_init (int argc, char *const *argv)
+{
+ name_buffer = xmalloc (NAME_FIELD_SIZE + 2);
+ name_buffer_length = NAME_FIELD_SIZE;
+
+ if (files_from_option)
+ {
+ if (!strcmp (files_from_option, "-"))
+ {
+ request_stdin ("-T");
+ name_file = stdin;
+ }
+ else if (name_file = fopen (files_from_option, "r"), !name_file)
+ FATAL_ERROR ((0, errno, _("Cannot open file %s"), files_from_option));
+ }
+}
+
+/*---.
+| ? |
+`---*/
+
+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)
+ {
+ 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
+#if 0
+ if (!exclude_option || !check_exclude (name_buffer))
+#endif
+ {
+ 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;