#define obstack_chunk_free free
#include <obstack.h>
+#include <fnmatch.h>
+
bool xheader_protected_pattern_p (const char *pattern);
bool xheader_protected_keyword_p (const char *keyword);
-/* Number of the global headers written so far. Not used yet */
+/* Used by xheader_finish() */
+static void code_string (char const *string, char const *keyword,
+ struct xheader *xhdr);
+static void extended_header_init ();
+
+/* Number of global headers written so far. */
static size_t global_header_count;
+/* FIXME: Possibly it should be reset after changing the volume.
+ POSIX %n specification says that it is expanded to the sequence
+ number of current global header in *the* archive. However, for
+ multi-volume archives this will yield duplicate header names
+ in different volumes, which I'd like to avoid. The best way
+ to solve this would be to use per-archive header count as required
+ by POSIX *and* set globexthdr.name to, say,
+ $TMPDIR/GlobalHead.%p.$NUMVOLUME.%n.
+
+ However it should wait until buffer.c is finally rewritten */
\f
/* Keyword options */
/* List of keyword patterns set by delete= option */
static struct keyword_list *keyword_pattern_list;
+
/* List of keyword/value pairs set by `keyword=value' option */
static struct keyword_list *keyword_global_override_list;
+
/* List of keyword/value pairs set by `keyword:=value' option */
static struct keyword_list *keyword_override_list;
+
+/* List of keyword/value pairs decoded from the last 'g' type header */
+static struct keyword_list *global_header_override_list;
+
/* Template for the name field of an 'x' type header */
static char *exthdr_name;
+
/* Template for the name field of a 'g' type header */
static char *globexthdr_name;
}
void
-xheader_list_append (struct keyword_list **root, char *kw, char *value)
+xheader_list_append (struct keyword_list **root, char const *kw,
+ char const *value)
{
struct keyword_list *kp = xmalloc (sizeof *kp);
kp->pattern = xstrdup (kw);
*root = kp;
}
+void
+xheader_list_destroy (struct keyword_list **root)
+{
+ if (root)
+ {
+ struct keyword_list *kw = *root;
+ while (kw)
+ {
+ struct keyword_list *next = kw->next;
+ free (kw->pattern);
+ free (kw->value);
+ free (kw);
+ kw = next;
+ }
+ *root = NULL;
+ }
+}
+
+
void
xheader_set_single_keyword (char *kw)
{
- USAGE_ERROR ((0, 0, "Keyword %s is unknown or not yet imlemented", kw));
+ USAGE_ERROR ((0, 0, _("Keyword %s is unknown or not yet imlemented"), kw));
}
void
if (strcmp (kw, "delete") == 0)
{
if (xheader_protected_pattern_p (p))
- USAGE_ERROR ((0, 0, "Pattern %s cannot be used", p));
+ USAGE_ERROR ((0, 0, _("Pattern %s cannot be used"), p));
xheader_list_append (&keyword_pattern_list, p, NULL);
}
else if (strcmp (kw, "exthdr.name") == 0)
else
{
if (xheader_protected_keyword_p (kw))
- USAGE_ERROR ((0, 0, "Keyword %s cannot be overridden", kw));
+ USAGE_ERROR ((0, 0, _("Keyword %s cannot be overridden"), kw));
if (global)
xheader_list_append (&keyword_global_override_list, kw, p);
else
}
}
+static void
+to_decimal (uintmax_t value, char *where, size_t size)
+{
+ size_t i = 0, j;
+
+ where[i++] = 0;
+ do
+ {
+ where[i++] = '0' + value % 10;
+ value /= 10;
+ }
+ while (i < size && value);
+ for (j = 0, i--; j < i; j++, i--)
+ {
+ char c = where[j];
+ where[j] = where[i];
+ where[i] = c;
+ }
+}
+
/*
string Includes: Replaced By:
%d The directory name of the file,
char pidbuf[64];
char nbuf[64];
- for (p = exthdr_name; *p && (p = strchr (p, '%')); )
+ for (p = fmt; *p && (p = strchr (p, '%')); )
{
switch (p[1])
{
break;
case 'd':
- dirname = safer_name_suffix (dir_name (st->orig_file_name), false);
- len += strlen (dirname) - 1;
+ if (st)
+ {
+ dirname = safer_name_suffix (dir_name (st->orig_file_name),
+ false);
+ len += strlen (dirname) - 1;
+ }
break;
case 'f':
- basename = base_name (st->orig_file_name);
- len += strlen (basename) - 1;
+ if (st)
+ {
+ basename = base_name (st->orig_file_name);
+ len += strlen (basename) - 1;
+ }
break;
case 'p':
- snprintf (pidbuf, sizeof pidbuf, "%lu",
- (unsigned long) getpid ());
+ to_decimal (getpid (), pidbuf, sizeof pidbuf);
len += strlen (pidbuf) - 1;
break;
case 'n':
if (allow_n)
{
- snprintf (nbuf, sizeof nbuf, "%lu",
- (unsigned long) global_header_count + 1);
+ to_decimal (global_header_count + 1, pidbuf, sizeof pidbuf);
len += strlen (nbuf) - 1;
}
break;
break;
case 'd':
- q = stpcpy (q, dirname);
+ if (dirname)
+ q = stpcpy (q, dirname);
p += 2;
break;
case 'f':
- q = stpcpy (q, basename);
+ if (basename)
+ q = stpcpy (q, basename);
p += 2;
break;
char *
xheader_xhdr_name (struct tar_stat_info *st)
{
- /* FIXME: POSIX requires the default name to be '%d/PaxHeaders.%p/%f' */
if (!exthdr_name)
- return xstrdup ("././@PaxHeader");
+ assign_string (&exthdr_name, "%d/PaxHeaders.%p/%f");
return xheader_format_name (st, exthdr_name, false);
}
#define GLOBAL_HEADER_TEMPLATE "/GlobalHead.%p.%n"
char *
-xheader_ghdr_name (struct tar_stat_info *st)
+xheader_ghdr_name ()
{
if (!globexthdr_name)
{
strcat(globexthdr_name, GLOBAL_HEADER_TEMPLATE);
}
- return xheader_format_name (st, globexthdr_name, true);
+ return xheader_format_name (NULL, globexthdr_name, true);
+}
+
+void
+xheader_write (char type, char *name, struct xheader *xhdr)
+{
+ union block *header;
+ size_t size;
+ char *p;
+
+ size = xhdr->size;
+ header = start_private_header (name, size);
+ header->header.typeflag = type;
+
+ simple_finish_header (header);
+
+ p = xhdr->buffer;
+
+ do
+ {
+ size_t len;
+
+ header = find_next_block ();
+ len = BLOCKSIZE;
+ if (len > size)
+ len = size;
+ memcpy (header->buffer, p, len);
+ if (len < BLOCKSIZE)
+ memset (header->buffer + len, 0, BLOCKSIZE - len);
+ p += len;
+ size -= len;
+ set_next_block_after (header);
+ }
+ while (size > 0);
+ xheader_destroy (xhdr);
+}
+
+void
+xheader_write_global ()
+{
+ char *name;
+ struct keyword_list *kp;
+
+ if (!keyword_global_override_list)
+ return;
+
+ extended_header_init ();
+ for (kp = keyword_global_override_list; kp; kp = kp->next)
+ code_string (kp->value, kp->pattern, &extended_header);
+ xheader_finish (&extended_header);
+ xheader_write (XGLTYPE, name = xheader_ghdr_name (),
+ &extended_header);
+ free (name);
+ global_header_count++;
}
\f
/* General Interface */
-/* Used by xheader_finish() */
-static void code_string (char const *string, char const *keyword,
- struct xheader *xhdr);
-
struct xhdr_tab
{
char const *keyword;
record.
Returns true on success, false otherwise. */
static bool
-decode_record (char **p, struct tar_stat_info *st)
+decode_record (char **p,
+ void (*handler) (void *, char const *, char const *),
+ void *data)
{
size_t len;
char const *keyword;
- char *eqp;
char *start = *p;
- struct xhdr_tab const *t;
+ char endc;
if (**p == 0)
return false;
return false;
}
- eqp = *p;
**p = 0;
- if (xheader_keyword_deleted_p (keyword)
- || xheader_keyword_override_p (keyword))
- return true;
- t = locate_handler (keyword);
- if (t)
- {
- char endc;
- char *value;
+ endc = start[len-1];
+ start[len-1] = 0;
- value = ++*p;
+ handler (data, keyword, *p + 1);
- endc = start[len-1];
- start[len-1] = 0;
- t->decoder (st, value);
- start[len-1] = endc;
- }
- *eqp = '=';
+ start[len-1] = endc;
+ **p = '=';
*p = &start[len];
return true;
}
}
}
+static void
+decx (void *data, char const *keyword, char const *value)
+{
+ struct xhdr_tab const *t;
+ struct tar_stat_info *st = data;
+
+ if (xheader_keyword_deleted_p (keyword)
+ || xheader_keyword_override_p (keyword))
+ return;
+
+ t = locate_handler (keyword);
+ if (t)
+ t->decoder (st, value);
+}
+
void
xheader_decode (struct tar_stat_info *st)
{
- char *p = extended_header.buffer + BLOCKSIZE;
- char *endp = &extended_header.buffer[extended_header.size-1];
-
run_override_list (keyword_global_override_list, st);
+ run_override_list (global_header_override_list, st);
- while (p < endp)
- if (!decode_record (&p, st))
- break;
-
+ if (extended_header.size)
+ {
+ char *p = extended_header.buffer + BLOCKSIZE;
+ char *endp = &extended_header.buffer[extended_header.size-1];
+
+ while (p < endp)
+ if (!decode_record (&p, decx, st))
+ break;
+ }
run_override_list (keyword_override_list, st);
}
+static void
+decg (void *data, char const *keyword, char const *value)
+{
+ struct keyword_list **kwl = data;
+ xheader_list_append (kwl, keyword, value);
+}
+
+void
+xheader_decode_global ()
+{
+ if (extended_header.size)
+ {
+ char *p = extended_header.buffer + BLOCKSIZE;
+ char *endp = &extended_header.buffer[extended_header.size-1];
+
+ xheader_list_destroy (&global_header_override_list);
+ while (p < endp)
+ if (!decode_record (&p, decg, &global_header_override_list))
+ break;
+ }
+}
+
+static void
+extended_header_init ()
+{
+ if (!extended_header.stk)
+ {
+ extended_header.stk = xmalloc (sizeof *extended_header.stk);
+ obstack_init (extended_header.stk);
+ }
+}
+
void
xheader_store (char const *keyword, struct tar_stat_info const *st, void *data)
{
struct xhdr_tab const *t;
- char *value;
if (extended_header.buffer)
return;
if (xheader_keyword_deleted_p (keyword)
|| xheader_keyword_override_p (keyword))
return;
- if (!extended_header.stk)
- {
- extended_header.stk = xmalloc (sizeof *extended_header.stk);
- obstack_init (extended_header.stk);
- }
+ extended_header_init ();
t->coder (st, keyword, &extended_header, data);
}
static void
code_string (char const *string, char const *keyword, struct xheader *xhdr)
{
- xheader_print (xhdr, keyword, string);
+ char *outstr;
+ if (!utf8_convert (true, string, &outstr))
+ {
+ /* FIXME: report error */
+ outstr = xstrdup (string);
+ }
+ xheader_print (xhdr, keyword, outstr);
+ free (outstr);
+}
+
+static void
+decode_string (char **string, char const *arg)
+{
+ if (*string)
+ {
+ free (*string);
+ *string = NULL;
+ }
+ if (!utf8_convert (false, arg, string))
+ {
+ /* FIXME: report error and act accordingly to --pax invalid=UTF-8 */
+ assign_string (string, arg);
+ }
}
static void
static void
gname_decoder (struct tar_stat_info *st, char const *arg)
{
- assign_string (&st->gname, arg);
+ decode_string (&st->gname, arg);
}
static void
static void
linkpath_decoder (struct tar_stat_info *st, char const *arg)
{
- assign_string (&st->link_name, arg);
+ decode_string (&st->link_name, arg);
}
static void
static void
path_decoder (struct tar_stat_info *st, char const *arg)
{
- assign_string (&st->orig_file_name, arg);
- assign_string (&st->file_name, arg);
+ decode_string (&st->orig_file_name, arg);
+ decode_string (&st->file_name, arg);
st->had_trailing_slash = strip_trailing_slashes (st->file_name);
}
{
uintmax_t u;
if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
- st->stat.st_size = u;
+ st->archive_file_size = st->stat.st_size = u;
}
static void
static void
uname_decoder (struct tar_stat_info *st, char const *arg)
{
- assign_string (&st->uname, arg);
+ decode_string (&st->uname, arg);
}
static void
{
uintmax_t u;
if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK)
- st->archive_file_size = u;
+ st->stat.st_size = u;
}
static void
{
if (st->sparse_map_avail == st->sparse_map_size)
{
- size_t newsize = st->sparse_map_size *= 2;
+ st->sparse_map_size *= 2;
st->sparse_map = xrealloc (st->sparse_map,
st->sparse_map_size
* sizeof st->sparse_map[0]);
/* The next directory entry actually contains the names of files
that were in the directory at the time the dump was made.
Supersedes GNUTYPE_DUMPDIR header type. */
- { "GNU.dumpdir", dumpdir_coder, dumpdir_decoder },
+ { "GNU.dump.name", dump_name_coder, dump_name_decoder },
+ { "GNU.dump.status", dump_status_coder, dump_status_decoder },
/* Keeps the tape/volume header. May be present only in the global headers.
Equivalent to GNUTYPE_VOLHDR. */