Ejemplo n.º 1
0
/*
 * Note: if text was provided, this just returns that text.  If you
 * really need the text to be rebuilt in a canonical form, set the
 * text, ask for the bitmaps, then set the bitmaps.  (Setting the
 * bitmaps clears any stored text.)  This design is deliberate: if
 * we're editing archives, we don't want to discard flags just because
 * they aren't supported on the current system.  The bitmap<->text
 * conversions are platform-specific (see below).
 */
const char *
archive_entry_fflags_text(struct archive_entry *entry)
{
	const char *f;
	char *p;

	if (archive_mstring_get_mbs(entry->archive,
	    &entry->ae_fflags_text, &f) == 0) {
		if (f != NULL)
			return (f);
	} else if (errno == ENOMEM)
		__archive_errx(1, "No memory");

	if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)
		return (NULL);

	p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
	if (p == NULL)
		return (NULL);

	archive_mstring_copy_mbs(&entry->ae_fflags_text, p);
	free(p);
	if (archive_mstring_get_mbs(entry->archive,
	    &entry->ae_fflags_text, &f) == 0)
		return (f);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (NULL);
}
static void
record_hardlink(struct cpio *cpio, struct archive_entry *entry)
{
	struct links_entry      *le;
	dev_t dev;
	int64_t ino;

	if (tk_archive_entry_nlink(entry) <= 1)
		return;

	dev = tk_archive_entry_dev(entry);
	ino = tk_archive_entry_ino64(entry);

	/*
	 * First look in the list of multiply-linked files.  If we've
	 * already dumped it, convert this entry to a hard link entry.
	 */
	for (le = cpio->links_head; le; le = le->next) {
		if (le->dev == dev && le->ino == ino) {
			tk_archive_entry_copy_hardlink(entry, le->name);

			if (--le->links <= 0) {
				if (le->previous != NULL)
					le->previous->next = le->next;
				if (le->next != NULL)
					le->next->previous = le->previous;
				if (cpio->links_head == le)
					cpio->links_head = le->next;
				free(le->name);
				free(le);
			}

			return;
		}
	}

	le = (struct links_entry *)malloc(sizeof(struct links_entry));
	if (le == NULL)
		__archive_errx(1, "Out of memory adding file to list");
	if (cpio->links_head != NULL)
		cpio->links_head->previous = le;
	le->next = cpio->links_head;
	le->previous = NULL;
	cpio->links_head = le;
	le->dev = dev;
	le->ino = ino;
	le->links = tk_archive_entry_nlink(entry) - 1;
	le->name = strdup(tk_archive_entry_pathname(entry));
	if (le->name == NULL)
		__archive_errx(1, "Out of memory adding file to list");
}
Ejemplo n.º 3
0
/*
 * Generate a text version of the ACL.  The flags parameter controls
 * the style of the generated ACL.
 */
