tar.c
来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 804 行 · 第 1/2 页
C
804 行
* acceptable to DOS. The routine returns a pointer to this string.
* The former step fixes names that come from DOS to be Unix-compatible;
* it will never change Unix filenames, except to make uppercase letters
* lowercase, because they are already Unix-compatible. The latter step
* fixes names that come from Unix to be DOS-compatible; it will never
* change DOS filenames, because they are already DOS-compatible.
*
* The translation of uppercase letters to lowercase ones in unix filenames
* that appeared in a tar file is a side-effect of the dual purpose of
* fixname; its only effect is that listings of filenames in a tar file
* will always be lowercase. An improvement might be to separate fixname
* into one routine to fix DOS names, and another to fix Unix names,
* but this is left for a future enhancement.
*
* Even in non-DOS environments, fixname() must at least return a pointer
* to a *copy* of the original filename, even if it is an unmodified
* copy. This is because this name string is used by tar at times after
* the string which was pointed to by 's' has been overwritten by other
* data. The present code does this properly.
*
* FIXME: This code is embarassingly complex and needs to be rewritten.
*/
char* fixname( char *s )
{
char *q;
char *prd;
char *lsl;
int name_cnt;
static char buf[256]; /* where the copy of the name is stored */
#ifdef MSDOS
/*
* CODE TO FIX DOS NAMES: DOS's filenames are always uppercase, though
* DOS maps lowercase characters to uppercase in filenames automatically.
* If we create the archive with these uppercase names, the files if
* un-tarred under Unix all have uppercase names. So we map the names to
* lowercase under DOS so that it works best for both. The same for \ vs
* /: DOS takes either, Unix needs /, so we use /. This first
* transformation occurs on the actual string we were passed, rather than
* a copy of it.
*/
strcpy(buf, s);
q = buf;
strlwr(q);
for (; *q; q++)
if (*q == '\\')
*q = '/';
/*
* CODE TO FIX UNIX NAMES: if more than one '.' in name, DOS won't create
* file. Delete all but last '.', then see if name longer than DOS
* allows. If so, prompt user for new name. (It might be better to
* always do the length check, but in this case it is more likely to be a
* problem since names with multiple dots often have fellow files with
* common left substrings which the part after the dot qualifies.)
*/
--q;
if( *q != '/' ) {
lsl = rindex(buf, '/');
if (lsl == NULL) {
lsl = buf;
} else {
lsl++;
}
prd = 0;
for (q = lsl; *q; q++)
{
if( *q == '.' ) {
if( prd != 0 ) break;
prd = q;
}
}
if( prd == 0 ) {
*q++ = '.';
}
*q = '\0';
q = index(lsl, '.');
if( q - lsl > 8 ) {
uprintf( ftty, "tar: the file name of %s is too long\n", buf );
strcpy( lsl+8, q );
uprintf( ftty, "tar: truncating to %s\n", buf );
q = index(lsl, '.');
}
if( strlen(q) > 4 ) {
uprintf( ftty, "tar: the file extension of %s is too long\n", buf );
q += 4;
*q = '\0';
uprintf( ftty, "tar: truncating to %s\n", buf );
}
/*
* prompt user for valid name, & check file existence, only if doing
* an "extract" operation with stdin not redirected.
*/
if( f_extract && !access(buf, 0) ) {
uprintf( ftty, "tar: %s already exists\n", buf );
name_cnt = 3;
if( prd == 0 ) {
strcat( buf, "002" );
} else if( strlen( prd ) == 2 ) {
strcat( buf, "02" );
} else if( strlen( prd ) == 3 ) {
strcat( buf, "2" );
} else {
name_cnt = 2;
}
q = buf + strlen( buf ) - 1;
while( !access(buf, 0) ) {
itoa( name_cnt, q, 10 );
if( name_cnt == 9 ) {
q--;
} else if( name_cnt == 99 ) {
q--;
}
name_cnt++;
}
uprintf( ftty, "tar: written as %s\n", buf );
}
q = buf + strlen(buf) - 1;
if( *q == '.' ) {
*q = '\0';
}
}
#endif
return (buf);
}
/*
* convmode(s) - return conversion mode bits for file with name s.
*
* This routine assumes filenames have the file type encoded in them
* in a standard way (e.g., MS/DOS extensions). It examines the
* name of the file, and returns any mode bits that need to be or'ed
* into the mode bits for the open (O_RDWR, etc. bits) for that particular
* file to be read such that it looks like a normal Unix file.
*
* Obviously, your OS has to meet all the above implied constraints, but at
* least this is a head start, I hope...
*/
int convmode( char * s )
{
char **p;
#ifdef MSDOS
while (*s)
{
if (*s == '.')
break;
s++;
}
if (*s == '\0')
s = " ."; /* special string for "no extension" */
/* it has space in front because of */
s++; /* <- that increment */
for (p = binexts; *p; p++)
{
if (strcmp(s, *p) == 0)
{
return (O_BINARY);
}
}
return (O_TEXT);
#else
/* for a Unix-like OS, always return 0 */
return (0);
#endif
}
#ifdef MSDOS
/*
* add an extension to the "binexts" list of file extensions that
* won't get O_BINARY translation (see convmode(), above)
*/
static void addbinext( char *s )
{
char **exts;
int n;
for (exts = binexts, n = 0; *exts; exts++, n++); /* find end */
if (n >= NBINEXTS - 1)
{
annofile(stderr, tar);
fprintf(stderr, "%s: too many extensions added (max=%d)\n",
s, NBINEXTS - 1);
exit(EX_ARGSBAD);
}
/* optional "." on front unless string is just "." */
if (s[0] == '.' && s[1] != '\0')
s++;
*exts++ = s;
*exts = 0;
}
#endif
/*
* Get the next name from argv or the name file.
*
* Result is in static storage and can't be relied upon across two calls.
*/
char *name_next( void )
{
static char buffer[NAMSIZ + 2]; /* Holding pattern */
char *p;
char *q;
if (namef == NULL)
{
/* Names come from argv, after options */
if (optind < n_argc)
{
return fixname(n_argv[optind++]);
}
return (char *) NULL;
}
p = fgets(buffer, NAMSIZ + 1 /* nl */ , namef);
if (p == NULL)
return p; /* End of file */
q = p + strlen(p) - 1; /* Find the newline */
*q-- = '\0'; /* Zap the newline */
while (*q == '/')
*q-- = '\0'; /* Zap trailing slashes too */
return fixname(p);
}
/*
* Close the name file, if any.
* BartoszP: used only in create.c
*/
void name_close( void )
{
if (namef != NULL && namef != stdin)
fclose(namef);
}
/*
* Gather names in a list for scanning.
* Could hash them later if we really care.
*
* If the names are already sorted to match the archive, we just
* read them one by one. name_gather reads the first one, and it
* is called by name_match as appropriate to read the next ones.
* At EOF, the last name read is just left in the buffer.
* This option lets users of small machines extract an arbitrary
* number of files by doing "tar t" and editing down the list of files.
*/
void name_gather( void )
{
char *p;
static struct name namebuff[1]; /* One-name buffer */
struct name *namebuf = namebuff;
if (f_sorted_names)
{
p = name_next();
if (p)
{
namebuf->length = strlen(p);
if (namebuf->length >= sizeof namebuf->name)
{
fprintf(stderr, "Argument name too long: %s\n",
p);
namebuf->length = (sizeof namebuf->name) - 1;
}
strncpy(namebuf->name, p, namebuf->length);
namebuf->next = (struct name *) NULL;
namebuf->found = 0;
namelist = namebuf;
namelast = namelist;
}
return;
}
/* Non sorted names -- read them all in */
while (NULL != (p = name_next()))
{
addname(p);
}
}
/*
* Add a name to the namelist.
*/
static void addname( char *name )
{
int i; /* Length of string */
struct name *p; /* Current struct pointer */
i = strlen(name);
/* NOSTRICT */
p = (struct name *)
malloc((unsigned) (i + sizeof(struct name) - NAMSIZ));
p->next = (struct name *) NULL;
p->length = i;
p->found = 0;
strncpy(p->name, name, i);
p->name[i] = '\0'; /* Null term */
if (namelast)
namelast->next = p;
namelast = p;
if (!namelist)
namelist = p;
}
/*
* Match a name from an archive, p, with a name from the namelist.
*
* FIXME: Allow regular expressions in the name list.
*/
int name_match( char *p )
{
struct name *nlp;
int len;
again:
if (0 == (nlp = namelist)) /* Empty namelist is easy */
return 1;
len = strlen(p);
for (; nlp != 0; nlp = nlp->next)
{
if (nlp->name[0] == p[0]/* First chars match */
&& nlp->length <= len /* Archive len >= specified */
&& (p[nlp->length] == '\0' || p[nlp->length] == '/')
/* Full match on file/dirname */
&& strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
{
nlp->found = 1; /* Remember it matched */
return 1; /* We got a match */
}
}
/*
* Filename from archive not found in namelist. If we have the whole
* namelist here, just return 0. Otherwise, read the next name in and
* compare it. If this was the last name, namelist->found will remain on.
* If not, we loop to compare the newly read name.
*/
if (f_sorted_names && namelist->found)
{
name_gather(); /* Read one more */
if (!namelist->found)
goto again;
}
return 0;
}
/*
* Print the names of things in the namelist that were not matched.
*/
void names_notfound( void )
{
struct name *nlp;
char *p;
for (nlp = namelist; nlp != 0; nlp = nlp->next)
{
if (!nlp->found)
{
fprintf(stderr, "tar: %s not found in archive\n",
nlp->name);
}
/*
* We could free() the list, but the process is about to die anyway,
* so save some CPU time. Amigas and other similarly broken software
* will need to waste the time, though.
*/
#ifndef unix
if (!f_sorted_names)
free(nlp);
#endif /* unix */
}
namelist = (struct name *) NULL;
namelast = (struct name *) NULL;
if (f_sorted_names)
{
while (0 != (p = name_next()))
fprintf(stderr, "tar: %s not found in archive\n", p);
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?