1139969Simp/*- 21556Srgrimes * Copyright (c) 1988, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * David Hitz of Auspex Systems Inc. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 4. Neither the name of the University nor the names of its contributors 171556Srgrimes * may be used to endorse or promote products derived from this software 181556Srgrimes * without specific prior written permission. 191556Srgrimes * 201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301556Srgrimes * SUCH DAMAGE. 311556Srgrimes */ 321556Srgrimes 33114433Sobrien#if 0 341556Srgrimes#ifndef lint 3520412Sstevestatic char const copyright[] = 361556Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\ 371556Srgrimes The Regents of the University of California. All rights reserved.\n"; 381556Srgrimes#endif /* not lint */ 391556Srgrimes 401556Srgrimes#ifndef lint 4136003Scharnierstatic char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94"; 42114433Sobrien#endif /* not lint */ 4335773Scharnier#endif 4499109Sobrien#include <sys/cdefs.h> 4599109Sobrien__FBSDID("$FreeBSD$"); 461556Srgrimes 471556Srgrimes/* 481556Srgrimes * Cp copies source files to target files. 498855Srgrimes * 501556Srgrimes * The global PATH_T structure "to" always contains the path to the 511556Srgrimes * current target file. Since fts(3) does not change directories, 5220412Ssteve * this path can be either absolute or dot-relative. 538855Srgrimes * 541556Srgrimes * The basic algorithm is to initialize "to" and use fts(3) to traverse 551556Srgrimes * the file hierarchy rooted in the argument list. A trivial case is the 561556Srgrimes * case of 'cp file1 file2'. The more interesting case is the case of 571556Srgrimes * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the 581556Srgrimes * path (relative to the root of the traversal) is appended to dir (stored 591556Srgrimes * in "to") to form the final target path. 601556Srgrimes */ 611556Srgrimes 62113431Sbde#include <sys/types.h> 631556Srgrimes#include <sys/stat.h> 641556Srgrimes 651556Srgrimes#include <err.h> 661556Srgrimes#include <errno.h> 671556Srgrimes#include <fts.h> 6876693Simp#include <limits.h> 69113431Sbde#include <signal.h> 7050381Smharo#include <stdio.h> 7178469Sdes#include <stdlib.h> 721556Srgrimes#include <string.h> 731556Srgrimes#include <unistd.h> 741556Srgrimes 751556Srgrimes#include "extern.h" 761556Srgrimes 771556Srgrimes#define STRIP_TRAILING_SLASH(p) { \ 785292Sbde while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ 791556Srgrimes *--(p).p_end = 0; \ 801556Srgrimes} 811556Srgrimes 8291087Smarkmstatic char emptystring[] = ""; 831556Srgrimes 8491087SmarkmPATH_T to = { to.p_path, emptystring, "" }; 851556Srgrimes 86161586Sjulianint fflag, iflag, lflag, nflag, pflag, vflag; 87100538Sjohanstatic int Rflag, rflag; 88113218Smdoddvolatile sig_atomic_t info; 89113209Smdodd 901556Srgrimesenum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; 911556Srgrimes 9299363Smarkmstatic int copy(char *[], enum op, int); 93103726Swollmanstatic int mastercmp(const FTSENT * const *, const FTSENT * const *); 94113430Sbdestatic void siginfo(int __unused); 951556Srgrimes 961556Srgrimesint 9790107Simpmain(int argc, char *argv[]) 981556Srgrimes{ 991556Srgrimes struct stat to_stat, tmp_stat; 1001556Srgrimes enum op type; 101245535Seadler int Hflag, Lflag, ch, fts_options, r, have_trailing_slash; 10296809Sache char *target; 1031556Srgrimes 104202461Sgavin fts_options = FTS_NOCHDIR | FTS_PHYSICAL; 105245535Seadler Hflag = Lflag = 0; 106202461Sgavin while ((ch = getopt(argc, argv, "HLPRafilnprvx")) != -1) 1071556Srgrimes switch (ch) { 1081556Srgrimes case 'H': 1091556Srgrimes Hflag = 1; 110245535Seadler Lflag = 0; 1111556Srgrimes break; 1121556Srgrimes case 'L': 1131556Srgrimes Lflag = 1; 114245535Seadler Hflag = 0; 1151556Srgrimes break; 1161556Srgrimes case 'P': 1171556Srgrimes Hflag = Lflag = 0; 1181556Srgrimes break; 1191556Srgrimes case 'R': 1201556Srgrimes Rflag = 1; 1211556Srgrimes break; 122177036Sjhb case 'a': 123177036Sjhb pflag = 1; 124177036Sjhb Rflag = 1; 125177036Sjhb Hflag = Lflag = 0; 126177036Sjhb break; 1271556Srgrimes case 'f': 12814416Swosch fflag = 1; 129100538Sjohan iflag = nflag = 0; 1301556Srgrimes break; 1311556Srgrimes case 'i': 13214416Swosch iflag = 1; 133100538Sjohan fflag = nflag = 0; 1341556Srgrimes break; 135162763Sru case 'l': 136162763Sru lflag = 1; 137162763Sru break; 138100538Sjohan case 'n': 139100538Sjohan nflag = 1; 140100538Sjohan fflag = iflag = 0; 141100538Sjohan break; 1421556Srgrimes case 'p': 1431556Srgrimes pflag = 1; 1441556Srgrimes break; 1451556Srgrimes case 'r': 146163109Strhodes rflag = Lflag = 1; 147245535Seadler Hflag = 0; 1481556Srgrimes break; 14950381Smharo case 'v': 15050381Smharo vflag = 1; 15150381Smharo break; 152202461Sgavin case 'x': 153202461Sgavin fts_options |= FTS_XDEV; 154202461Sgavin break; 1551556Srgrimes default: 1561556Srgrimes usage(); 1571556Srgrimes break; 1581556Srgrimes } 1591556Srgrimes argc -= optind; 1601556Srgrimes argv += optind; 1611556Srgrimes 1621556Srgrimes if (argc < 2) 1631556Srgrimes usage(); 1641556Srgrimes 165163233Strhodes if (Rflag && rflag) 166163233Strhodes errx(1, "the -R and -r options may not be specified together"); 167163233Strhodes if (rflag) 168163109Strhodes Rflag = 1; 1691556Srgrimes if (Rflag) { 1701556Srgrimes if (Hflag) 1711556Srgrimes fts_options |= FTS_COMFOLLOW; 1721556Srgrimes if (Lflag) { 1731556Srgrimes fts_options &= ~FTS_PHYSICAL; 1741556Srgrimes fts_options |= FTS_LOGICAL; 1751556Srgrimes } 1761556Srgrimes } else { 1771556Srgrimes fts_options &= ~FTS_PHYSICAL; 17898171Stjr fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; 1791556Srgrimes } 180113209Smdodd (void)signal(SIGINFO, siginfo); 1811556Srgrimes 1821556Srgrimes /* Save the target base in "to". */ 1831556Srgrimes target = argv[--argc]; 18476693Simp if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) 1851556Srgrimes errx(1, "%s: name too long", target); 1861556Srgrimes to.p_end = to.p_path + strlen(to.p_path); 1871556Srgrimes if (to.p_path == to.p_end) { 1881556Srgrimes *to.p_end++ = '.'; 1891556Srgrimes *to.p_end = 0; 1901556Srgrimes } 19196809Sache have_trailing_slash = (to.p_end[-1] == '/'); 19296809Sache if (have_trailing_slash) 19396809Sache STRIP_TRAILING_SLASH(to); 1941556Srgrimes to.target_end = to.p_end; 1951556Srgrimes 1961556Srgrimes /* Set end of argument list for fts(3). */ 1978855Srgrimes argv[argc] = NULL; 1988855Srgrimes 1991556Srgrimes /* 2001556Srgrimes * Cp has two distinct cases: 2011556Srgrimes * 2021556Srgrimes * cp [-R] source target 2031556Srgrimes * cp [-R] source1 ... sourceN directory 2041556Srgrimes * 2051556Srgrimes * In both cases, source can be either a file or a directory. 2061556Srgrimes * 2071556Srgrimes * In (1), the target becomes a copy of the source. That is, if the 2081556Srgrimes * source is a file, the target will be a file, and likewise for 2091556Srgrimes * directories. 2101556Srgrimes * 2111556Srgrimes * In (2), the real target is not directory, but "directory/source". 2121556Srgrimes */ 2131556Srgrimes r = stat(to.p_path, &to_stat); 2141556Srgrimes if (r == -1 && errno != ENOENT) 2151556Srgrimes err(1, "%s", to.p_path); 2161556Srgrimes if (r == -1 || !S_ISDIR(to_stat.st_mode)) { 2171556Srgrimes /* 2181556Srgrimes * Case (1). Target is not a directory. 2198855Srgrimes */ 220174912Sedwin if (argc > 1) 221174912Sedwin errx(1, "%s is not a directory", to.p_path); 222174912Sedwin 2231556Srgrimes /* 2241556Srgrimes * Need to detect the case: 2251556Srgrimes * cp -R dir foo 2261556Srgrimes * Where dir is a directory and foo does not exist, where 2271556Srgrimes * we want pathname concatenations turned on but not for 2281556Srgrimes * the initial mkdir(). 2291556Srgrimes */ 2301556Srgrimes if (r == -1) { 231163109Strhodes if (Rflag && (Lflag || Hflag)) 2321556Srgrimes stat(*argv, &tmp_stat); 2331556Srgrimes else 2341556Srgrimes lstat(*argv, &tmp_stat); 2358855Srgrimes 236163233Strhodes if (S_ISDIR(tmp_stat.st_mode) && Rflag) 2371556Srgrimes type = DIR_TO_DNE; 2381556Srgrimes else 2391556Srgrimes type = FILE_TO_FILE; 2401556Srgrimes } else 2411556Srgrimes type = FILE_TO_FILE; 24296808Sache 24396808Sache if (have_trailing_slash && type == FILE_TO_FILE) { 24496808Sache if (r == -1) 24596808Sache errx(1, "directory %s does not exist", 24696808Sache to.p_path); 24796808Sache else 24896808Sache errx(1, "%s is not a directory", to.p_path); 24996808Sache } 2501556Srgrimes } else 2511556Srgrimes /* 2521556Srgrimes * Case (2). Target is a directory. 2531556Srgrimes */ 2541556Srgrimes type = FILE_TO_DIR; 2551556Srgrimes 2561556Srgrimes exit (copy(argv, type, fts_options)); 2571556Srgrimes} 2581556Srgrimes 259105395Smarkmstatic int 26090107Simpcopy(char *argv[], enum op type, int fts_options) 2611556Srgrimes{ 2621556Srgrimes struct stat to_stat; 2631556Srgrimes FTS *ftsp; 2641556Srgrimes FTSENT *curr; 26591087Smarkm int base = 0, dne, badcp, rval; 26691087Smarkm size_t nlen; 2675292Sbde char *p, *target_mid; 26888439Smckay mode_t mask, mode; 2691556Srgrimes 27087655Smckay /* 27187655Smckay * Keep an inverted copy of the umask, for use in correcting 27287655Smckay * permissions on created directories when not using -p. 27387655Smckay */ 27487655Smckay mask = ~umask(0777); 27587655Smckay umask(~mask); 27687655Smckay 2771556Srgrimes if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) 27899744Sdillon err(1, "fts_open"); 27953819Smharo for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { 2801556Srgrimes switch (curr->fts_info) { 2811556Srgrimes case FTS_NS: 28236812Sdt case FTS_DNR: 2831556Srgrimes case FTS_ERR: 2841556Srgrimes warnx("%s: %s", 2851556Srgrimes curr->fts_path, strerror(curr->fts_errno)); 28653819Smharo badcp = rval = 1; 2871556Srgrimes continue; 2881556Srgrimes case FTS_DC: /* Warn, continue. */ 2891556Srgrimes warnx("%s: directory causes a cycle", curr->fts_path); 29053819Smharo badcp = rval = 1; 2911556Srgrimes continue; 29291087Smarkm default: 29396371Salfred ; 2941556Srgrimes } 2951556Srgrimes 2961556Srgrimes /* 2978855Srgrimes * If we are in case (2) or (3) above, we need to append the 2988855Srgrimes * source name to the target name. 2991556Srgrimes */ 3001556Srgrimes if (type != FILE_TO_FILE) { 3011556Srgrimes /* 3021556Srgrimes * Need to remember the roots of traversals to create 3031556Srgrimes * correct pathnames. If there's a directory being 3041556Srgrimes * copied to a non-existent directory, e.g. 3051556Srgrimes * cp -R a/dir noexist 3061556Srgrimes * the resulting path name should be noexist/foo, not 3071556Srgrimes * noexist/dir/foo (where foo is a file in dir), which 3081556Srgrimes * is the case where the target exists. 3091556Srgrimes * 3101556Srgrimes * Also, check for "..". This is for correct path 31146684Skris * concatenation for paths ending in "..", e.g. 3121556Srgrimes * cp -R .. /tmp 3131556Srgrimes * Paths ending in ".." are changed to ".". This is 3141556Srgrimes * tricky, but seems the easiest way to fix the problem. 3151556Srgrimes * 3161556Srgrimes * XXX 3171556Srgrimes * Since the first level MUST be FTS_ROOTLEVEL, base 3181556Srgrimes * is always initialized. 3191556Srgrimes */ 32046073Simp if (curr->fts_level == FTS_ROOTLEVEL) { 3211556Srgrimes if (type != DIR_TO_DNE) { 3221556Srgrimes p = strrchr(curr->fts_path, '/'); 3238855Srgrimes base = (p == NULL) ? 0 : 3241556Srgrimes (int)(p - curr->fts_path + 1); 3251556Srgrimes 3268855Srgrimes if (!strcmp(&curr->fts_path[base], 3271556Srgrimes "..")) 3281556Srgrimes base += 1; 3291556Srgrimes } else 3301556Srgrimes base = curr->fts_pathlen; 33146073Simp } 3321556Srgrimes 3331556Srgrimes p = &curr->fts_path[base]; 3341556Srgrimes nlen = curr->fts_pathlen - base; 3355292Sbde target_mid = to.target_end; 3365292Sbde if (*p != '/' && target_mid[-1] != '/') 3375292Sbde *target_mid++ = '/'; 3385292Sbde *target_mid = 0; 33976693Simp if (target_mid - to.p_path + nlen >= PATH_MAX) { 3408855Srgrimes warnx("%s%s: name too long (not copied)", 3415292Sbde to.p_path, p); 34253819Smharo badcp = rval = 1; 3435292Sbde continue; 3445292Sbde } 3455292Sbde (void)strncat(target_mid, p, nlen); 3465292Sbde to.p_end = target_mid + nlen; 3471556Srgrimes *to.p_end = 0; 3481556Srgrimes STRIP_TRAILING_SLASH(to); 3491556Srgrimes } 3501556Srgrimes 35187655Smckay if (curr->fts_info == FTS_DP) { 35287655Smckay /* 35388755Smckay * We are nearly finished with this directory. If we 35488755Smckay * didn't actually copy it, or otherwise don't need to 35588755Smckay * change its attributes, then we are done. 35687655Smckay */ 35788439Smckay if (!curr->fts_number) 35888439Smckay continue; 35988439Smckay /* 36088439Smckay * If -p is in effect, set all the attributes. 36188439Smckay * Otherwise, set the correct permissions, limited 36288755Smckay * by the umask. Optimise by avoiding a chmod() 36388755Smckay * if possible (which is usually the case if we 36488755Smckay * made the directory). Note that mkdir() does not 36588755Smckay * honour setuid, setgid and sticky bits, but we 36688755Smckay * normally want to preserve them on directories. 36788439Smckay */ 368103801Smckay if (pflag) { 369117065Sjmg if (setfile(curr->fts_statp, -1)) 370149790Scsjp rval = 1; 371149790Scsjp if (preserve_dir_acls(curr->fts_statp, 372149790Scsjp curr->fts_accpath, to.p_path) != 0) 373149790Scsjp rval = 1; 374103801Smckay } else { 37588439Smckay mode = curr->fts_statp->st_mode; 37688439Smckay if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) || 37788439Smckay ((mode | S_IRWXU) & mask) != (mode & mask)) 37888439Smckay if (chmod(to.p_path, mode & mask) != 0){ 37988439Smckay warn("chmod: %s", to.p_path); 38088439Smckay rval = 1; 38188439Smckay } 38287655Smckay } 38387655Smckay continue; 38487655Smckay } 38587655Smckay 3861556Srgrimes /* Not an error but need to remember it happened */ 3871556Srgrimes if (stat(to.p_path, &to_stat) == -1) 3881556Srgrimes dne = 1; 3891556Srgrimes else { 3901556Srgrimes if (to_stat.st_dev == curr->fts_statp->st_dev && 3911556Srgrimes to_stat.st_ino == curr->fts_statp->st_ino) { 3921556Srgrimes warnx("%s and %s are identical (not copied).", 3931556Srgrimes to.p_path, curr->fts_path); 39453819Smharo badcp = rval = 1; 3951556Srgrimes if (S_ISDIR(curr->fts_statp->st_mode)) 3961556Srgrimes (void)fts_set(ftsp, curr, FTS_SKIP); 3971556Srgrimes continue; 3981556Srgrimes } 39920412Ssteve if (!S_ISDIR(curr->fts_statp->st_mode) && 40020412Ssteve S_ISDIR(to_stat.st_mode)) { 40191087Smarkm warnx("cannot overwrite directory %s with " 40291087Smarkm "non-directory %s", 40320412Ssteve to.p_path, curr->fts_path); 40453819Smharo badcp = rval = 1; 40520412Ssteve continue; 40620412Ssteve } 4071556Srgrimes dne = 0; 4081556Srgrimes } 4091556Srgrimes 4101556Srgrimes switch (curr->fts_statp->st_mode & S_IFMT) { 4111556Srgrimes case S_IFLNK: 41298171Stjr /* Catch special case of a non-dangling symlink */ 41398171Stjr if ((fts_options & FTS_LOGICAL) || 41498171Stjr ((fts_options & FTS_COMFOLLOW) && 41598171Stjr curr->fts_level == 0)) { 41698171Stjr if (copy_file(curr, dne)) 41798171Stjr badcp = rval = 1; 41898171Stjr } else { 41998171Stjr if (copy_link(curr, !dne)) 42098171Stjr badcp = rval = 1; 42198171Stjr } 4221556Srgrimes break; 4231556Srgrimes case S_IFDIR: 424163109Strhodes if (!Rflag) { 4251556Srgrimes warnx("%s is a directory (not copied).", 4261556Srgrimes curr->fts_path); 4271556Srgrimes (void)fts_set(ftsp, curr, FTS_SKIP); 42853819Smharo badcp = rval = 1; 4291556Srgrimes break; 4301556Srgrimes } 4311556Srgrimes /* 4321556Srgrimes * If the directory doesn't exist, create the new 4331556Srgrimes * one with the from file mode plus owner RWX bits, 4341556Srgrimes * modified by the umask. Trade-off between being 4351556Srgrimes * able to write the directory (if from directory is 4361556Srgrimes * 555) and not causing a permissions race. If the 4371556Srgrimes * umask blocks owner writes, we fail.. 4381556Srgrimes */ 4391556Srgrimes if (dne) { 4408855Srgrimes if (mkdir(to.p_path, 4411556Srgrimes curr->fts_statp->st_mode | S_IRWXU) < 0) 4421556Srgrimes err(1, "%s", to.p_path); 4431556Srgrimes } else if (!S_ISDIR(to_stat.st_mode)) { 4441556Srgrimes errno = ENOTDIR; 4455879Sdg err(1, "%s", to.p_path); 4461556Srgrimes } 4471556Srgrimes /* 44888439Smckay * Arrange to correct directory attributes later 44987655Smckay * (in the post-order phase) if this is a new 45088439Smckay * directory, or if the -p flag is in effect. 4511556Srgrimes */ 45288439Smckay curr->fts_number = pflag || dne; 4531556Srgrimes break; 4541556Srgrimes case S_IFBLK: 4551556Srgrimes case S_IFCHR: 4561556Srgrimes if (Rflag) { 4571556Srgrimes if (copy_special(curr->fts_statp, !dne)) 45853819Smharo badcp = rval = 1; 4597572Sbde } else { 4601556Srgrimes if (copy_file(curr, dne)) 46153819Smharo badcp = rval = 1; 4627572Sbde } 4631556Srgrimes break; 464161586Sjulian case S_IFSOCK: 465161586Sjulian warnx("%s is a socket (not copied).", 466161586Sjulian curr->fts_path); 467208821Strasz break; 4681556Srgrimes case S_IFIFO: 4697572Sbde if (Rflag) { 4701556Srgrimes if (copy_fifo(curr->fts_statp, !dne)) 47153819Smharo badcp = rval = 1; 4727572Sbde } else { 4731556Srgrimes if (copy_file(curr, dne)) 47453819Smharo badcp = rval = 1; 4757572Sbde } 4761556Srgrimes break; 4771556Srgrimes default: 4781556Srgrimes if (copy_file(curr, dne)) 47953819Smharo badcp = rval = 1; 4801556Srgrimes break; 4811556Srgrimes } 48253819Smharo if (vflag && !badcp) 48350543Smharo (void)printf("%s -> %s\n", curr->fts_path, to.p_path); 4841556Srgrimes } 4851556Srgrimes if (errno) 4861556Srgrimes err(1, "fts_read"); 487160098Smaxim fts_close(ftsp); 4881556Srgrimes return (rval); 4891556Srgrimes} 4901556Srgrimes 4911556Srgrimes/* 4921556Srgrimes * mastercmp -- 4931556Srgrimes * The comparison function for the copy order. The order is to copy 4941556Srgrimes * non-directory files before directory files. The reason for this 4951556Srgrimes * is because files tend to be in the same cylinder group as their 4961556Srgrimes * parent directory, whereas directories tend not to be. Copying the 4971556Srgrimes * files first reduces seeking. 4981556Srgrimes */ 499105395Smarkmstatic int 500103726Swollmanmastercmp(const FTSENT * const *a, const FTSENT * const *b) 5011556Srgrimes{ 5021556Srgrimes int a_info, b_info; 5031556Srgrimes 5041556Srgrimes a_info = (*a)->fts_info; 5051556Srgrimes if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR) 5061556Srgrimes return (0); 5071556Srgrimes b_info = (*b)->fts_info; 5081556Srgrimes if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR) 5091556Srgrimes return (0); 5101556Srgrimes if (a_info == FTS_D) 5111556Srgrimes return (-1); 5121556Srgrimes if (b_info == FTS_D) 5131556Srgrimes return (1); 5141556Srgrimes return (0); 5151556Srgrimes} 516113209Smdodd 517113209Smdoddstatic void 518113430Sbdesiginfo(int sig __unused) 519113209Smdodd{ 520113209Smdodd 521113209Smdodd info = 1; 522113209Smdodd} 523