const wchar_t *
archive_entry_acl_text_w(struct archive_entry *entry, int flags)
{
	const wchar_t *r;
	r = archive_acl_text_w(entry->archive, &entry->acl, flags);
	if (r == NULL && errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (r);
}
Ejemplo n.º 4
0
const char *
archive_entry_acl_text(struct archive_entry *entry, int flags)
{
	const char *p;
	if (archive_acl_text_l(&entry->acl, flags, &p, NULL, NULL) != 0
	    && errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (p);
}
static char *
utf8_encode(const wchar_t *wval)
{
	int utf8len;
	const wchar_t *wp;
	unsigned long wc;
	char *utf8_value, *p;

	utf8len = 0;
	for (wp = wval; *wp != L'\0'; ) {
		wc = *wp++;

		if (wc >= 0xd800 && wc <= 0xdbff
		    && *wp >= 0xdc00 && *wp <= 0xdfff) {
			/* This is a surrogate pair.  Combine into a
			 * full Unicode value before encoding into
			 * UTF-8. */
			wc = (wc - 0xd800) << 10; /* High 10 bits */
			wc += (*wp++ - 0xdc00); /* Low 10 bits */
			wc += 0x10000; /* Skip BMP */
		}
		if (wc <= 0x7f)
			utf8len++;
		else if (wc <= 0x7ff)
			utf8len += 2;
		else if (wc <= 0xffff)
			utf8len += 3;
		else if (wc <= 0x1fffff)
			utf8len += 4;
		else if (wc <= 0x3ffffff)
			utf8len += 5;
		else if (wc <= 0x7fffffff)
			utf8len += 6;
		/* Ignore larger values; UTF-8 can't encode them. */
	}

	utf8_value = (char *)malloc(utf8len + 1);
	if (utf8_value == NULL) {
		__archive_errx(1, "Not enough memory for attributes");
		return (NULL);
	}

	for (wp = wval, p = utf8_value; *wp != L'\0'; ) {
		wc = *wp++;
		if (wc >= 0xd800 && wc <= 0xdbff
		    && *wp >= 0xdc00 && *wp <= 0xdfff) {
			/* Combine surrogate pair. */
			wc = (wc - 0xd800) << 10;
			wc += *wp++ - 0xdc00 + 0x10000;
		}
		if (wc <= 0x7f) {
			*p++ = (char)wc;
		} else if (wc <= 0x7ff) {
			p[0] = 0xc0 | ((wc >> 6) & 0x1f);
			p[1] = 0x80 | (wc & 0x3f);
			p += 2;
		} else if (wc <= 0xffff) {
Ejemplo n.º 6
0
int
archive_read_open2(struct archive *_a, void *client_data,
    archive_open_callback *client_opener,
    archive_read_callback *client_reader,
    archive_skip_callback *client_skipper,
    archive_close_callback *client_closer)
{
	struct archive_read *a = (struct archive_read *)_a;
	struct archive_read_filter *filter;
	int e;

	__archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
	    "archive_read_open");
	archive_clear_error(&a->archive);

	if (client_reader == NULL)
		__archive_errx(1,
		    "No reader function provided to archive_read_open");

	/* Open data source. */
	if (client_opener != NULL) {
		e =(client_opener)(&a->archive, client_data);
		if (e != 0) {
			/* If the open failed, call the closer to clean up. */
			if (client_closer)
				(client_closer)(&a->archive, client_data);
			return (e);
		}
	}

	/* Save the client functions and mock up the initial source. */
	a->client.reader = client_reader;
	a->client.skipper = client_skipper;
	a->client.closer = client_closer;

	filter = calloc(1, sizeof(*filter));
	if (filter == NULL)
		return (ARCHIVE_FATAL);
	filter->bidder = NULL;
	filter->upstream = NULL;
	filter->archive = a;
	filter->data = client_data;
	filter->read = client_read_proxy;
	filter->skip = client_skip_proxy;
	filter->close = client_close_proxy;
	filter->name = "none";
	filter->code = ARCHIVE_COMPRESSION_NONE;
	a->filter = filter;

	/* Build out the input pipeline. */
	e = build_stream(a);
	if (e == ARCHIVE_OK)
		a->archive.state = ARCHIVE_STATE_HEADER;

	return (e);
}
Ejemplo n.º 7
0
const char *
archive_entry_gname_utf8(struct archive_entry *entry)
{
	const char *p;
	if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0)
		return (p);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (NULL);
}
Ejemplo n.º 8
0
const wchar_t *
archive_entry_uname_w(struct archive_entry *entry)
{
	const wchar_t *p;
	if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0)
		return (p);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (NULL);
}
Ejemplo n.º 9
0
int
archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
{
	if (archive_mstring_update_utf8(entry->archive,
	    &entry->ae_gname, name) == 0)
		return (1);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (0);
}
Ejemplo n.º 10
0
struct archive_string *
__archive_string_append(struct archive_string *as, const char *p, size_t s)
{
	if (__archive_string_ensure(as, as->length + s + 1) == NULL)
		__archive_errx(1, "Out of memory");
	memcpy(as->s + as->length, p, s);
	as->s[as->length + s] = 0;
	as->length += s;
	return (as);
}
Ejemplo n.º 11
0
/*
 * Return the next ACL entry in the list.  Fake entries for the
 * standard permissions and include them in the returned list.
 */
int
archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
    int *permset, int *tag, int *id, const char **name)
{
	int r;
	r = archive_acl_next(entry->archive, &entry->acl, want_type, type,
		permset, tag, id, name);
	if (r == ARCHIVE_FATAL && errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (r);
}
Ejemplo n.º 12
0
const char *
archive_entry_sourcepath(struct archive_entry *entry)
{
	const char *p;
	if (archive_mstring_get_mbs(
	    entry->archive, &entry->ae_sourcepath, &p) == 0)
		return (p);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (NULL);
}
Ejemplo n.º 13
0
void
__archive_string_concat(struct archive_string *dest, struct archive_string *src)
{
	if (src->length > 0) {
		if (__archive_string_ensure(dest, dest->length + src->length + 1) == NULL)
			__archive_errx(1, "Out of memory");
		memcpy(dest->s + dest->length, src->s, src->length);
		dest->length += src->length;
		dest->s[dest->length] = 0;
	}
}
Ejemplo n.º 14
0
void
archive_entry_xattr_add_entry(struct archive_entry *entry,
	const char *name, const void *value, size_t size)
{
	struct ae_xattr	*xp;

	if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)
		__archive_errx(1, "Out of memory");

	if ((xp->name = strdup(name)) == NULL)
		__archive_errx(1, "Out of memory");

	if ((xp->value = malloc(size)) != NULL) {
		memcpy(xp->value, value, size);
		xp->size = size;
	} else
		xp->size = 0;

	xp->next = entry->xattr_head;
	entry->xattr_head = xp;
}
/*
 * Mark a previously-returned block of data as read.
 */
static ssize_t
read_consume(struct archive_read *a, size_t n)
{
	struct private_data *state;

	state = (struct private_data *)a->decompressor->data;
	a->archive.file_position += n;
	state->read_next += n;
	if (state->read_next > state->stream.next_out)
		__archive_errx(1, "Request to consume too many "
		    "bytes from bzip2 decompressor");
	return (n);
}
Ejemplo n.º 16
0
void
__archive_string_copy(struct archive_string *dest, struct archive_string *src)
{
	if (src->length == 0)
		dest->length = 0;
	else {
		if (__archive_string_ensure(dest, src->length + 1) == NULL)
			__archive_errx(1, "Out of memory");
		memcpy(dest->s, src->s, src->length);
		dest->length = src->length;
		dest->s[dest->length] = 0;
	}
}
Ejemplo n.º 17
0
const wchar_t *
archive_entry_symlink_w(struct archive_entry *entry)
{
	const wchar_t *p;
	if ((entry->ae_set & AE_SET_SYMLINK) == 0)
		return (NULL);
	if (archive_mstring_get_wcs(
	    entry->archive, &entry->ae_symlink, &p) == 0)
		return (p);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (NULL);
}
Ejemplo n.º 18
0
const char *
archive_entry_hardlink_utf8(struct archive_entry *entry)
{
	const char *p;
	if ((entry->ae_set & AE_SET_HARDLINK) == 0)
		return (NULL);
	if (archive_mstring_get_utf8(
	    entry->archive, &entry->ae_hardlink, &p) == 0)
		return (p);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (NULL);
}
Ejemplo n.º 19
0
int
archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)
{
	if (target != NULL)
		entry->ae_set |= AE_SET_HARDLINK;
	else
		entry->ae_set &= ~AE_SET_HARDLINK;
	if (archive_mstring_update_utf8(entry->archive,
	    &entry->ae_hardlink, target) == 0)
		return (1);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (0);
}
Ejemplo n.º 20
0
int
archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)
{
	if (linkname != NULL)
		entry->ae_set |= AE_SET_SYMLINK;
	else
		entry->ae_set &= ~AE_SET_SYMLINK;
	if (archive_mstring_update_utf8(entry->archive,
	    &entry->ae_symlink, linkname) == 0)
		return (1);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (0);
}
Ejemplo n.º 21
0
static int64_t
client_skip_proxy(struct archive_read_filter *self, int64_t request)
{
	if (request < 0)
		__archive_errx(1, "Negative skip requested.");
	if (request == 0)
		return 0;

	if (self->archive->client.skipper != NULL) {
		/* Seek requests over 1GiB are broken down into
		 * multiple seeks.  This avoids overflows when the
		 * requests get passed through 32-bit arguments. */
		int64_t skip_limit = (int64_t)1 << 30;
		int64_t total = 0;
		for (;;) {
			int64_t get, ask = request;
			if (ask > skip_limit)
				ask = skip_limit;
			get = (self->archive->client.skipper)
				(&self->archive->archive, self->data, ask);
			total += get;
			if (get == 0 || get == request)
				return (total);
			if (get > request)
				return ARCHIVE_FATAL;
			request -= get;
		}
	} else if (self->archive->client.seeker != NULL
		&& request > 64 * 1024) {
		/* If the client provided a seeker but not a skipper,
		 * we can use the seeker to skip forward.
		 *
		 * Note: This isn't always a good idea.  The client
		 * skipper is allowed to skip by less than requested
		 * if it needs to maintain block alignment.  The
		 * seeker is not allowed to play such games, so using
		 * the seeker here may be a performance loss compared
		 * to just reading and discarding.  That's why we
		 * only do this for skips of over 64k.
		 */
		int64_t before = self->position;
		int64_t after = (self->archive->client.seeker)
		    (&self->archive->archive, self->data, request, SEEK_CUR);
		if (after != before + request)
			return ARCHIVE_FATAL;
		return after - before;
	}
	return 0;
}
Ejemplo n.º 22
0
int
archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
{
	int r;
	if (entry->ae_set & AE_SET_SYMLINK)
		r = archive_mstring_update_utf8(entry->archive,
		    &entry->ae_symlink, target);
	else
		r = archive_mstring_update_utf8(entry->archive,
		    &entry->ae_hardlink, target);
	if (r == 0)
		return (1);
	if (errno == ENOMEM)
		__archive_errx(1, "No memory");
	return (0);
}
Ejemplo n.º 23
0
/*
 * Allow each registered format to bid on whether it wants to handle
 * the next entry.  Return index of winning bidder.
 */
