+gname_to_gid (char const *gname, gid_t *gidp)
+{
+ struct group *group;
+
+ if (cached_no_such_gname
+ && strcmp (gname, cached_no_such_gname) == 0)
+ return 0;
+
+ if (!cached_gname
+ || gname[0] != cached_gname[0]
+ || strcmp (gname, cached_gname) != 0)
+ {
+ group = getgrnam (gname);
+ if (group)
+ {
+ cached_gid = group->gr_gid;
+ assign_string (&cached_gname, gname);
+ }
+ else
+ {
+ assign_string (&cached_no_such_gname, gname);
+ return 0;
+ }
+ }
+ *gidp = cached_gid;
+ return 1;
+}
+
+\f
+/* Names from the command call. */
+
+static struct name *namelist; /* first name in list, if any */
+static struct name **nametail = &namelist; /* end of name list */
+
+/* File name arguments are processed in two stages: first a
+ name_array (see below) is filled, then the names from it
+ are moved into the namelist.
+
+ This awkward process is needed only to implement --same-order option,
+ which is meant to help process large archives on machines with
+ limited memory. With this option on, namelist contains at most one
+ entry, which diminishes the memory consumption.
+
+ However, I very much doubt if we still need this -- Sergey */
+
+/* A name_array element contains entries of three types: */
+
+#define NELT_NAME 0 /* File name */
+#define NELT_CHDIR 1 /* Change directory request */
+#define NELT_FMASK 2 /* Change fnmatch options request */
+
+struct name_elt /* A name_array element. */
+{
+ char type; /* Element type, see NELT_* constants above */
+ union
+ {
+ const char *name; /* File or directory name */
+ int matching_flags;/* fnmatch options if type == NELT_FMASK */
+ } v;
+};
+
+static struct name_elt *name_array; /* store an array of names */
+static size_t allocated_names; /* how big is the array? */
+static size_t names; /* how many entries does it have? */
+static size_t name_index; /* how many of the entries have we scanned? */
+
+/* Check the size of name_array, reallocating it as necessary. */
+static void
+check_name_alloc ()
+{
+ if (names == allocated_names)
+ {
+ if (allocated_names == 0)
+ allocated_names = 10; /* Set initial allocation */
+ name_array = x2nrealloc (name_array, &allocated_names,
+ sizeof (name_array[0]));
+ }
+}
+
+/* Add to name_array the file NAME with fnmatch options MATCHING_FLAGS */
+void
+name_add_name (const char *name, int matching_flags)
+{
+ static int prev_flags = 0; /* FIXME: Or EXCLUDE_ANCHORED? */
+ struct name_elt *ep;
+
+ check_name_alloc ();
+ ep = &name_array[names++];
+ if (prev_flags != matching_flags)
+ {
+ ep->type = NELT_FMASK;
+ ep->v.matching_flags = matching_flags;
+ prev_flags = matching_flags;
+ check_name_alloc ();
+ ep = &name_array[names++];
+ }
+ ep->type = NELT_NAME;
+ ep->v.name = name;
+}
+
+/* Add to name_array a chdir request for the directory NAME */
+void
+name_add_dir (const char *name)
+{
+ struct name_elt *ep;
+ check_name_alloc ();
+ ep = &name_array[names++];
+ ep->type = NELT_CHDIR;
+ ep->v.name = name;
+}
+
+\f
+/* Names from external name file. */
+
+static char *name_buffer; /* buffer to hold the current file name */
+static size_t name_buffer_length; /* allocated length of name_buffer */
+
+/* Set up to gather file names for tar. They can either come from a
+ file or were saved from decoding arguments. */
+void
+name_init (void)
+{
+ name_buffer = xmalloc (NAME_FIELD_SIZE + 2);
+ name_buffer_length = NAME_FIELD_SIZE;
+}
+
+void
+name_term (void)
+{
+ free (name_buffer);
+ free (name_array);
+}
+
+static int matching_flags; /* exclude_fnmatch options */
+
+/* Get the next NELT_NAME element from name_array. Result is in
+ static storage and can't be relied upon across two calls.
+
+ If CHANGE_DIRS is true, treat any entries of type NELT_CHDIR as
+ the request to change to the given directory. If filename_terminator
+ is NUL, CHANGE_DIRS is effectively always false.
+
+ Entries of type NELT_FMASK cause updates of the matching_flags
+ value. */
+struct name_elt *
+name_next_elt (int change_dirs)
+{
+ static struct name_elt entry;
+ const char *source;
+ char *cursor;
+
+ if (filename_terminator == '\0')
+ change_dirs = 0;
+
+ while (name_index != names)
+ {
+ struct name_elt *ep;
+ size_t source_len;
+
+ ep = &name_array[name_index++];
+ if (ep->type == NELT_FMASK)
+ {
+ matching_flags = ep->v.matching_flags;
+ continue;
+ }
+
+ source = ep->v.name;
+ 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 (change_dirs && ep->type == NELT_CHDIR)
+ {
+ if (chdir (name_buffer) < 0)
+ chdir_fatal (name_buffer);
+ }
+ else
+ {
+ if (unquote_option)
+ unquote_string (name_buffer);
+ if (incremental_option)
+ register_individual_file (name_buffer);
+ entry.type = ep->type;
+ entry.v.name = name_buffer;
+ return &entry;
+ }
+ }
+
+ return NULL;
+}
+
+const char *
+name_next (int change_dirs)
+{
+ struct name_elt *nelt = name_next_elt (change_dirs);
+ return nelt ? nelt->v.name : NULL;
+}
+
+/* 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;
+
+ struct name_elt *ep;
+
+ 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 ((ep = name_next_elt (0)) && ep->type == NELT_CHDIR)
+ change_dir = chdir_arg (xstrdup (ep->v.name));
+
+ if (ep)
+ {
+ size_t needed_size;
+
+ buffer->length = strlen (ep->v.name);
+ needed_size = offsetof (struct name, name) + buffer->length + 1;
+ if (allocated_size < needed_size)
+ {
+ do
+ {
+ allocated_size *= 2;
+ if (! allocated_size)
+ xalloc_die ();