Annotation of 43BSDReno/bin/cp/cp.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1988 The Regents of the University of California.
        !             3:  * All rights reserved.
        !             4:  *
        !             5:  * This code is derived from software contributed to Berkeley by
        !             6:  * David Hitz of Auspex Systems Inc.
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms are permitted provided
        !             9:  * that: (1) source distributions retain this entire copyright notice and
        !            10:  * comment, and (2) distributions including binaries display the following
        !            11:  * acknowledgement:  ``This product includes software developed by the
        !            12:  * University of California, Berkeley and its contributors'' in the
        !            13:  * documentation or other materials provided with the distribution and in
        !            14:  * all advertising materials mentioning features or use of this software.
        !            15:  * Neither the name of the University nor the names of its contributors may
        !            16:  * be used to endorse or promote products derived from this software without
        !            17:  * specific prior written permission.
        !            18:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
        !            19:  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
        !            20:  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
        !            21:  */
        !            22: 
        !            23: #ifndef lint
        !            24: char copyright[] =
        !            25: "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
        !            26:  All rights reserved.\n";
        !            27: #endif /* not lint */
        !            28: 
        !            29: #ifndef lint
        !            30: static char sccsid[] = "@(#)cp.c       5.20 (Berkeley) 6/29/90";
        !            31: #endif /* not lint */
        !            32: 
        !            33: /*
        !            34:  * cp copies source files to target files.
        !            35:  * 
        !            36:  * The global PATH_T structures "to" and "from" always contain paths to the
        !            37:  * current source and target files, respectively.  Since cp does not change
        !            38:  * directories, these paths can be either absolute or dot-realative.
        !            39:  * 
        !            40:  * The basic algorithm is to initialize "to" and "from", and then call the
        !            41:  * recursive copy() function to do the actual work.  If "from" is a file,
        !            42:  * copy copies the data.  If "from" is a directory, copy creates the
        !            43:  * corresponding "to" directory, and calls itself recursively on all of
        !            44:  * the entries in the "from" directory.
        !            45:  */
        !            46: 
        !            47: #include <sys/param.h>
        !            48: #include <sys/stat.h>
        !            49: #include <sys/file.h>
        !            50: #include <sys/dir.h>
        !            51: #include <sys/time.h>
        !            52: #include <stdio.h>
        !            53: #include <errno.h>
        !            54: #include <stdlib.h>
        !            55: #include <string.h>
        !            56: 
        !            57: #define        type(st)        ((st).st_mode & S_IFMT)
        !            58: 
        !            59: typedef struct {
        !            60:        char    p_path[MAXPATHLEN + 1]; /* pointer to the start of a path. */
        !            61:        char    *p_end;                 /* pointer to NULL at end of path. */
        !            62: } PATH_T;
        !            63: 
        !            64: PATH_T from = { "", from.p_path };
        !            65: PATH_T to = { "", to.p_path };
        !            66: 
        !            67: uid_t myuid;
        !            68: int exit_val, myumask;
        !            69: int iflag, pflag, orflag, rflag;
        !            70: int (*statfcn)();
        !            71: char *buf, *pname;
        !            72: char *path_append(), *path_basename();
        !            73: 
        !            74: main(argc, argv)
        !            75:        int argc;
        !            76:        char **argv;
        !            77: {
        !            78:        extern int optind;
        !            79:        struct stat to_stat;
        !            80:        register int c, r;
        !            81:        int symfollow, lstat(), stat();
        !            82:        char *old_to, *p;
        !            83: 
        !            84:        /*
        !            85:         * cp is used by mv(1) -- except for usage statements, print
        !            86:         * the "called as" program name.
        !            87:         */
        !            88:        pname = (p = rindex(*argv,'/')) ? ++p : *argv;
        !            89: 
        !            90:        symfollow = 0;
        !            91:        while ((c = getopt(argc, argv, "Rfhipr")) != EOF) {
        !            92:        switch ((char)c) {
        !            93:                case 'f':
        !            94:                        iflag = 0;
        !            95:                        break;
        !            96:                case 'h':
        !            97:                        symfollow = 1;
        !            98:                        break;
        !            99:                case 'i':
        !           100:                        iflag = isatty(fileno(stdin));
        !           101:                        break;
        !           102:                case 'p':
        !           103:                        pflag = 1;
        !           104:                        break;
        !           105:                case 'R':
        !           106:                        rflag = 1;
        !           107:                        break;
        !           108:                case 'r':
        !           109:                        orflag = 1;
        !           110:                        break;
        !           111:                case '?':
        !           112:                default:
        !           113:                        usage();
        !           114:                        break;
        !           115:                }
        !           116:        }
        !           117:        argc -= optind;
        !           118:        argv += optind;
        !           119: 
        !           120:        if (argc < 2)
        !           121:                usage();
        !           122: 
        !           123:        if (rflag && orflag) {
        !           124:                (void)fprintf(stderr,
        !           125:                    "cp: the -R and -r options are mutually exclusive.\n");
        !           126:                exit(1);
        !           127:        }
        !           128: 
        !           129:        buf = (char *)malloc(MAXBSIZE);
        !           130:        if (!buf) {
        !           131:                (void)fprintf(stderr, "%s: out of space.\n", pname);
        !           132:                exit(1);
        !           133:        }
        !           134: 
        !           135:        myuid = getuid();
        !           136: 
        !           137:        /* copy the umask for explicit mode setting */
        !           138:        myumask = umask(0);
        !           139:        (void)umask(myumask);
        !           140: 
        !           141:        /* consume last argument first. */
        !           142:        if (!path_set(&to, argv[--argc]))
        !           143:                exit(exit_val);
        !           144: 
        !           145:        statfcn = symfollow || !rflag ? stat : lstat;
        !           146: 
        !           147:        /*
        !           148:         * Cp has two distinct cases:
        !           149:         *
        !           150:         * Case (1)       $ cp [-rip] source target
        !           151:         *
        !           152:         * Case (2)       $ cp [-rip] source1 ... directory
        !           153:         *
        !           154:         * In both cases, source can be either a file or a directory.
        !           155:         *
        !           156:         * In (1), the target becomes a copy of the source. That is, if the
        !           157:         * source is a file, the target will be a file, and likewise for
        !           158:         * directories.
        !           159:         *
        !           160:         * In (2), the real target is not directory, but "directory/source".
        !           161:         */
        !           162: 
        !           163:        r = stat(to.p_path, &to_stat);
        !           164:        if (r == -1 && errno != ENOENT) {
        !           165:                error(to.p_path);
        !           166:                exit(1);
        !           167:        }
        !           168:        if (r == -1 || type(to_stat) != S_IFDIR) {
        !           169:                /*
        !           170:                 * Case (1).  Target is not a directory.
        !           171:                 */
        !           172:                if (argc > 1) {
        !           173:                        usage();
        !           174:                        exit(1);
        !           175:                }
        !           176:                if (!path_set(&from, *argv))
        !           177:                        exit(exit_val);
        !           178:                copy();
        !           179:        }
        !           180:        else {
        !           181:                /*
        !           182:                 * Case (2).  Target is a directory.
        !           183:                 */
        !           184:                for (;; ++argv) {
        !           185:                        if (!path_set(&from, *argv))
        !           186:                                continue;
        !           187:                        old_to = path_append(&to, path_basename(&from), -1);
        !           188:                        if (!old_to)
        !           189:                                continue;
        !           190:                        copy();
        !           191:                        if (!--argc)
        !           192:                                break;
        !           193:                        path_restore(&to, old_to);
        !           194:                }
        !           195:        }
        !           196:        exit(exit_val);
        !           197: }
        !           198: 
        !           199: /* copy file or directory at "from" to "to". */
        !           200: copy()
        !           201: {
        !           202:        struct stat from_stat, to_stat;
        !           203:        int dne, statval;
        !           204: 
        !           205:        statval = statfcn(from.p_path, &from_stat);
        !           206:        if (statval == -1) {
        !           207:                error(from.p_path);
        !           208:                return;
        !           209:        }
        !           210: 
        !           211:        /* not an error, but need to remember it happened */
        !           212:        if (stat(to.p_path, &to_stat) == -1)
        !           213:                dne = 1;
        !           214:        else {
        !           215:                if (to_stat.st_dev == from_stat.st_dev &&
        !           216:                    to_stat.st_ino == from_stat.st_ino) {
        !           217:                        (void)fprintf(stderr,
        !           218:                            "%s: %s and %s are identical (not copied).\n",
        !           219:                            pname, to.p_path, from.p_path);
        !           220:                        exit_val = 1;
        !           221:                        return;
        !           222:                }
        !           223:                dne = 0;
        !           224:        }
        !           225: 
        !           226:        switch(type(from_stat)) {
        !           227:        case S_IFLNK:
        !           228:                copy_link(!dne);
        !           229:                return;
        !           230:        case S_IFDIR:
        !           231:                if (!rflag && !orflag) {
        !           232:                        (void)fprintf(stderr,
        !           233:                            "%s: %s is a directory (not copied).\n",
        !           234:                            pname, from.p_path);
        !           235:                        exit_val = 1;
        !           236:                        return;
        !           237:                }
        !           238:                if (dne) {
        !           239:                        /*
        !           240:                         * If the directory doesn't exist, create the new
        !           241:                         * one with the from file mode plus owner RWX bits,
        !           242:                         * modified by the umask.  Trade-off between being
        !           243:                         * able to write the directory (if from directory is
        !           244:                         * 555) and not causing a permissions race.  If the
        !           245:                         * umask blocks owner writes cp fails.
        !           246:                         */
        !           247:                        if (mkdir(to.p_path, from_stat.st_mode|S_IRWXU) < 0) {
        !           248:                                error(to.p_path);
        !           249:                                return;
        !           250:                        }
        !           251:                }
        !           252:                else if (type(to_stat) != S_IFDIR) {
        !           253:                        (void)fprintf(stderr, "%s: %s: not a directory.\n",
        !           254:                            pname, to.p_path);
        !           255:                        return;
        !           256:                }
        !           257:                copy_dir();
        !           258:                /*
        !           259:                 * If not -p and directory didn't exist, set it to be the
        !           260:                 * same as the from directory, umodified by the umask;
        !           261:                 * arguably wrong, but it's been that way forever.
        !           262:                 */
        !           263:                if (pflag)
        !           264:                        setfile(&from_stat, 0);
        !           265:                else if (dne)
        !           266:                        (void)chmod(to.p_path, from_stat.st_mode);
        !           267:                return;
        !           268:        case S_IFCHR:
        !           269:        case S_IFBLK:
        !           270:                if (rflag) {
        !           271:                        copy_special(&from_stat, !dne);
        !           272:                        return;
        !           273:                }
        !           274:                break;
        !           275:        case S_IFIFO:
        !           276:                if (rflag) {
        !           277:                        copy_fifo(&from_stat, !dne);
        !           278:                        return;
        !           279:                }
        !           280:                break;
        !           281:        }
        !           282:        copy_file(&from_stat, dne);
        !           283: }
        !           284: 
        !           285: copy_file(fs, dne)
        !           286:        struct stat *fs;
        !           287:        int dne;
        !           288: {
        !           289:        register int from_fd, to_fd, rcount, wcount;
        !           290:        struct stat to_stat;
        !           291: 
        !           292:        if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) {
        !           293:                error(from.p_path);
        !           294:                return;
        !           295:        }
        !           296: 
        !           297:        /*
        !           298:         * If the file exists and we're interactive, verify with the user.
        !           299:         * If the file DNE, set the mode to be the from file, minus setuid
        !           300:         * bits, modified by the umask; arguably wrong, but it makes copying
        !           301:         * executables work right and it's been that way forever.  (The
        !           302:         * other choice is 666 or'ed with the execute bits on the from file
        !           303:         * modified by the umask.)
        !           304:         */
        !           305:        if (!dne) {
        !           306:                if (iflag) {
        !           307:                        int checkch, ch;
        !           308: 
        !           309:                        (void)fprintf(stderr, "overwrite %s? ", to.p_path);
        !           310:                        checkch = ch = getchar();
        !           311:                        while (ch != '\n' && ch != EOF)
        !           312:                                ch = getchar();
        !           313:                        if (checkch != 'y') {
        !           314:                                (void)close(from_fd);
        !           315:                                return;
        !           316:                        }
        !           317:                }
        !           318:                to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0);
        !           319:        } else
        !           320:                to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC,
        !           321:                    fs->st_mode & ~(S_ISUID|S_ISGID));
        !           322: 
        !           323:        if (to_fd == -1) {
        !           324:                error(to.p_path);
        !           325:                (void)close(from_fd);
        !           326:                return;
        !           327:        }
        !           328: 
        !           329:        while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
        !           330:                wcount = write(to_fd, buf, rcount);
        !           331:                if (rcount != wcount || wcount == -1) {
        !           332:                        error(to.p_path);
        !           333:                        break;
        !           334:                }
        !           335:        }
        !           336:        if (rcount < 0)
        !           337:                error(from.p_path);
        !           338:        if (pflag)
        !           339:                setfile(fs, to_fd);
        !           340:        /*
        !           341:         * If the source was setuid or setgid, lose the bits unless the
        !           342:         * copy is owned by the same user and group.
        !           343:         */
        !           344:        else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid)
        !           345:                if (fstat(to_fd, &to_stat))
        !           346:                        error(to.p_path);
        !           347: #define        RETAINBITS      (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
        !           348:                else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd,
        !           349:                    fs->st_mode & RETAINBITS & ~myumask))
        !           350:                        error(to.p_path);
        !           351:        (void)close(from_fd);
        !           352:        (void)close(to_fd);
        !           353: }
        !           354: 
        !           355: copy_dir()
        !           356: {
        !           357:        struct stat from_stat;
        !           358:        struct direct *dp, **dir_list;
        !           359:        register int dir_cnt, i;
        !           360:        char *old_from, *old_to;
        !           361: 
        !           362:        dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL);
        !           363:        if (dir_cnt == -1) {
        !           364:                (void)fprintf(stderr, "%s: can't read directory %s.\n",
        !           365:                    pname, from.p_path);
        !           366:                exit_val = 1;
        !           367:        }
        !           368: 
        !           369:        /*
        !           370:         * Instead of handling directory entries in the order they appear
        !           371:         * on disk, do non-directory files before directory files.
        !           372:         * There are two reasons to do directories last.  The first is
        !           373:         * efficiency.  Files tend to be in the same cylinder group as
        !           374:         * their parent, whereas directories tend not to be.  Copying files
        !           375:         * all at once reduces seeking.  Second, deeply nested tree's
        !           376:         * could use up all the file descriptors if we didn't close one
        !           377:         * directory before recursivly starting on the next.
        !           378:         */
        !           379:        /* copy files */
        !           380:        for (i = 0; i < dir_cnt; ++i) {
        !           381:                dp = dir_list[i];
        !           382:                if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
        !           383:                    && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
        !           384:                        goto done;
        !           385:                old_from = path_append(&from, dp->d_name, (int)dp->d_namlen);
        !           386:                if (!old_from)
        !           387:                        goto done;
        !           388: 
        !           389:                if (statfcn(from.p_path, &from_stat) < 0) {
        !           390:                        error(dp->d_name);
        !           391:                        path_restore(&from, old_from);
        !           392:                        goto done;
        !           393:                }
        !           394:                if (type(from_stat) == S_IFDIR) {
        !           395:                        path_restore(&from, old_from);
        !           396:                        continue;
        !           397:                }
        !           398:                old_to = path_append(&to, dp->d_name, (int)dp->d_namlen);
        !           399:                if (old_to) {
        !           400:                        copy();
        !           401:                        path_restore(&to, old_to);
        !           402:                }
        !           403:                path_restore(&from, old_from);
        !           404: done:          dir_list[i] = NULL;
        !           405:                (void)free((void *)dp);
        !           406:        }
        !           407: 
        !           408:        /* copy directories */
        !           409:        for (i = 0; i < dir_cnt; ++i) {
        !           410:                dp = dir_list[i];
        !           411:                if (!dp)
        !           412:                        continue;
        !           413:                old_from = path_append(&from, dp->d_name, (int) dp->d_namlen);
        !           414:                if (!old_from) {
        !           415:                        (void)free((void *)dp);
        !           416:                        continue;
        !           417:                }
        !           418:                old_to = path_append(&to, dp->d_name, (int) dp->d_namlen);
        !           419:                if (!old_to) {
        !           420:                        (void)free((void *)dp);
        !           421:                        path_restore(&from, old_from);
        !           422:                        continue;
        !           423:                }
        !           424:                copy();
        !           425:                free((void *)dp);
        !           426:                path_restore(&from, old_from);
        !           427:                path_restore(&to, old_to);
        !           428:        }
        !           429:        free((void *)dir_list);
        !           430: }
        !           431: 
        !           432: copy_link(exists)
        !           433:        int exists;
        !           434: {
        !           435:        int len;
        !           436:        char link[MAXPATHLEN];
        !           437: 
        !           438:        if ((len = readlink(from.p_path, link, sizeof(link))) == -1) {
        !           439:                error(from.p_path);
        !           440:                return;
        !           441:        }
        !           442:        link[len] = '\0';
        !           443:        if (exists && unlink(to.p_path)) {
        !           444:                error(to.p_path);
        !           445:                return;
        !           446:        }
        !           447:        if (symlink(link, to.p_path)) {
        !           448:                error(link);
        !           449:                return;
        !           450:        }
        !           451: }
        !           452: 
        !           453: copy_fifo(from_stat, exists)
        !           454:        struct stat *from_stat;
        !           455:        int exists;
        !           456: {
        !           457:        if (exists && unlink(to.p_path)) {
        !           458:                error(to.p_path);
        !           459:                return;
        !           460:        }
        !           461:        if (mkfifo(to.p_path, from_stat->st_mode)) {
        !           462:                error(to.p_path);
        !           463:                return;
        !           464:        }
        !           465:        if (pflag)
        !           466:                setfile(from_stat, 0);
        !           467: }
        !           468: 
        !           469: copy_special(from_stat, exists)
        !           470:        struct stat *from_stat;
        !           471:        int exists;
        !           472: {
        !           473:        if (exists && unlink(to.p_path)) {
        !           474:                error(to.p_path);
        !           475:                return;
        !           476:        }
        !           477:        if (mknod(to.p_path, from_stat->st_mode,  from_stat->st_rdev)) {
        !           478:                error(to.p_path);
        !           479:                return;
        !           480:        }
        !           481:        if (pflag)
        !           482:                setfile(from_stat, 0);
        !           483: }
        !           484: 
        !           485: setfile(fs, fd)
        !           486:        register struct stat *fs;
        !           487:        int fd;
        !           488: {
        !           489:        static struct timeval tv[2];
        !           490: 
        !           491:        fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
        !           492: 
        !           493:        tv[0].tv_sec = fs->st_atime;
        !           494:        tv[1].tv_sec = fs->st_mtime;
        !           495:        if (utimes(to.p_path, tv))
        !           496:                error(to.p_path);
        !           497:        /*
        !           498:         * Changing the ownership probably won't succeed, unless we're root
        !           499:         * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
        !           500:         * the mode; current BSD behavior is to remove all setuid bits on
        !           501:         * chown.  If chown fails, lose setuid/setgid bits.
        !           502:         */
        !           503:        if (fd ? fchown(fd, fs->st_uid, fs->st_gid) :
        !           504:            chown(to.p_path, fs->st_uid, fs->st_gid)) {
        !           505:                if (errno != EPERM)
        !           506:                        error(to.p_path);
        !           507:                fs->st_mode &= ~(S_ISUID|S_ISGID);
        !           508:        }
        !           509:        if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode))
        !           510:                error(to.p_path);
        !           511: }
        !           512: 
        !           513: ismember(gid)
        !           514:        gid_t gid;
        !           515: {
        !           516:        register int cnt;
        !           517:        static int ngroups, groups[NGROUPS];
        !           518: 
        !           519:        if (!ngroups) {
        !           520:                ngroups = getgroups(NGROUPS, groups);
        !           521:                if (ngroups == -1) {
        !           522:                        ngroups = 0;
        !           523:                        exit_val = 1;
        !           524:                        (void)fprintf(stderr, "%s: %s\n",
        !           525:                            pname, strerror(errno));
        !           526:                        return(0);
        !           527:                }
        !           528:        }
        !           529:        for (cnt = ngroups; cnt--;)
        !           530:                if (gid == groups[cnt])
        !           531:                        return(1);
        !           532:        return(0);
        !           533: }
        !           534: 
        !           535: error(s)
        !           536:        char *s;
        !           537: {
        !           538:        exit_val = 1;
        !           539:        (void)fprintf(stderr, "%s: %s: %s\n", pname, s, strerror(errno));
        !           540: }
        !           541: 
        !           542: /********************************************************************
        !           543:  * Path Manipulation Routines.
        !           544:  ********************************************************************/
        !           545: 
        !           546: /*
        !           547:  * These functions manipulate paths in PATH_T structures.
        !           548:  * 
        !           549:  * They eliminate multiple slashes in paths when they notice them, and keep
        !           550:  * the path non-slash terminated.
        !           551:  *
        !           552:  * Both path_set() and path_append() return 0 if the requested name
        !           553:  * would be too long.
        !           554:  */
        !           555: 
        !           556: #define        STRIP_TRAILING_SLASH(p) { \
        !           557:        while ((p)->p_end > (p)->p_path && (p)->p_end[-1] == '/') \
        !           558:                *--(p)->p_end = 0; \
        !           559:        }
        !           560: 
        !           561: /*
        !           562:  * Move specified string into path.  Convert "" to "." to handle BSD
        !           563:  * semantics for a null path.  Strip trailing slashes.
        !           564:  */
        !           565: path_set(p, string)
        !           566:        register PATH_T *p;
        !           567:        char *string;
        !           568: {
        !           569:        if (strlen(string) > MAXPATHLEN) {
        !           570:                (void)fprintf(stderr, "%s: %s: name too long.\n",
        !           571:                    pname, string);
        !           572:                exit_val = 1;
        !           573:                return(0);
        !           574:        }
        !           575: 
        !           576:        (void)strcpy(p->p_path, string);
        !           577:        p->p_end = p->p_path + strlen(p->p_path);
        !           578: 
        !           579:        if (p->p_path == p->p_end) {
        !           580:                *p->p_end++ = '.';
        !           581:                *p->p_end = 0;
        !           582:        }
        !           583: 
        !           584:        STRIP_TRAILING_SLASH(p);
        !           585:        return(1);
        !           586: }
        !           587: 
        !           588: /*
        !           589:  * Append specified string to path, inserting '/' if necessary.  Return a
        !           590:  * pointer to the old end of path for restoration.
        !           591:  */
        !           592: char *
        !           593: path_append(p, name, len)
        !           594:        register PATH_T *p;
        !           595:        char *name;
        !           596:        int len;
        !           597: {
        !           598:        char *old;
        !           599: 
        !           600:        old = p->p_end;
        !           601:        if (len == -1)
        !           602:                len = strlen(name);
        !           603: 
        !           604:        /*
        !           605:         * The final "+ 1" accounts for the '/' between old path and name.
        !           606:         */
        !           607:        if ((len + p->p_end - p->p_path + 1) > MAXPATHLEN) {
        !           608:                (void)fprintf(stderr,
        !           609:                    "%s: %s/%s: name too long.\n", pname, p->p_path, name);
        !           610:                exit_val = 1;
        !           611:                return(0);
        !           612:        }
        !           613: 
        !           614:        /*
        !           615:         * This code should always be executed, since paths shouldn't
        !           616:         * end in '/'.
        !           617:         */
        !           618:        if (p->p_end[-1] != '/') {
        !           619:                *p->p_end++ = '/';
        !           620:                *p->p_end = 0;
        !           621:        }
        !           622: 
        !           623:        (void)strncat(p->p_end, name, len);
        !           624:        p->p_end += len;
        !           625:        *p->p_end = 0;
        !           626: 
        !           627:        STRIP_TRAILING_SLASH(p);
        !           628:        return(old);
        !           629: }
        !           630: 
        !           631: /*
        !           632:  * Restore path to previous value.  (As returned by path_append.)
        !           633:  */
        !           634: path_restore(p, old)
        !           635:        PATH_T *p;
        !           636:        char *old;
        !           637: {
        !           638:        p->p_end = old;
        !           639:        *p->p_end = 0;
        !           640: }
        !           641: 
        !           642: /*
        !           643:  * Return basename of path.  (Like basename(1).)
        !           644:  */
        !           645: char *
        !           646: path_basename(p)
        !           647:        PATH_T *p;
        !           648: {
        !           649:        char *basename;
        !           650: 
        !           651:        basename = rindex(p->p_path, '/');
        !           652:        return(basename ? ++basename : p->p_path);
        !           653: }
        !           654: 
        !           655: usage()
        !           656: {
        !           657:        (void)fprintf(stderr,
        !           658: "usage: cp [-Rfhip] src target;\n   or: cp [-Rfhip] src1 ... srcN directory\n");
        !           659:        exit(1);
        !           660: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.