static int
choose_format(struct archive_read *a)
{
	int slots;
	int i;
	int bid, best_bid;
	int best_bid_slot;

	slots = sizeof(a->formats) / sizeof(a->formats[0]);
	best_bid = -1;
	best_bid_slot = -1;

	/* Set up a->format and a->pformat_data for convenience of bidders. */
	a->format = &(a->formats[0]);
	for (i = 0; i < slots; i++, a->format++) {
		if (a->format->bid) {
			bid = (a->format->bid)(a);
			if (bid == ARCHIVE_FATAL)
				return (ARCHIVE_FATAL);
			if ((bid > best_bid) || (best_bid_slot < 0)) {
				best_bid = bid;
				best_bid_slot = i;
			}
		}
	}

	/*
	 * There were no bidders; this is a serious programmer error
	 * and demands a quick and definitive abort.
	 */
	if (best_bid_slot < 0)
		__archive_errx(1, "No formats were registered; you must "
		    "invoke at least one archive_read_support_format_XXX "
		    "function in order to successfully read an archive.");

	/*
	 * There were bidders, but no non-zero bids; this means we
	 * can't support this stream.
	 */
	if (best_bid < 1) {
		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
		    "Unrecognized archive format");
		return (ARCHIVE_FATAL);
	}

	return (best_bid_slot);
}
Ejemplo n.º 24
0
/*
 * Used internally by decompression routines to register their bid and
 * initialization functions.
 */
