+\f
+static bool files_from_option; /* When set, tar will not refuse to create
+ empty archives */
+
+/* Default density numbers for [0-9][lmh] device specifications */
+
+#if defined DEVICE_PREFIX && !defined DENSITY_LETTER
+# ifndef LOW_DENSITY_NUM
+# define LOW_DENSITY_NUM 0
+# endif
+
+# ifndef MID_DENSITY_NUM
+# define MID_DENSITY_NUM 8
+# endif
+
+# ifndef HIGH_DENSITY_NUM
+# define HIGH_DENSITY_NUM 16
+# endif
+#endif
+
+\f
+static char *
+tar_help_filter (int key, const char *text, void *input)
+{
+ struct obstack stk;
+ char *s;
+
+ switch (key)
+ {
+ default:
+ s = (char*) text;
+ break;
+
+ case 'j':
+ s = xasprintf (_("filter the archive through %s"), BZIP2_PROGRAM);
+ break;
+
+ case 'z':
+ s = xasprintf (_("filter the archive through %s"), GZIP_PROGRAM);
+ break;
+
+ case 'Z':
+ s = xasprintf (_("filter the archive through %s"), COMPRESS_PROGRAM);
+ break;
+
+ case LZIP_OPTION:
+ s = xasprintf (_("filter the archive through %s"), LZIP_PROGRAM);
+ break;
+
+ case LZMA_OPTION:
+ s = xasprintf (_("filter the archive through %s"), LZMA_PROGRAM);
+ break;
+
+ case LZOP_OPTION:
+ s = xasprintf (_("filter the archive through %s"), LZOP_PROGRAM);
+
+ case 'J':
+ s = xasprintf (_("filter the archive through %s"), XZ_PROGRAM);
+ break;
+
+ case ARGP_KEY_HELP_EXTRA:
+ {
+ const char *tstr;
+
+ obstack_init (&stk);
+ tstr = _("Valid arguments for the --quoting-style option are:");
+ obstack_grow (&stk, tstr, strlen (tstr));
+ obstack_grow (&stk, "\n\n", 2);
+ tar_list_quoting_styles (&stk, " ");
+ tstr = _("\n*This* tar defaults to:\n");
+ obstack_grow (&stk, tstr, strlen (tstr));
+ s = format_default_settings ();
+ obstack_grow (&stk, s, strlen (s));
+ obstack_1grow (&stk, '\n');
+ obstack_1grow (&stk, 0);
+ s = xstrdup (obstack_finish (&stk));
+ obstack_free (&stk, NULL);
+ }
+ }
+ return s;
+}
+\f
+static char *
+expand_pax_option (struct tar_args *targs, const char *arg)
+{
+ struct obstack stk;
+ char *res;
+
+ obstack_init (&stk);
+ while (*arg)
+ {
+ size_t seglen = strcspn (arg, ",");
+ char *p = memchr (arg, '=', seglen);
+ if (p)
+ {
+ size_t len = p - arg + 1;
+ obstack_grow (&stk, arg, len);
+ len = seglen - len;
+ for (++p; *p && isspace ((unsigned char) *p); p++)
+ len--;
+ if (*p == '{' && p[len-1] == '}')
+ {
+ struct timespec ts;
+ char *tmp = xmalloc (len);
+ memcpy (tmp, p + 1, len-2);
+ tmp[len-2] = 0;
+ if (get_date_or_file (targs, "--pax-option", tmp, &ts) == 0)
+ {
+ char buf[TIMESPEC_STRSIZE_BOUND];
+ char const *s = code_timespec (ts, buf);
+ obstack_grow (&stk, s, strlen (s));
+ }
+ else
+ obstack_grow (&stk, p, len);
+ free (tmp);
+ }
+ else
+ obstack_grow (&stk, p, len);
+ }
+ else
+ obstack_grow (&stk, arg, seglen);
+
+ arg += seglen;
+ if (*arg)
+ {
+ obstack_1grow (&stk, *arg);
+ arg++;
+ }
+ }
+ obstack_1grow (&stk, 0);
+ res = xstrdup (obstack_finish (&stk));
+ obstack_free (&stk, NULL);
+ return res;
+}
+
+\f
+static uintmax_t
+parse_owner_group (char *arg, uintmax_t field_max, char const **name_option)
+{
+ uintmax_t u = UINTMAX_MAX;
+ char *end;
+ char const *name = 0;
+ char const *invalid_num = 0;
+ char *colon = strchr (arg, ':');
+
+ if (colon)
+ {
+ char const *num = colon + 1;
+ *colon = '\0';
+ if (*arg)
+ name = arg;
+ if (num && (! (xstrtoumax (num, &end, 10, &u, "") == LONGINT_OK
+ && u <= field_max)))
+ invalid_num = num;
+ }
+ else
+ {
+ uintmax_t u1;
+ switch ('0' <= *arg && *arg <= '9'
+ ? xstrtoumax (arg, &end, 10, &u1, "")
+ : LONGINT_INVALID)
+ {
+ default:
+ name = arg;
+ break;
+
+ case LONGINT_OK:
+ if (u1 <= field_max)
+ {
+ u = u1;
+ break;
+ }
+ /* Fall through. */
+ case LONGINT_OVERFLOW:
+ invalid_num = arg;
+ break;
+ }
+ }
+
+ if (invalid_num)
+ FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (invalid_num),
+ _("Invalid owner or group ID")));
+ if (name)
+ *name_option = name;
+ return u;
+}
+
+#define TAR_SIZE_SUFFIXES "bBcGgkKMmPTtw"
+
+/* Either NL or NUL, as decided by the --null option. */
+static char filename_terminator;
+
+static char const *const sort_mode_arg[] = {
+ "none",
+ "name",
+#if D_INO_IN_DIRENT
+ "inode",
+#endif
+ NULL
+};
+
+static int sort_mode_flag[] = {
+ SAVEDIR_SORT_NONE,
+ SAVEDIR_SORT_NAME,
+#if D_INO_IN_DIRENT
+ SAVEDIR_SORT_INODE
+#endif
+};
+
+ARGMATCH_VERIFY (sort_mode_arg, sort_mode_flag);
+