1228753Smm/*-
2228753Smm * Copyright (c) 2003-2007 Tim Kientzle
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24228753Smm */
25228753Smm
26228753Smm#include "bsdtar_platform.h"
27228763Smm__FBSDID("$FreeBSD$");
28228753Smm
29228753Smm#ifdef HAVE_SYS_STAT_H
30228753Smm#include <sys/stat.h>
31228753Smm#endif
32228753Smm#ifdef HAVE_SYS_TYPES_H
33228753Smm#include <sys/types.h>  /* Linux doesn't define mode_t, etc. in sys/stat.h. */
34228753Smm#endif
35228753Smm#include <ctype.h>
36228753Smm#ifdef HAVE_ERRNO_H
37228753Smm#include <errno.h>
38228753Smm#endif
39228753Smm#ifdef HAVE_IO_H
40228753Smm#include <io.h>
41228753Smm#endif
42228753Smm#ifdef HAVE_STDARG_H
43228753Smm#include <stdarg.h>
44228753Smm#endif
45228753Smm#ifdef HAVE_STDINT_H
46228753Smm#include <stdint.h>
47228753Smm#endif
48228753Smm#include <stdio.h>
49228753Smm#ifdef HAVE_STDLIB_H
50228753Smm#include <stdlib.h>
51228753Smm#endif
52228753Smm#ifdef HAVE_STRING_H
53228753Smm#include <string.h>
54228753Smm#endif
55228753Smm#ifdef HAVE_WCTYPE_H
56228753Smm#include <wctype.h>
57228753Smm#else
58228753Smm/* If we don't have wctype, we need to hack up some version of iswprint(). */
59232153Smm#define	iswprint isprint
60228753Smm#endif
61228753Smm
62228753Smm#include "bsdtar.h"
63228753Smm#include "err.h"
64228753Smm
65228753Smmstatic size_t	bsdtar_expand_char(char *, size_t, char);
66228753Smmstatic const char *strip_components(const char *path, int elements);
67228753Smm
68228753Smm#if defined(_WIN32) && !defined(__CYGWIN__)
69232153Smm#define	read _read
70228753Smm#endif
71228753Smm
72228753Smm/* TODO:  Hack up a version of mbtowc for platforms with no wide
73228753Smm * character support at all.  I think the following might suffice,
74228753Smm * but it needs careful testing.
75228753Smm * #if !HAVE_MBTOWC
76232153Smm * #define	mbtowc(wcp, p, n) ((*wcp = *p), 1)
77228753Smm * #endif
78228753Smm */
79228753Smm
80228753Smm/*
81228753Smm * Print a string, taking care with any non-printable characters.
82228753Smm *
83228753Smm * Note that we use a stack-allocated buffer to receive the formatted
84228753Smm * string if we can.  This is partly performance (avoiding a call to
85228753Smm * malloc()), partly out of expedience (we have to call vsnprintf()
86228753Smm * before malloc() anyway to find out how big a buffer we need; we may
87228753Smm * as well point that first call at a small local buffer in case it
88228753Smm * works), but mostly for safety (so we can use this to print messages
89228753Smm * about out-of-memory conditions).
90228753Smm */
91228753Smm
92228753Smmvoid
93228753Smmsafe_fprintf(FILE *f, const char *fmt, ...)
94228753Smm{
95228753Smm	char fmtbuff_stack[256]; /* Place to format the printf() string. */
96228753Smm	char outbuff[256]; /* Buffer for outgoing characters. */
97228753Smm	char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */
98228753Smm	char *fmtbuff;  /* Pointer to fmtbuff_stack or fmtbuff_heap. */
99228753Smm	int fmtbuff_length;
100228753Smm	int length, n;
101228753Smm	va_list ap;
102228753Smm	const char *p;
103228753Smm	unsigned i;
104228753Smm	wchar_t wc;
105228753Smm	char try_wc;
106228753Smm
107228753Smm	/* Use a stack-allocated buffer if we can, for speed and safety. */
108228753Smm	fmtbuff_heap = NULL;
109228753Smm	fmtbuff_length = sizeof(fmtbuff_stack);
110228753Smm	fmtbuff = fmtbuff_stack;
111228753Smm
112228753Smm	/* Try formatting into the stack buffer. */
113228753Smm	va_start(ap, fmt);
114228753Smm	length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap);
115228753Smm	va_end(ap);
116228753Smm
117228753Smm	/* If the result was too large, allocate a buffer on the heap. */
118232153Smm	while (length < 0 || length >= fmtbuff_length) {
119232153Smm		if (length >= fmtbuff_length)
120232153Smm			fmtbuff_length = length+1;
121232153Smm		else if (fmtbuff_length < 8192)
122232153Smm			fmtbuff_length *= 2;
123248616Smm		else if (fmtbuff_length < 1000000)
124248616Smm			fmtbuff_length += fmtbuff_length / 4;
125232153Smm		else {
126248616Smm			length = fmtbuff_length;
127248616Smm			fmtbuff_heap[length-1] = '\0';
128248616Smm			break;
129232153Smm		}
130232153Smm		free(fmtbuff_heap);
131228753Smm		fmtbuff_heap = malloc(fmtbuff_length);
132228753Smm
133228753Smm		/* Reformat the result into the heap buffer if we can. */
134228753Smm		if (fmtbuff_heap != NULL) {
135228753Smm			fmtbuff = fmtbuff_heap;
136228753Smm			va_start(ap, fmt);
137228753Smm			length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap);
138228753Smm			va_end(ap);
139228753Smm		} else {
140228753Smm			/* Leave fmtbuff pointing to the truncated
141228753Smm			 * string in fmtbuff_stack. */
142228753Smm			length = sizeof(fmtbuff_stack) - 1;
143232153Smm			break;
144228753Smm		}
145228753Smm	}
146228753Smm
147228753Smm	/* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit
148228753Smm	 * more portable, so we use that here instead. */
149248616Smm	if (mbtowc(NULL, NULL, 1) == -1) { /* Reset the shift state. */
150248616Smm		/* mbtowc() should never fail in practice, but
151248616Smm		 * handle the theoretical error anyway. */
152248616Smm		free(fmtbuff_heap);
153248616Smm		return;
154248616Smm	}
155228753Smm
156228753Smm	/* Write data, expanding unprintable characters. */
157228753Smm	p = fmtbuff;
158228753Smm	i = 0;
159228753Smm	try_wc = 1;
160228753Smm	while (*p != '\0') {
161228753Smm
162228753Smm		/* Convert to wide char, test if the wide
163228753Smm		 * char is printable in the current locale. */
164228753Smm		if (try_wc && (n = mbtowc(&wc, p, length)) != -1) {
165228753Smm			length -= n;
166228753Smm			if (iswprint(wc) && wc != L'\\') {
167228753Smm				/* Printable, copy the bytes through. */
168228753Smm				while (n-- > 0)
169228753Smm					outbuff[i++] = *p++;
170228753Smm			} else {
171228753Smm				/* Not printable, format the bytes. */
172228753Smm				while (n-- > 0)
173228753Smm					i += (unsigned)bsdtar_expand_char(
174228753Smm					    outbuff, i, *p++);
175228753Smm			}
176228753Smm		} else {
177228753Smm			/* After any conversion failure, don't bother
178228753Smm			 * trying to convert the rest. */
179228753Smm			i += (unsigned)bsdtar_expand_char(outbuff, i, *p++);
180228753Smm			try_wc = 0;
181228753Smm		}
182228753Smm
183228753Smm		/* If our output buffer is full, dump it and keep going. */
184228753Smm		if (i > (sizeof(outbuff) - 20)) {
185228753Smm			outbuff[i] = '\0';
186228753Smm			fprintf(f, "%s", outbuff);
187228753Smm			i = 0;
188228753Smm		}
189228753Smm	}
190228753Smm	outbuff[i] = '\0';
191228753Smm	fprintf(f, "%s", outbuff);
192228753Smm
193228753Smm	/* If we allocated a heap-based formatting buffer, free it now. */
194248616Smm	free(fmtbuff_heap);
195228753Smm}
196228753Smm
197228753Smm/*
198228753Smm * Render an arbitrary sequence of bytes into printable ASCII characters.
199228753Smm */
200228753Smmstatic size_t
201228753Smmbsdtar_expand_char(char *buff, size_t offset, char c)
202228753Smm{
203228753Smm	size_t i = offset;
204228753Smm
205228753Smm	if (isprint((unsigned char)c) && c != '\\')
206228753Smm		buff[i++] = c;
207228753Smm	else {
208228753Smm		buff[i++] = '\\';
209228753Smm		switch (c) {
210228753Smm		case '\a': buff[i++] = 'a'; break;
211228753Smm		case '\b': buff[i++] = 'b'; break;
212228753Smm		case '\f': buff[i++] = 'f'; break;
213228753Smm		case '\n': buff[i++] = 'n'; break;
214228753Smm#if '\r' != '\n'
215228753Smm		/* On some platforms, \n and \r are the same. */
216228753Smm		case '\r': buff[i++] = 'r'; break;
217228753Smm#endif
218228753Smm		case '\t': buff[i++] = 't'; break;
219228753Smm		case '\v': buff[i++] = 'v'; break;
220228753Smm		case '\\': buff[i++] = '\\'; break;
221228753Smm		default:
222228753Smm			sprintf(buff + i, "%03o", 0xFF & (int)c);
223228753Smm			i += 3;
224228753Smm		}
225228753Smm	}
226228753Smm
227228753Smm	return (i - offset);
228228753Smm}
229228753Smm
230228753Smmint
231228753Smmyes(const char *fmt, ...)
232228753Smm{
233228753Smm	char buff[32];
234228753Smm	char *p;
235228753Smm	ssize_t l;
236228753Smm
237228753Smm	va_list ap;
238228753Smm	va_start(ap, fmt);
239228753Smm	vfprintf(stderr, fmt, ap);
240228753Smm	va_end(ap);
241228753Smm	fprintf(stderr, " (y/N)? ");
242228753Smm	fflush(stderr);
243228753Smm
244228753Smm	l = read(2, buff, sizeof(buff) - 1);
245228776Smm	if (l < 0) {
246228776Smm	  fprintf(stderr, "Keyboard read failed\n");
247228776Smm	  exit(1);
248228776Smm	}
249228776Smm	if (l == 0)
250228753Smm		return (0);
251228753Smm	buff[l] = 0;
252228753Smm
253228753Smm	for (p = buff; *p != '\0'; p++) {
254228753Smm		if (isspace((unsigned char)*p))
255228753Smm			continue;
256228753Smm		switch(*p) {
257228753Smm		case 'y': case 'Y':
258228753Smm			return (1);
259228753Smm		case 'n': case 'N':
260228753Smm			return (0);
261228753Smm		default:
262228753Smm			return (0);
263228753Smm		}
264228753Smm	}
265228753Smm
266228753Smm	return (0);
267228753Smm}
268228753Smm
269228753Smm/*-
270228753Smm * The logic here for -C <dir> attempts to avoid
271228753Smm * chdir() as long as possible.  For example:
272228753Smm * "-C /foo -C /bar file"          needs chdir("/bar") but not chdir("/foo")
273228753Smm * "-C /foo -C bar file"           needs chdir("/foo/bar")
274228753Smm * "-C /foo -C bar /file1"         does not need chdir()
275228753Smm * "-C /foo -C bar /file1 file2"   needs chdir("/foo/bar") before file2
276228753Smm *
277228753Smm * The only correct way to handle this is to record a "pending" chdir
278228753Smm * request and combine multiple requests intelligently until we
279228753Smm * need to process a non-absolute file.  set_chdir() adds the new dir
280228753Smm * to the pending list; do_chdir() actually executes any pending chdir.
281228753Smm *
282228753Smm * This way, programs that build tar command lines don't have to worry
283228753Smm * about -C with non-existent directories; such requests will only
284228753Smm * fail if the directory must be accessed.
285228753Smm *
286228753Smm */
287228753Smmvoid
288228753Smmset_chdir(struct bsdtar *bsdtar, const char *newdir)
289228753Smm{
290232153Smm#if defined(_WIN32) && !defined(__CYGWIN__)
291232153Smm	if (newdir[0] == '/' || newdir[0] == '\\' ||
292232153Smm	    /* Detect this type, for example, "C:\" or "C:/" */
293232153Smm	    (((newdir[0] >= 'a' && newdir[0] <= 'z') ||
294232153Smm	      (newdir[0] >= 'A' && newdir[0] <= 'Z')) &&
295232153Smm	    newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) {
296232153Smm#else
297228753Smm	if (newdir[0] == '/') {
298232153Smm#endif
299228753Smm		/* The -C /foo -C /bar case; dump first one. */
300228753Smm		free(bsdtar->pending_chdir);
301228753Smm		bsdtar->pending_chdir = NULL;
302228753Smm	}
303228753Smm	if (bsdtar->pending_chdir == NULL)
304228753Smm		/* Easy case: no previously-saved dir. */
305228753Smm		bsdtar->pending_chdir = strdup(newdir);
306228753Smm	else {
307228753Smm		/* The -C /foo -C bar case; concatenate */
308228753Smm		char *old_pending = bsdtar->pending_chdir;
309228753Smm		size_t old_len = strlen(old_pending);
310228753Smm		bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2);
311228753Smm		if (old_pending[old_len - 1] == '/')
312228753Smm			old_pending[old_len - 1] = '\0';
313228753Smm		if (bsdtar->pending_chdir != NULL)
314228753Smm			sprintf(bsdtar->pending_chdir, "%s/%s",
315228753Smm			    old_pending, newdir);
316228753Smm		free(old_pending);
317228753Smm	}
318228753Smm	if (bsdtar->pending_chdir == NULL)
319228753Smm		lafe_errc(1, errno, "No memory");
320228753Smm}
321228753Smm
322228753Smmvoid
323228753Smmdo_chdir(struct bsdtar *bsdtar)
324228753Smm{
325228753Smm	if (bsdtar->pending_chdir == NULL)
326228753Smm		return;
327228753Smm
328228753Smm	if (chdir(bsdtar->pending_chdir) != 0) {
329228753Smm		lafe_errc(1, 0, "could not chdir to '%s'\n",
330228753Smm		    bsdtar->pending_chdir);
331228753Smm	}
332228753Smm	free(bsdtar->pending_chdir);
333228753Smm	bsdtar->pending_chdir = NULL;
334228753Smm}
335228753Smm
336228753Smmstatic const char *
337228753Smmstrip_components(const char *p, int elements)
338228753Smm{
339228753Smm	/* Skip as many elements as necessary. */
340228753Smm	while (elements > 0) {
341228753Smm		switch (*p++) {
342228753Smm		case '/':
343228753Smm#if defined(_WIN32) && !defined(__CYGWIN__)
344228753Smm		case '\\': /* Support \ path sep on Windows ONLY. */
345228753Smm#endif
346228753Smm			elements--;
347228753Smm			break;
348228753Smm		case '\0':
349228753Smm			/* Path is too short, skip it. */
350228753Smm			return (NULL);
351228753Smm		}
352228753Smm	}
353228753Smm
354228753Smm	/* Skip any / characters.  This handles short paths that have
355228753Smm	 * additional / termination.  This also handles the case where
356228753Smm	 * the logic above stops in the middle of a duplicate //
357228753Smm	 * sequence (which would otherwise get converted to an
358228753Smm	 * absolute path). */
359228753Smm	for (;;) {
360228753Smm		switch (*p) {
361228753Smm		case '/':
362228753Smm#if defined(_WIN32) && !defined(__CYGWIN__)
363228753Smm		case '\\': /* Support \ path sep on Windows ONLY. */
364228753Smm#endif
365228753Smm			++p;
366228753Smm			break;
367228753Smm		case '\0':
368228753Smm			return (NULL);
369228753Smm		default:
370228753Smm			return (p);
371228753Smm		}
372228753Smm	}
373228753Smm}
374228753Smm
375228753Smm/*
376228753Smm * Handle --strip-components and any future path-rewriting options.
377228753Smm * Returns non-zero if the pathname should not be extracted.
378228753Smm *
379228753Smm * TODO: Support pax-style regex path rewrites.
380228753Smm */
381228753Smmint
382228753Smmedit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
383228753Smm{
384228753Smm	const char *name = archive_entry_pathname(entry);
385248616Smm#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
386228753Smm	char *subst_name;
387228753Smm	int r;
388228753Smm
389232153Smm	r = apply_substitution(bsdtar, name, &subst_name, 0, 0);
390228753Smm	if (r == -1) {
391228753Smm		lafe_warnc(0, "Invalid substitution, skipping entry");
392228753Smm		return 1;
393228753Smm	}
394228753Smm	if (r == 1) {
395228753Smm		archive_entry_copy_pathname(entry, subst_name);
396228753Smm		if (*subst_name == '\0') {
397228753Smm			free(subst_name);
398228753Smm			return -1;
399228753Smm		} else
400228753Smm			free(subst_name);
401228753Smm		name = archive_entry_pathname(entry);
402228753Smm	}
403228753Smm
404228753Smm	if (archive_entry_hardlink(entry)) {
405232153Smm		r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 0, 1);
406228753Smm		if (r == -1) {
407228753Smm			lafe_warnc(0, "Invalid substitution, skipping entry");
408228753Smm			return 1;
409228753Smm		}
410228753Smm		if (r == 1) {
411228753Smm			archive_entry_copy_hardlink(entry, subst_name);
412228753Smm			free(subst_name);
413228753Smm		}
414228753Smm	}
415228753Smm	if (archive_entry_symlink(entry) != NULL) {
416232153Smm		r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0);
417228753Smm		if (r == -1) {
418228753Smm			lafe_warnc(0, "Invalid substitution, skipping entry");
419228753Smm			return 1;
420228753Smm		}
421228753Smm		if (r == 1) {
422228753Smm			archive_entry_copy_symlink(entry, subst_name);
423228753Smm			free(subst_name);
424228753Smm		}
425228753Smm	}
426228753Smm#endif
427228753Smm
428228753Smm	/* Strip leading dir names as per --strip-components option. */
429228753Smm	if (bsdtar->strip_components > 0) {
430228753Smm		const char *linkname = archive_entry_hardlink(entry);
431228753Smm
432228753Smm		name = strip_components(name, bsdtar->strip_components);
433228753Smm		if (name == NULL)
434228753Smm			return (1);
435228753Smm
436228753Smm		if (linkname != NULL) {
437228753Smm			linkname = strip_components(linkname,
438228753Smm			    bsdtar->strip_components);
439228753Smm			if (linkname == NULL)
440228753Smm				return (1);
441228753Smm			archive_entry_copy_hardlink(entry, linkname);
442228753Smm		}
443228753Smm	}
444228753Smm
445228753Smm	/* By default, don't write or restore absolute pathnames. */
446228753Smm	if (!bsdtar->option_absolute_paths) {
447228753Smm		const char *rp, *p = name;
448228753Smm		int slashonly = 1;
449228753Smm
450228753Smm		/* Remove leading "//./" or "//?/" or "//?/UNC/"
451228753Smm		 * (absolute path prefixes used by Windows API) */
452228753Smm		if ((p[0] == '/' || p[0] == '\\') &&
453228753Smm		    (p[1] == '/' || p[1] == '\\') &&
454228753Smm		    (p[2] == '.' || p[2] == '?') &&
455228753Smm		    (p[3] == '/' || p[3] == '\\'))
456228753Smm		{
457228753Smm			if (p[2] == '?' &&
458228753Smm			    (p[4] == 'U' || p[4] == 'u') &&
459228753Smm			    (p[5] == 'N' || p[5] == 'n') &&
460228753Smm			    (p[6] == 'C' || p[6] == 'c') &&
461228753Smm			    (p[7] == '/' || p[7] == '\\'))
462228753Smm				p += 8;
463228753Smm			else
464228753Smm				p += 4;
465228753Smm			slashonly = 0;
466228753Smm		}
467228753Smm		do {
468228753Smm			rp = p;
469228753Smm			/* Remove leading drive letter from archives created
470228753Smm			 * on Windows. */
471228753Smm			if (((p[0] >= 'a' && p[0] <= 'z') ||
472228753Smm			     (p[0] >= 'A' && p[0] <= 'Z')) &&
473228753Smm				 p[1] == ':') {
474228753Smm				p += 2;
475228753Smm				slashonly = 0;
476228753Smm			}
477228753Smm			/* Remove leading "/../", "//", etc. */
478228753Smm			while (p[0] == '/' || p[0] == '\\') {
479228753Smm				if (p[1] == '.' && p[2] == '.' &&
480228753Smm					(p[3] == '/' || p[3] == '\\')) {
481228753Smm					p += 3; /* Remove "/..", leave "/"
482228753Smm							 * for next pass. */
483228753Smm					slashonly = 0;
484228753Smm				} else
485228753Smm					p += 1; /* Remove "/". */
486228753Smm			}
487228753Smm		} while (rp != p);
488228753Smm
489228753Smm		if (p != name && !bsdtar->warned_lead_slash) {
490228753Smm			/* Generate a warning the first time this happens. */
491228753Smm			if (slashonly)
492228753Smm				lafe_warnc(0,
493228753Smm				    "Removing leading '%c' from member names",
494228753Smm				    name[0]);
495228753Smm			else
496228753Smm				lafe_warnc(0,
497228753Smm				    "Removing leading drive letter from "
498228753Smm				    "member names");
499228753Smm			bsdtar->warned_lead_slash = 1;
500228753Smm		}
501228753Smm
502228753Smm		/* Special case: Stripping everything yields ".". */
503228753Smm		if (*p == '\0')
504228753Smm			name = ".";
505228753Smm		else
506228753Smm			name = p;
507228753Smm	} else {
508228753Smm		/* Strip redundant leading '/' characters. */
509228753Smm		while (name[0] == '/' && name[1] == '/')
510228753Smm			name++;
511228753Smm	}
512228753Smm
513228753Smm	/* Safely replace name in archive_entry. */
514228753Smm	if (name != archive_entry_pathname(entry)) {
515228753Smm		char *q = strdup(name);
516228753Smm		archive_entry_copy_pathname(entry, q);
517228753Smm		free(q);
518228753Smm	}
519228753Smm	return (0);
520228753Smm}
521228753Smm
522228753Smm/*
523228753Smm * It would be nice to just use printf() for formatting large numbers,
524228753Smm * but the compatibility problems are quite a headache.  Hence the
525228753Smm * following simple utility function.
526228753Smm */
527228753Smmconst char *
528228753Smmtar_i64toa(int64_t n0)
529228753Smm{
530228753Smm	static char buff[24];
531232153Smm	uint64_t n = n0 < 0 ? -n0 : n0;
532228753Smm	char *p = buff + sizeof(buff);
533228753Smm
534228753Smm	*--p = '\0';
535228753Smm	do {
536228753Smm		*--p = '0' + (int)(n % 10);
537232153Smm	} while (n /= 10);
538228753Smm	if (n0 < 0)
539228753Smm		*--p = '-';
540228753Smm	return p;
541228753Smm}
542228753Smm
543228753Smm/*
544228753Smm * Like strcmp(), but try to be a little more aware of the fact that
545228753Smm * we're comparing two paths.  Right now, it just handles leading
546228753Smm * "./" and trailing '/' specially, so that "a/b/" == "./a/b"
547228753Smm *
548228753Smm * TODO: Make this better, so that "./a//b/./c/" == "a/b/c"
549228753Smm * TODO: After this works, push it down into libarchive.
550228753Smm * TODO: Publish the path normalization routines in libarchive so
551228753Smm * that bsdtar can normalize paths and use fast strcmp() instead
552228753Smm * of this.
553228753Smm *
554228753Smm * Note: This is currently only used within write.c, so should
555228753Smm * not handle \ path separators.
556228753Smm */
557228753Smm
558228753Smmint
559228753Smmpathcmp(const char *a, const char *b)
560228753Smm{
561228753Smm	/* Skip leading './' */
562228753Smm	if (a[0] == '.' && a[1] == '/' && a[2] != '\0')
563228753Smm		a += 2;
564228753Smm	if (b[0] == '.' && b[1] == '/' && b[2] != '\0')
565228753Smm		b += 2;
566228753Smm	/* Find the first difference, or return (0) if none. */
567228753Smm	while (*a == *b) {
568228753Smm		if (*a == '\0')
569228753Smm			return (0);
570228753Smm		a++;
571228753Smm		b++;
572228753Smm	}
573228753Smm	/*
574228753Smm	 * If one ends in '/' and the other one doesn't,
575228753Smm	 * they're the same.
576228753Smm	 */
577228753Smm	if (a[0] == '/' && a[1] == '\0' && b[0] == '\0')
578228753Smm		return (0);
579228753Smm	if (a[0] == '\0' && b[0] == '/' && b[1] == '\0')
580228753Smm		return (0);
581228753Smm	/* They're really different, return the correct sign. */
582228753Smm	return (*(const unsigned char *)a - *(const unsigned char *)b);
583228753Smm}
584