struct archive_read_filter_bidder *
__archive_read_get_bidder(struct archive_read *a)
{
	int i, number_slots;

	__archive_check_magic(&a->archive,
	    ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
	    "__archive_read_get_bidder");

	number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);

	for (i = 0; i < number_slots; i++) {
		if (a->bidders[i].bid == NULL) {
			memset(a->bidders + i, 0, sizeof(a->bidders[0]));
			return (a->bidders + i);
		}
	}

	__archive_errx(1, "Not enough slots for compression registration");
	return (NULL); /* Never actually executed. */
}
Ejemplo n.º 25
0
/*
 * Used internally by read format handlers to register their bid and
 * initialization functions.
 */
int
__archive_read_register_format(struct archive_read *a,
    void *format_data,
    const char *name,
    int (*bid)(struct archive_read *),
    int (*options)(struct archive_read *, const char *, const char *),
    int (*read_header)(struct archive_read *, struct archive_entry *),
    int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
    int (*read_data_skip)(struct archive_read *),
    int (*cleanup)(struct archive_read *))
{
	int i, number_slots;

	__archive_check_magic(&a->archive,
	    ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
	    "__archive_read_register_format");

	number_slots = sizeof(a->formats) / sizeof(a->formats[0]);

	for (i = 0; i < number_slots; i++) {
		if (a->formats[i].bid == bid)
			return (ARCHIVE_WARN); /* We've already installed */
		if (a->formats[i].bid == NULL) {
			a->formats[i].bid = bid;
			a->formats[i].options = options;
			a->formats[i].read_header = read_header;
			a->formats[i].read_data = read_data;
			a->formats[i].read_data_skip = read_data_skip;
			a->formats[i].cleanup = cleanup;
			a->formats[i].data = format_data;
			a->formats[i].name = name;
			return (ARCHIVE_OK);
		}
	}

	__archive_errx(1, "Not enough slots for format registration");
	return (ARCHIVE_FATAL); /* Never actually called. */
}
static void
add_entry(struct iso9660 *iso9660, struct file_info *file)
{
	/* Expand our pending files list as necessary. */
	if (iso9660->pending_files_used >= iso9660->pending_files_allocated) {
		struct file_info **new_pending_files;
		int new_size = iso9660->pending_files_allocated * 2;

		if (new_size < 1024)
			new_size = 1024;
		new_pending_files = (struct file_info **)malloc(new_size * sizeof(new_pending_files[0]));
		if (new_pending_files == NULL)
			__archive_errx(1, "Out of memory");
		memcpy(new_pending_files, iso9660->pending_files,
		    iso9660->pending_files_allocated * sizeof(new_pending_files[0]));
		if (iso9660->pending_files != NULL)
			free(iso9660->pending_files);
		iso9660->pending_files = new_pending_files;
		iso9660->pending_files_allocated = new_size;
	}

	iso9660->pending_files[iso9660->pending_files_used++] = file;
}
Ejemplo n.º 27
0
/*
 * Like 'vsprintf', but ensures the target is big enough, resizing if
 * necessary.
 */
