X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fxheader.c;h=62fbf5db32a569cb3f6ae8504def61527f7f528b;hb=a5bafe7962470f7d888d34b899ec6ff50855d050;hp=7c993a484b08b03856123f51c32e07dd45f6cb96;hpb=440e65ef2c2b61b43a4d520f4bca9a669f71a789;p=chaz%2Ftar diff --git a/src/xheader.c b/src/xheader.c index 7c993a4..62fbf5d 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -28,11 +28,28 @@ #define obstack_chunk_free free #include +#include + 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 */ /* Keyword options */ @@ -47,12 +64,19 @@ struct keyword_list /* 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; @@ -79,7 +103,8 @@ xheader_keyword_override_p (const char *keyword) } 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); @@ -88,10 +113,29 @@ xheader_list_append (struct keyword_list **root, char *kw, char *value) *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 @@ -117,7 +161,7 @@ xheader_set_keyword_equal (char *kw, char *eq) 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) @@ -127,7 +171,7 @@ xheader_set_keyword_equal (char *kw, char *eq) 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 @@ -149,6 +193,26 @@ xheader_set_option (char *string) } } +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, @@ -173,7 +237,7 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) char pidbuf[64]; char nbuf[64]; - for (p = exthdr_name; *p && (p = strchr (p, '%')); ) + for (p = fmt; *p && (p = strchr (p, '%')); ) { switch (p[1]) { @@ -182,26 +246,31 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) 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; @@ -222,12 +291,14 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) 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; @@ -264,16 +335,15 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) 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) { @@ -287,16 +357,65 @@ xheader_ghdr_name (struct tar_stat_info *st) 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++; } /* 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; @@ -351,13 +470,14 @@ xheader_protected_keyword_p (const char *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; @@ -381,26 +501,15 @@ decode_record (char **p, struct tar_stat_info *st) 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; } @@ -416,26 +525,75 @@ run_override_list (struct keyword_list *kp, struct tar_stat_info *st) } } +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; @@ -445,11 +603,7 @@ xheader_store (char const *keyword, struct tar_stat_info const *st, void *data) 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); } @@ -565,7 +719,29 @@ xheader_destroy (struct xheader *xhdr) 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 @@ -655,7 +831,7 @@ gname_coder (struct tar_stat_info const *st, char const *keyword, static void gname_decoder (struct tar_stat_info *st, char const *arg) { - assign_string (&st->gname, arg); + decode_string (&st->gname, arg); } static void @@ -668,7 +844,7 @@ linkpath_coder (struct tar_stat_info const *st, char const *keyword, 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 @@ -707,8 +883,8 @@ path_coder (struct tar_stat_info const *st, char const *keyword, 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); } @@ -724,7 +900,7 @@ size_decoder (struct tar_stat_info *st, char const *arg) { 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 @@ -752,7 +928,7 @@ uname_coder (struct tar_stat_info const *st, char const *keyword, static void uname_decoder (struct tar_stat_info *st, char const *arg) { - assign_string (&st->uname, arg); + decode_string (&st->uname, arg); } static void @@ -767,7 +943,7 @@ sparse_size_decoder (struct tar_stat_info *st, char const *arg) { uintmax_t u; if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) - st->archive_file_size = u; + st->stat.st_size = u; } static void @@ -821,7 +997,7 @@ sparse_numbytes_decoder (struct tar_stat_info *st, char const *arg) { 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]); @@ -858,7 +1034,8 @@ struct xhdr_tab const xhdr_tab[] = { /* 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. */