/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1987-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* pax -- portable archive interchange
*
* test registry:
*
* 0000010 dump option table
* 0000020 force DELTA_TEMP to file
* 0000040 pretend io device is char special
* 0000100 don't copy in holes
* 0000200 sleep(1) between meter displays
*/
static const char usage[] =
"[-?\n@(#)$Id: pax (AT&T Research) 2012-05-25 $\n]"
"[+NAME?pax - read, write, and list file archives]"
"[+DESCRIPTION?The pax command reads, writes, and lists archive files in"
" various formats. There are four operation modes controlled by"
" combinations of the -\br\b and -\bw\b options.]"
"[+?\bpax -w\b writes the files and directories named by the \apathname\a"
" arguments to the standard output together with pathname and status"
" information. A directory \apathname\a argument refers to the files and"
" (recursively) subdirectories of that directory. If no \apathname\a"
" arguments are given then the standard input is read to get a list of"
" pathnames to copy, one pathname per line. In this case only those"
" pathnames appearing on the standard input are copied.]"
"[+?\bpax -r\b reads the standard input that is assumed to be the result of a"
" previous \bpax -w\b command. Only member files with names"
" that match any of the \apattern\a arguments are selected. Matching"
" is done before any \b-i\b or \b-s\b options are applied. A"
" \apattern\a is given in the name-generating notation of \bsh\b(1),"
" except that the \b/\b character is also matched. The default if no"
" \apattern\a is given is \b*\b which selects all files. The selected"
" files are conditionally created and copied relative to the current"
" directory tree, subject to the options described below. By default the"
" owner and group of selected files will be that of the current user, and"
" the permissions and modify times will be the same as those in the"
" archive.]"
"[+?\bpax -rw\b reads the files and directories named in the \apathname\a"
" arguments and copies them to the destination \adirectory\a."
" A directory \apathname\a argument refers to the files and (recursively)"
" subdirectories of that directory. If no \apathname\a arguments are"
" given then the standard input is read to get a list of pathnames to"
" copy, one pathname per l, lineine. In this case only those pathnames"
" appearing on the standard input are copied. \adirectory\a must exist"
" before the copy.]"
"[+?\bpax\b (\b-r\b and \b-w\b omitted) reads the standard input that is"
" assumed to be the result of a previous \bpax -w\b command and lists"
" table of contents of the selected member files on the standard output.]"
"[+?The standard archive formats are automatically detected on input."
" The default output archive format is \b\fdefault\f\b, but may be"
" overridden by the \b-x\b option described below. \bpax\b archives may"
" be concatenated to combine multiple volumes on a single tape or file."
" This is accomplished by forcing any format prescribed pad data to be"
" null bytes. Hard links are not maintained between volumes, and delta"
" and base archives cannot be multi-volume.]"
" subsequent file names are prompted for on the terminal input. The"
" response may be:]{"
" [+!command?Execute \acommand\a via \bsystem\b(3) and prompt"
" again for file name.]"
" [+EOF?Terminate processing and exit.]"
" [+CR?An empty input line retains the previous file name.]"
" [+pathname?The file name for the next archive part.]"
"}"
"[+?\bgetconf PATH_RESOLVE\b determines how symbolic links are handled. This"
" can be explicitly overridden by the \b--logical\b, \b--metaphysical\b,"
" and \b--physical\b options below. \bPATH_RESOLVE\b can be one of:]{"
" [+logical?Follow all symbolic links.]"
" [+metaphysical?Follow command argument symbolic links,"
" otherwise don't follow.]"
" [+physical?Don't follow symbolic links.]"
"}"
;
/* state.usage is generated at runtime from usage+options+usage2 */
static const char usage2[] =
"\n"
"[ pathname ... ]\n"
"[ pattern ... ]\n"
"[ pathname ... directory ]\n"
"\n"
"[+DIAGNOSTICS?The number of files, blocks, and optionally the number"
" of volumes and media parts are listed on the standard error."
" For -\bv\b the input archive formats are also listed on the"
" standard error.]"
"[+EXAMPLES]{"
" [+pax -w -t 1m .?Copies the contents of the current directory to"
" tape drive 1, medium density.]"
" [+mkdir newdir; cd olddir; pax -rw . newdir?Copies the"
" \aolddir\a directory hierarchy to \anewdir\a.]"
"}"
"[+SEE ALSO?\bar\b(1), \bcpio\b(1), \bfind\b(1), \bgetconf\b(1), \bgzip\b(1),"
" \bksh\b(1), \bratz\b(1), \bstar\b(1), \btar\b(1), \btw\b(1),"
" \bfsync\b(2), \blibdelta\b(3), \bcpio\b(5), \btar\b(5)]"
"[+BUGS?Special privileges may be required to copy special files."
" Some archive formats have hard upper limits on member string, numeric"
" and data sizes. Attribute values larger than the standard min-max"
" values may cause additional header or embedded data records to be"
" output for some formats; these records are ignored by old versions"
" of \bcpio\b(1) and \btar\b(1). \b--format=pax\b avoids size and"
" portability limitations \abut\a requires a working reader on the"
" receiving side.]"
;
#include "pax.h"
#include <ardir.h>
#include <iconv.h>
#include <tm.h>
{
#if !DEBUG
#endif
};
static struct
{
char* arg0;
char* owner;
} opt;
/*
* clean up dir info before exit
*/
static void
{
switch (sig)
{
case SIGINT:
case SIGQUIT:
break;
}
finish(1);
}
/*
* enter new substitute expression(s)
*/
static void
{
int c;
for (;;)
{
while (isspace(*s))
s++;
if (!*s)
break;
{
}
if (c)
for (;;)
{
switch (*s++)
{
case 'i':
continue;
}
s--;
break;
}
if (*s && !isspace(*s))
if (*lastmap)
else
}
}
/*
* clear meter line for each error message
*/
static ssize_t
{
{
}
}
/*
* construct an action from command
*/
static Filter_t*
{
register char* s;
register char* t;
register int c;
register int q;
register int n;
s = (char*)command;
if (pattern && *s && *s == *(s + 1))
{
s += 2;
pattern = 0;
}
if (pattern)
{
nospace();
if (c = regcomp(re, s, REG_SHELL|REG_AUGMENTED|REG_DELIMITED|REG_LENIENT|REG_NULL|REG_LEFT|REG_RIGHT))
}
else
re = 0;
command = (const char*)s;
q = 0;
n = 3;
while (c = *s++)
if (c == '"' || c == '\'')
{
if (!q)
q = c;
else if (q == c)
q = 0;
}
else if (c == '\\')
{
if (q != '\'' && *s)
s++;
}
else if (!q && isspace(c))
{
n++;
while (isspace(*s))
s++;
}
nospace();
q = 0;
n = 1;
t = s;
while (c = *s++)
if (c == '"' || c == '\'')
{
if (!q)
q = c;
else if (q == c)
q = 0;
else
*t++ = c;
}
else if (c == '\\')
{
if (q == '\'')
*t++ = c;
else if (*s)
*t++ = *s++;
}
else if (q || !isspace(c))
*t++ = c;
else
{
*t++ = 0;
while (isspace(*s))
s++;
if (*(t = s))
}
*t = 0;
return fp;
}
/*
* return action for f, 0 if no match
*/
{
do
{
return fp;
return 0;
}
/*
* set options from line if != 0 or argv according to usage
* type: 0:command EXTTYPE:extended GLBTYPE:global
*/
void
{
intmax_t n;
int c;
int y;
int assignment;
int cvt;
int index;
int offset;
int from;
int to;
char* e;
char* s;
char* v;
char* o;
char* end;
cvt = 0;
for (;;)
{
if (hdr)
{
if (!*line)
break;
line++;
s = line;
y = 0;
y = y * 10 + (c - '0');
if ((e = (s + y - 1)) > end)
e = end;
else
*e++ = 0;
line++;
o = line;
assignment = 0;
for (;;)
{
switch (*line++)
{
case 0:
line--;
break;
case '=':
*(line - 1) = 0;
break;
case ':':
if (*line == '=')
{
*(line - 1) = 0;
line++;
assignment = 1;
break;
}
continue;
default:
continue;
}
break;
}
v = line;
line = e;
y = 1;
{
s = o;
{
o += sizeof(VENDOR);
}
{
o += 2;
y = 0;
}
if (!op)
{
continue;
}
}
if (!y)
n = 0;
n = 1;
else
{
if (*e)
}
}
break;
else if (c > 0)
{
if (c == '?')
continue;
}
else
{
v = "";
else if (!n)
y = 1;
}
/*
* option precedence levels
*
* 8 ignore all
* 7 command:=
* 6 ignore extended
* 5 extended:=
* 4 extended=
* 3 command=
* 2 global:=
* 1 global=
*/
switch (type)
{
case EXTTYPE:
c = 4;
break;
case GLBTYPE:
c = 1;
break;
default:
c = 3;
break;
}
c += assignment;
continue;
{
else
if (*v)
{
}
else
vp = 0;
}
else
vp = 0;
message((-4, "option: %c %s%s%s=%s entry=%d:%d level=%d:%d number=%I*u", type ? type : '-', y ? "" : "no", op->name, assignment ? ":" : "", v, op->entry, ap ? ap->entry : 0, op->level, c, sizeof(n), n));
{
case OPT_action:
if (*v)
{
else
{
else
}
}
break;
case OPT_append:
break;
case OPT_atime:
if (vp)
{
if (*e)
{
if (*e != '.')
if (*e == '.')
if (*e)
{
y = e - s;
for (y = e - s; y < 9; y++)
for (; y > 9; y--)
}
}
}
break;
case OPT_base:
if (y)
{
if (!*v || streq(v, "-"))
{
state.delta2delta++;
{
break;
}
}
}
break;
case OPT_blocksize:
if (y)
{
}
else
break;
case OPT_blok:
if (!*v)
else
while (*v) switch (*v++)
{
case 'i':
break;
case 'o':
break;
default:
break;
}
break;
case OPT_checksum:
if (y)
{
if (e = strchr(v, ':'))
*e++ = 0;
else
{
e = v;
v = "md5";
}
}
else
break;
case OPT_chmod:
if (y && *v)
{
strperm(v, &e, 0);
if (*e)
}
else
break;
case OPT_clobber:
break;
case OPT_comment:
break;
case OPT_complete:
break;
case OPT_crossdevice:
if (!y)
else
break;
case OPT_ctime:
if (vp)
goto settime;
break;
case OPT_debug:
if (y)
{
y = error_info.trace;
error_info.trace = -(int)n;
if (!y)
}
else
error_info.trace = 0;
break;
case OPT_delete:
if (y && *v)
break;
case OPT_delta_base_checksum:
break;
case OPT_delta_base_size:
break;
case OPT_delta_checksum:
if (ap)
break;
case OPT_delta_compress:
break;
case OPT_delta_index:
if (ap)
{
{
{
if (c > 0)
else
}
}
else
}
break;
case OPT_delta_method:
if (ap)
{
}
break;
case OPT_delta_op:
break;
case OPT_delta_ordered:
break;
case OPT_delta_update:
break;
case OPT_delta_version:
break;
case OPT_descend:
break;
case OPT_different:
case OPT_newer:
case OPT_update:
break;
case OPT_dots:
break;
case OPT_edit:
break;
case OPT_eom:
break;
case OPT_exact:
break;
case OPT_extended_name:
break;
case OPT_file:
break;
case OPT_filter:
if (y && *v)
{
}
else
break;
case OPT_format:
if (!y)
else if (s = strdup(v))
{
v = s;
do
{
for (e = s, o = 0;;)
{
switch (*e++)
{
case 0:
e = 0;
break;
case ' ':
case '\t':
case '\n':
case ':':
case ',':
case '.':
*(e - 1) = 0;
if (*s)
break;
s = e;
continue;
case '=':
if (!o)
{
*(e - 1) = 0;
o = e;
}
continue;
default:
continue;
}
break;
}
if (!(fp = getformat(s, 0)) && s == v && s[0] == 't' && !strchr(s, ':') && (fp = getformat(s + 1, 0)))
{
s++;
else
fp = 0;
}
if (!fp)
{
if (!pathpath("lib/pax", opt.arg0, PATH_EXECUTE, tmp1, sizeof(tmp1)) || sfsprintf(tmp2, sizeof(tmp2) - 1, "%s/%s.fmt", tmp1, s) <= 0 || !(sp = sfopen(NiL, tmp2, "r")))
if (*e != '#')
{
}
}
else
{
{
case ARCHIVE:
break;
case COMPRESS:
break;
case DELTA:
break;
}
}
} while (s = e);
}
break;
case OPT_from:
case OPT_to:
if (!cvt)
{
cvt = 1;
}
if ((y = ccmapid(v)) < 0)
{
case OPT_from:
from = y;
break;
case OPT_to:
to = y;
break;
}
break;
case OPT_global_name:
break;
case OPT_header:
v = y ? strdup(v) : (char*)0;
if (assignment)
else
break;
case OPT_ignore:
if (y && *v)
{
if (assignment)
else
}
break;
case OPT_install:
break;
case OPT_intermediate:
state.intermediate = y;
break;
case OPT_invalid:
if (line)
{
n = 0;
y = strlen(v);
while (s = strchr(s, '['))
{
c = *++s;
o = ++s;
for (;;)
{
if (strneq(v, o, y))
{
s = "";
n = c;
break;
}
if (!(o = strchr(o, '|')))
break;
o++;
}
}
}
switch ((int)n)
{
case 'b':
break;
case 'i':
break;
case 'p':
break;
case 't':
break;
case 'u':
break;
default:
break;
}
break;
case OPT_invert:
state.matchsense = !y;
break;
case OPT_keepgoing:
break;
case OPT_label:
{
if (assignment)
else
v = tmp1;
}
break;
case OPT_link:
if (y)
else
break;
case OPT_linkdata:
break;
case OPT_listformat:
if (y && *v)
break;
case OPT_listmacro:
if (y && *v)
{
if (s = strchr(v, '='))
*s++ = 0;
{
if (!s)
break;
nospace();
}
if (s)
{
*(s - 1) = 0;
}
else
}
break;
case OPT_local:
break;
case OPT_logical:
if (y)
else
break;
case OPT_maxout:
break;
case OPT_metaphysical:
if (y)
else
break;
case OPT_meter:
{
nospace();
{
}
}
break;
case OPT_mkdir:
break;
case OPT_mtime:
if (vp)
goto settime;
break;
case OPT_options:
if (v)
{
}
break;
case OPT_ordered:
break;
case OPT_owner:
else if (*v)
break;
case OPT_passphrase:
break;
case OPT_physical:
if (y)
{
}
else
break;
case OPT_preserve:
for (;;)
{
switch (*v++)
{
case 0:
break;
case 'a':
continue;
case 'e':
continue;
case 'm':
continue;
case 'o':
continue;
case 'p':
continue;
case 's':
continue;
default:
continue;
}
break;
}
break;
case OPT_read:
if (y)
else
break;
case OPT_record_charset:
break;
case OPT_record_delimiter:
if (!y)
else
break;
case OPT_record_format:
break;
case OPT_record_header:
if (!y)
{
}
break;
case OPT_record_line:
break;
case OPT_record_match:
break;
case OPT_record_pad:
break;
case OPT_record_size:
break;
case OPT_record_trailer:
if (!y)
{
}
break;
case OPT_reset_atime:
state.resetacctime = y;
break;
case OPT_size:
break;
case OPT_strict:
break;
case OPT_summary:
break;
case OPT_symlink:
if (y)
else
break;
case OPT_sync:
#if _lib_fsync
#else
#endif
break;
case OPT_tape:
s = strtape(v, &e);
if (*s)
for (;;)
{
switch (*e++)
{
case 'k':
if (!(n = strtonll(e, &e, 0, 1)))
n = -1;
continue;
case 's':
if (!(n = strtonll(e, &e, 0, 1)))
n = -1;
continue;
}
e--;
break;
}
if (*e)
break;
case OPT_test:
if (y)
else
break;
case OPT_testdate:
if (y)
{
if (*e)
}
else
break;
case OPT_times:
if (y)
{
}
break;
case OPT_unblocked:
if (!*v)
else
while (*v) switch (*v++)
{
case 'i':
break;
case 'o':
break;
default:
break;
}
break;
case OPT_uncompressed:
break;
case OPT_verbose:
break;
case OPT_verify:
break;
case OPT_warn:
break;
case OPT_write:
if (y)
else
{
}
break;
case OPT_yes:
break;
default:
break;
}
}
{
}
if (cvt)
{
}
}
/*
* option match with VENDOR check
*/
static int
{
return strmatch(name, pattern) || (op->flags & OPT_VENDOR) && strmatch(sfprints("%s.%s", VENDOR, name), pattern);
}
/*
* mark ignored header keywords
*/
static void
ignore(void)
{
char* all;
char* ext;
int lev;
all = 0;
nospace();
ext = 0;
nospace();
{
{
if (!(op->flags & OPT_READONLY) && (all && matchopt(pos->bucket->name, all, op) && (lev = 8) || ext && matchopt(pos->bucket->name, ext, op) && (lev = 6)) && op->level < lev)
}
}
}
/*
* list fp for optinfo()
*/
static void
{
register const char* p;
register int c;
{
if (*p == '(')
p++;
while (c = *p++)
{
if (c == ')' && !*p)
break;
if (c == '?' || c == ']')
}
}
while (c = *p++)
{
if (c == ']')
}
{
case 0:
break;
case IN:
break;
case OUT:
break;
}
}
/*
* optget() info discipline function
*/
static int
{
register const char* p;
register int i;
register int c;
switch (*s)
{
case 'c':
{
if (*p == '(')
p++;
while (c = *p++)
{
if (c == ')' && !*p)
break;
if (c == '?' || c == ']')
}
while (c = *p++)
{
if (c == ']')
}
}
break;
case 'd':
break;
case 'D':
fp = 0;
{
while (c = *p++)
{
if (c == ']')
}
}
break;
case 'f':
fp = 0;
ar = 0;
fp = 0;
fp = 0;
break;
break;
case 'l':
{
}
" [+----?subformats ----]"
" [+case\b::\bp\b\a1\a::\bs\b\a1\a::...::\bp\b\an\a::\bs\b\an\a?Expands"
" to \bs\b\ai\a if the value of \aid\a matches the shell"
" pattern \bp\b\ai\a, or the empty string if there is no"
" match.]"
" [+mode?The integral value as a \bfmtmode\b(3) string.]"
" [+perm?The integral value as a \bfmtperm\b(3) string.]"
" [+time[=\aformat\a]]?The integral value as a \bstrftime\b(3)"
" string. For example,"
" \b--format=\"%8(mtime)u %(ctime:time=%H:%M:%S)s\"\b"
" lists the mtime in seconds since the epoch and the"
" ctime as hours:minutes:seconds.]");
break;
}
return 0;
}
int
{
register int i;
register char* s;
char* p;
int n;
int pass = 0;
unsigned long blocksize;
nospace();
nospace();
if (!(state.tmp.fmt = sfstropen()) || !(state.tmp.lst = sfstropen()) || !(state.tmp.str = sfstropen()))
nospace();
nospace();
{
if (strchr(p, '|'))
p = strdup(p);
do
{
if (s = strchr(p, '|'))
*s++ = 0;
} while (p = s);
}
nospace();
{
{
}
{
}
{
}
}
nospace();
if (error_info.errors)
{
}
sfputr(opt.listformat, (state.list && state.verbose) ? "%(mode)s %2(nlink)d %-8(uname)s %-8(gname)s%8(device:case::%(size)llu:*:%(device)s)s %(mtime)s %(delta.op:case:?*:%(delta.op)s )s%(path)s%(linkop:case:?*: %(linkop)s %(linkpath)s)s" : "%(delta.op:case:?*:%(delta.op)s )s%(path)s%(linkop:case:?*: %(linkop)s %(linkpath)s)s", ' ');
nospace();
ignore();
{
if (streq(s, "-"))
{
s = "sh -c";
}
}
else
/*
* determine the buffer sizes
*/
{
break;
/*FALLTHROUGH*/
case IN:
case OUT:
break;
}
{
pass = 1;
{
}
else
{
close(1);
}
{
}
{
{
}
else
}
}
else
{
else
}
{
else
{
close(0);
}
{
}
}
nospace();
message((-1, "blocksize=%d buffersize=%d recordsize=%d", state.blocksize, state.buffersize, state.record.size));
/*
* initialize the main io
*/
{
case IN:
case OUT:
break;
}
{
{
}
}
{
{
{
}
{
}
else
{
}
}
{
List_t* p;
i = 1;
i++;
i++;
cmd[i] = 0;
close(1);
if (dup(n) != 1)
close(n);
nospace();
}
{
nospace();
}
{
nospace();
}
}
if (!(state.linktab = hashalloc(NiL, HASH_set, HASH_ALLOCATE, HASH_namesize, sizeof(Fileid_t), HASH_name, "links", 0)))
if ((state.operation & IN) && !state.list && !(state.restore = hashalloc(NiL, HASH_set, HASH_ALLOCATE, HASH_name, "restore", 0)))
{
{
{
}
}
else
}
interactive();
umask(0);
#if DEBUG
{
{
sfprintf(sfstderr, "%-16s %c %2d %d perm=%ld:%s temp=%ld:%s%s%s\n", op->name, op->flag ? op->flag : '-', op->index, op->level, op->perm.number, op->perm.string, op->temp.number, op->temp.string, (op->flags & OPT_HEADER) ? " HEADER" : "", (op->flags & OPT_READONLY) ? " READONLY" : "");
}
}
#endif
for (i = 0; i < elementsof(signals); i++)
{
case IN:
if (*argv)
{
}
break;
case OUT:
if (*argv)
break;
{
if (*argv)
}
else
{
if (--argc < 0)
{
}
if (*argv)
/*
* initialize destination dir
*/
}
break;
}
finish(0);
}
/*
* print number of blocks actually copied and exit
*/
void
{
register off_t n;
{
}
{
}
{
}
{
message((-1, "%s totals entries=%d count=%I*d expand=%I*d offset=%I*d BLOCKSIZE=%I*d n=%I*d blocks=%I*d", ap->name, ap->entries, sizeof(ap->io->count), ap->io->count, sizeof(ap->io->expand), ap->io->expand, sizeof(ap->io->offset), ap->io->offset, sizeof(BLOCKSIZE), BLOCKSIZE, sizeof(n), n, sizeof(n), (n + BLOCKSIZE - 1) / BLOCKSIZE));
{
else
*x1 = 0;
else
*x2 = 0;
{
sfsprintf(x3, x, "%I*u file%s, ", sizeof(ap->selected), ap->selected, ap->selected == 1 ? "" : "s");
else
*x4 = 0;
}
else
}
}
{
pause();
}
}
/*
* return release stamp
*/
char*
release(void)
{
register char* b;
register char* s;
register char* t;
{
memcpy(b, s, t - s);
b[t - s] = 0;
}
else
return b;
}