void
archive_string_vsprintf(struct archive_string *as, const char *fmt,
    va_list ap)
{
	char long_flag;
	intmax_t s; /* Signed integer temp. */
	uintmax_t u; /* Unsigned integer temp. */
	const char *p, *p2;
	const wchar_t *pw;

	if (archive_string_ensure(as, 64) == NULL)
		__archive_errx(1, "Out of memory");

	if (fmt == NULL) {
		as->s[0] = 0;
		return;
	}

	for (p = fmt; *p != '\0'; p++) {
		const char *saved_p = p;

		if (*p != '%') {
			archive_strappend_char(as, *p);
			continue;
		}

		p++;

		long_flag = '\0';
		switch(*p) {
		case 'j':
		case 'l':
		case 'z':
			long_flag = *p;
			p++;
			break;
		}

		switch (*p) {
		case '%':
			archive_strappend_char(as, '%');
			break;
		case 'c':
			s = va_arg(ap, int);
			archive_strappend_char(as, (char)s);
			break;
		case 'd':
			switch(long_flag) {
			case 'j': s = va_arg(ap, intmax_t); break;
			case 'l': s = va_arg(ap, long); break;
			case 'z': s = va_arg(ap, ssize_t); break;
			default:  s = va_arg(ap, int); break;
			}
		        append_int(as, s, 10);
			break;
		case 's':
			switch(long_flag) {
			case 'l':
				pw = va_arg(ap, wchar_t *);
				if (pw == NULL)
					pw = L"(null)";
				if (archive_string_append_from_wcs(as, pw,
				    wcslen(pw)) != 0 && errno == ENOMEM)
					__archive_errx(1, "Out of memory");
				break;
			default:
				p2 = va_arg(ap, char *);
				if (p2 == NULL)
					p2 = "(null)";
				archive_strcat(as, p2);
				break;
			}
			break;
		case 'S':
			pw = va_arg(ap, wchar_t *);
			if (pw == NULL)
				pw = L"(null)";
			if (archive_string_append_from_wcs(as, pw,
			    wcslen(pw)) != 0 && errno == ENOMEM)
				__archive_errx(1, "Out of memory");
			break;
		case 'o': case 'u': case 'x': case 'X':
			/* Common handling for unsigned integer formats. */
			switch(long_flag) {
			case 'j': u = va_arg(ap, uintmax_t); break;
			case 'l': u = va_arg(ap, unsigned long); break;
			case 'z': u = va_arg(ap, size_t); break;
			default:  u = va_arg(ap, unsigned int); break;
			}
			/* Format it in the correct base. */
			switch (*p) {
			case 'o': append_uint(as, u, 8); break;
			case 'u': append_uint(as, u, 10); break;
			default: append_uint(as, u, 16); break;
			}
			break;
		default:
			/* Rewind and print the initial '%' literally. */
			p = saved_p;
			archive_strappend_char(as, *p);
		}
	}
}
Ejemplo n.º 28
0
/*
 * Like 'vsprintf', but ensures the target is big enough, resizing if
 * necessary.
 */
