+
+static void
+sparse_size_coder (struct tar_stat_info const *st, char const *keyword,
+ struct xheader *xhdr, void *data)
+{
+ size_coder (st, keyword, xhdr, data);
+}
+
+static void
+sparse_size_decoder (struct tar_stat_info *st, char const *arg)
+{
+ uintmax_t u;
+ if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK)
+ st->archive_file_size = u;
+}
+
+static void
+sparse_numblocks_coder (struct tar_stat_info const *st, char const *keyword,
+ struct xheader *xhdr, void *data)
+{
+ code_num (st->sparse_map_avail, keyword, xhdr);
+}
+
+static void
+sparse_numblocks_decoder (struct tar_stat_info *st, char const *arg)
+{
+ uintmax_t u;
+ if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK)
+ {
+ st->sparse_map_size = u;
+ st->sparse_map = calloc(st->sparse_map_size, sizeof(st->sparse_map[0]));
+ st->sparse_map_avail = 0;
+ }
+}
+
+static void
+sparse_offset_coder (struct tar_stat_info const *st, char const *keyword,
+ struct xheader *xhdr, void *data)
+{
+ size_t i = *(size_t*)data;
+ code_num (st->sparse_map[i].offset, keyword, xhdr);
+}
+
+static void
+sparse_offset_decoder (struct tar_stat_info *st, char const *arg)
+{
+ uintmax_t u;
+ if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK)
+ st->sparse_map[st->sparse_map_avail].offset = u;
+}
+
+static void
+sparse_numbytes_coder (struct tar_stat_info const *st, char const *keyword,
+ struct xheader *xhdr, void *data)
+{
+ size_t i = *(size_t*)data;
+ code_num (st->sparse_map[i].numbytes, keyword, xhdr);
+}
+
+static void
+sparse_numbytes_decoder (struct tar_stat_info *st, char const *arg)
+{
+ uintmax_t u;
+ if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK)
+ {
+ if (st->sparse_map_avail == st->sparse_map_size)
+ {
+ size_t newsize = st->sparse_map_size *= 2;
+ st->sparse_map = xrealloc (st->sparse_map,
+ st->sparse_map_size
+ * sizeof st->sparse_map[0]);
+ }
+ st->sparse_map[st->sparse_map_avail++].numbytes = u;
+ }
+}
+
+struct xhdr_tab const xhdr_tab[] = {
+ { "atime", atime_coder, atime_decoder },
+ { "comment", dummy_coder, dummy_decoder },
+ { "charset", dummy_coder, dummy_decoder },
+ { "ctime", ctime_coder, ctime_decoder },
+ { "gid", gid_coder, gid_decoder },
+ { "gname", gname_coder, gname_decoder },
+ { "linkpath", linkpath_coder, linkpath_decoder},
+ { "mtime", mtime_coder, mtime_decoder },
+ { "path", path_coder, path_decoder },
+ { "size", size_coder, size_decoder },
+ { "uid", uid_coder, uid_decoder },
+ { "uname", uname_coder, uname_decoder },
+
+ /* Sparse file handling */
+ { "GNU.sparse.size", sparse_size_coder, sparse_size_decoder },
+ { "GNU.sparse.numblocks", sparse_numblocks_coder, sparse_numblocks_decoder },
+ { "GNU.sparse.offset", sparse_offset_coder, sparse_offset_decoder },
+ { "GNU.sparse.numbytes", sparse_numbytes_coder, sparse_numbytes_decoder },
+
+#if 0 /* GNU private keywords (not yet implemented) */
+
+ /* 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 },
+
+ /* Keeps the tape/volume header. May be present only in the global headers.
+ Equivalent to GNUTYPE_VOLHDR. */
+ { "GNU.volume.header", volume_header_coder, volume_header_decoder },
+
+ /* These may be present in a first global header of the archive.
+ They provide the same functionality as GNUTYPE_MULTIVOL header.
+ The GNU.volume.size keeps the real_s_sizeleft value, which is
+ otherwise kept in the size field of a multivolume header. The
+ GNU.volume.offset keeps the offset of the start of this volume,
+ otherwise kept in oldgnu_header.offset. */
+ { "GNU.volume.size", volume_size_coder, volume_size_decoder },
+ { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder },
+#endif
+
+ { NULL, NULL, NULL }
+};