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 + -
显示快捷键?