void
__archive_string_vsprintf(struct archive_string *as, const char *fmt,
    va_list ap)
{
	char long_flag;
	intmax_t s; /* Signed integer temp. */
	uintmax_t u; /* Unsigned integer temp. */
	const char *p, *p2;

	if (__archive_string_ensure(as, 64) == NULL)
		__archive_errx(1, "Out of memory");

	if (fmt == NULL) {
		as->s[0] = 0;
		return;
	}

	long_flag = '\0';
	for (p = fmt; *p != '\0'; p++) {
		const char *saved_p = p;

		if (*p != '%') {
			archive_strappend_char(as, *p);
			continue;
		}

		p++;

		switch(*p) {
		case 'j':
			long_flag = 'j';
			p++;
			break;
		case 'l':
			long_flag = 'l';
			p++;
			break;
		}

		switch (*p) {
		case '%':
			__archive_strappend_char(as, '%');
			break;
		case 'c':
			s = va_arg(ap, int);
			__archive_strappend_char(as, s);
			break;
		case 'd':
			switch(long_flag) {
			case 'j': s = va_arg(ap, intmax_t); break;
			case 'l': s = va_arg(ap, long); break;
			default:  s = va_arg(ap, int); break;
			}
			archive_strappend_int(as, s, 10);
			break;
		case 's':
			p2 = va_arg(ap, char *);
			archive_strcat(as, p2);
			break;
		case 'o': case 'u': case 'x': case 'X':
			/* Common handling for unsigned integer formats. */
			switch(long_flag) {
			case 'j': u = va_arg(ap, uintmax_t); break;
			case 'l': u = va_arg(ap, unsigned long); break;
			default:  u = va_arg(ap, unsigned int); break;
			}
			/* Format it in the correct base. */
			switch (*p) {
			case 'o': archive_strappend_int(as, u, 8); break;
			case 'u': archive_strappend_int(as, u, 10); break;
			default: archive_strappend_int(as, u, 16); break;
			}
			break;
		default:
			/* Rewind and print the initial '%' literally. */
			p = saved_p;
			archive_strappend_char(as, *p);
		}
	}
}
static int
zip_read_file_header(struct archive_read *a, struct archive_entry *entry,
    struct zip *zip)
{
	const struct zip_file_header *p;
	const void *h;

