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