	if ((p = __archive_read_ahead(a, sizeof *p, NULL)) == NULL) {
		tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
		    "Truncated ZIP file header");
		return (ARCHIVE_FATAL);
	}

	zip->version = p->version[0];
	zip->system = p->version[1];
	zip->flags = tk_archive_le16dec(p->flags);
	zip->compression = tk_archive_le16dec(p->compression);
	if (zip->compression <
	    sizeof(compression_names)/sizeof(compression_names[0]))
		zip->compression_name = compression_names[zip->compression];
	else
		zip->compression_name = "??";
	zip->mtime = zip_time(p->timedate);
	zip->ctime = 0;
	zip->atime = 0;
	zip->mode = 0;
	zip->uid = 0;
	zip->gid = 0;
	zip->crc32 = tk_archive_le32dec(p->crc32);
	zip->filename_length = tk_archive_le16dec(p->filename_length);
	zip->extra_length = tk_archive_le16dec(p->extra_length);
	zip->uncompressed_size = tk_archive_le32dec(p->uncompressed_size);
	zip->compressed_size = tk_archive_le32dec(p->compressed_size);

	__archive_read_consume(a, sizeof(struct zip_file_header));


	/* Read the filename. */
	if ((h = __archive_read_ahead(a, zip->filename_length, NULL)) == NULL) {
		tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
		    "Truncated ZIP file header");
		return (ARCHIVE_FATAL);
	}
	if (tk_archive_string_ensure(&zip->pathname, zip->filename_length) == NULL)
		__archive_errx(1, "Out of memory");
	tk_archive_strncpy(&zip->pathname, h, zip->filename_length);
	__archive_read_consume(a, zip->filename_length);
	tk_archive_entry_set_pathname(entry, zip->pathname.s);

	if (zip->pathname.s[tk_archive_strlen(&zip->pathname) - 1] == '/')
		zip->mode = AE_IFDIR | 0777;
	else
		zip->mode = AE_IFREG | 0777;

	/* Read the extra data. */
	if ((h = __archive_read_ahead(a, zip->extra_length, NULL)) == NULL) {
		tk_archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
		    "Truncated ZIP file header");
		return (ARCHIVE_FATAL);
	}
	process_extra(h, zip);
	__archive_read_consume(a, zip->extra_length);

	/* Populate some additional entry fields: */
	tk_archive_entry_set_mode(entry, zip->mode);
	tk_archive_entry_set_uid(entry, zip->uid);
	tk_archive_entry_set_gid(entry, zip->gid);
	tk_archive_entry_set_mtime(entry, zip->mtime, 0);
	tk_archive_entry_set_ctime(entry, zip->ctime, 0);
	tk_archive_entry_set_atime(entry, zip->atime, 0);
	/* Set the size only if it's meaningful. */
	if (0 == (zip->flags & ZIP_LENGTH_AT_END))
		tk_archive_entry_set_size(entry, zip->uncompressed_size);

	zip->entry_bytes_remaining = zip->compressed_size;
	zip->entry_offset = 0;

	/* If there's no body, force read_data() to return EOF immediately. */
	if (0 == (zip->flags & ZIP_LENGTH_AT_END)
	    && zip->entry_bytes_remaining < 1)
		zip->end_of_entry = 1;

	/* Set up a more descriptive format name. */
	sprintf(zip->format_name, "ZIP %d.%d (%s)",
	    zip->version / 10, zip->version % 10,
	    zip->compression_name);
	a->archive.archive_format_name = zip->format_name;

	return (ARCHIVE_OK);
}