| Commit | Line | Data |
|---|---|---|
| 7cfd531a | 1 | /*- |
| 984263bc MD |
2 | * Copyright (c) 1988, 1993, 1994 |
| 3 | * The Regents of the University of California. 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, with or without | |
| 9 | * modification, are permitted provided that the following conditions | |
| 10 | * are met: | |
| 11 | * 1. Redistributions of source code must retain the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer in the | |
| 15 | * documentation and/or other materials provided with the distribution. | |
| 984263bc MD |
16 | * 4. Neither the name of the University nor the names of its contributors |
| 17 | * may be used to endorse or promote products derived from this software | |
| 18 | * without specific prior written permission. | |
| 19 | * | |
| 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
| 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
| 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 30 | * SUCH DAMAGE. | |
| 31 | */ | |
| 32 | ||
| 984263bc MD |
33 | /* |
| 34 | * Cp copies source files to target files. | |
| 35 | * | |
| 36 | * The global PATH_T structure "to" always contains the path to the | |
| 37 | * current target file. Since fts(3) does not change directories, | |
| 38 | * this path can be either absolute or dot-relative. | |
| 39 | * | |
| 40 | * The basic algorithm is to initialize "to" and use fts(3) to traverse | |
| 41 | * the file hierarchy rooted in the argument list. A trivial case is the | |
| 42 | * case of 'cp file1 file2'. The more interesting case is the case of | |
| 43 | * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the | |
| 44 | * path (relative to the root of the traversal) is appended to dir (stored | |
| 45 | * in "to") to form the final target path. | |
| 46 | */ | |
| 47 | ||
| 779388d9 | 48 | #include <sys/types.h> |
| 984263bc MD |
49 | #include <sys/stat.h> |
| 50 | ||
| 51 | #include <err.h> | |
| 52 | #include <errno.h> | |
| 53 | #include <fts.h> | |
| 54 | #include <limits.h> | |
| 779388d9 | 55 | #include <signal.h> |
| 984263bc MD |
56 | #include <stdio.h> |
| 57 | #include <stdlib.h> | |
| 58 | #include <string.h> | |
| 59 | #include <unistd.h> | |
| 60 | ||
| 61 | #include "extern.h" | |
| 62 | ||
| 63 | #define STRIP_TRAILING_SLASH(p) { \ | |
| 64 | while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ | |
| 65 | *--(p).p_end = 0; \ | |
| 66 | } | |
| 67 | ||
| 7cfd531a JM |
68 | static char emptystring[] = ""; |
| 69 | ||
| 70 | PATH_T to = { to.p_path, emptystring, "" }; | |
| 984263bc | 71 | |
| 7cfd531a | 72 | int fflag, iflag, lflag, nflag, pflag, vflag; |
| 08ffd647 | 73 | static int Rflag, rflag; |
| 779388d9 | 74 | volatile sig_atomic_t info; |
| 984263bc MD |
75 | |
| 76 | enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; | |
| 77 | ||
| 7cfd531a | 78 | static int copy(char *[], enum op, int); |
| 59b616d3 | 79 | static int mastercmp (const FTSENT * const *, const FTSENT * const *); |
| 779388d9 | 80 | static void siginfo (int); |
| 984263bc MD |
81 | |
| 82 | int | |
| 7cfd531a | 83 | main(int argc, char *argv[]) |
| 984263bc MD |
84 | { |
| 85 | struct stat to_stat, tmp_stat; | |
| 86 | enum op type; | |
| 7cfd531a | 87 | int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash; |
| 984263bc MD |
88 | char *target; |
| 89 | ||
| 7cfd531a | 90 | fts_options = FTS_NOCHDIR | FTS_PHYSICAL; |
| 984263bc | 91 | Hflag = Lflag = Pflag = 0; |
| 7cfd531a | 92 | while ((ch = getopt(argc, argv, "HLPRafilnprvx")) != -1) |
| 984263bc MD |
93 | switch (ch) { |
| 94 | case 'H': | |
| 95 | Hflag = 1; | |
| 96 | Lflag = Pflag = 0; | |
| 97 | break; | |
| 98 | case 'L': | |
| 99 | Lflag = 1; | |
| 100 | Hflag = Pflag = 0; | |
| 101 | break; | |
| 102 | case 'P': | |
| 103 | Pflag = 1; | |
| 104 | Hflag = Lflag = 0; | |
| 105 | break; | |
| 106 | case 'R': | |
| 107 | Rflag = 1; | |
| 108 | break; | |
| 7cfd531a JM |
109 | case 'a': |
| 110 | Pflag = 1; | |
| 111 | pflag = 1; | |
| 112 | Rflag = 1; | |
| 113 | Hflag = Lflag = 0; | |
| 114 | break; | |
| 984263bc MD |
115 | case 'f': |
| 116 | fflag = 1; | |
| 117 | iflag = nflag = 0; | |
| 118 | break; | |
| 119 | case 'i': | |
| 120 | iflag = 1; | |
| 121 | fflag = nflag = 0; | |
| 122 | break; | |
| 7cfd531a JM |
123 | case 'l': |
| 124 | lflag = 1; | |
| 125 | break; | |
| 984263bc MD |
126 | case 'n': |
| 127 | nflag = 1; | |
| 128 | fflag = iflag = 0; | |
| 129 | break; | |
| 130 | case 'p': | |
| 131 | pflag = 1; | |
| 132 | break; | |
| 133 | case 'r': | |
| 7cfd531a JM |
134 | rflag = Lflag = 1; |
| 135 | Hflag = Pflag = 0; | |
| 984263bc MD |
136 | break; |
| 137 | case 'v': | |
| 138 | vflag = 1; | |
| 139 | break; | |
| 7cfd531a JM |
140 | case 'x': |
| 141 | fts_options |= FTS_XDEV; | |
| 142 | break; | |
| 984263bc MD |
143 | default: |
| 144 | usage(); | |
| 145 | break; | |
| 146 | } | |
| 147 | argc -= optind; | |
| 148 | argv += optind; | |
| 149 | ||
| 150 | if (argc < 2) | |
| 151 | usage(); | |
| 152 | ||
| 7cfd531a JM |
153 | if (Rflag && rflag) |
| 154 | errx(1, "the -R and -r options may not be specified together"); | |
| 155 | if (rflag) | |
| 156 | Rflag = 1; | |
| 984263bc MD |
157 | if (Rflag) { |
| 158 | if (Hflag) | |
| 159 | fts_options |= FTS_COMFOLLOW; | |
| 160 | if (Lflag) { | |
| 161 | fts_options &= ~FTS_PHYSICAL; | |
| 162 | fts_options |= FTS_LOGICAL; | |
| 163 | } | |
| 164 | } else { | |
| 165 | fts_options &= ~FTS_PHYSICAL; | |
| 3abc2185 | 166 | fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; |
| 984263bc | 167 | } |
| 7cfd531a | 168 | (void)signal(SIGINFO, siginfo); |
| 984263bc MD |
169 | |
| 170 | /* Save the target base in "to". */ | |
| 171 | target = argv[--argc]; | |
| 172 | if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) | |
| 173 | errx(1, "%s: name too long", target); | |
| 174 | to.p_end = to.p_path + strlen(to.p_path); | |
| 7cfd531a | 175 | if (to.p_path == to.p_end) { |
| 984263bc MD |
176 | *to.p_end++ = '.'; |
| 177 | *to.p_end = 0; | |
| 178 | } | |
| 7cfd531a JM |
179 | have_trailing_slash = (to.p_end[-1] == '/'); |
| 180 | if (have_trailing_slash) | |
| 3abc2185 | 181 | STRIP_TRAILING_SLASH(to); |
| 984263bc MD |
182 | to.target_end = to.p_end; |
| 183 | ||
| 184 | /* Set end of argument list for fts(3). */ | |
| 185 | argv[argc] = NULL; | |
| 186 | ||
| 187 | /* | |
| 188 | * Cp has two distinct cases: | |
| 189 | * | |
| 190 | * cp [-R] source target | |
| 191 | * cp [-R] source1 ... sourceN directory | |
| 192 | * | |
| 193 | * In both cases, source can be either a file or a directory. | |
| 194 | * | |
| 195 | * In (1), the target becomes a copy of the source. That is, if the | |
| 196 | * source is a file, the target will be a file, and likewise for | |
| 197 | * directories. | |
| 198 | * | |
| 199 | * In (2), the real target is not directory, but "directory/source". | |
| 200 | */ | |
| 201 | r = stat(to.p_path, &to_stat); | |
| 202 | if (r == -1 && errno != ENOENT) | |
| 203 | err(1, "%s", to.p_path); | |
| 204 | if (r == -1 || !S_ISDIR(to_stat.st_mode)) { | |
| 205 | /* | |
| 206 | * Case (1). Target is not a directory. | |
| 207 | */ | |
| 7cfd531a JM |
208 | if (argc > 1) |
| 209 | errx(1, "%s is not a directory", to.p_path); | |
| 210 | ||
| 984263bc MD |
211 | /* |
| 212 | * Need to detect the case: | |
| 213 | * cp -R dir foo | |
| 214 | * Where dir is a directory and foo does not exist, where | |
| 215 | * we want pathname concatenations turned on but not for | |
| 216 | * the initial mkdir(). | |
| 217 | */ | |
| 218 | if (r == -1) { | |
| 7cfd531a | 219 | if (Rflag && (Lflag || Hflag)) |
| 984263bc MD |
220 | stat(*argv, &tmp_stat); |
| 221 | else | |
| 222 | lstat(*argv, &tmp_stat); | |
| 223 | ||
| 7cfd531a | 224 | if (S_ISDIR(tmp_stat.st_mode) && Rflag) |
| 984263bc MD |
225 | type = DIR_TO_DNE; |
| 226 | else | |
| 227 | type = FILE_TO_FILE; | |
| 228 | } else | |
| 229 | type = FILE_TO_FILE; | |
| 3abc2185 | 230 | |
| 7cfd531a | 231 | if (have_trailing_slash && type == FILE_TO_FILE) { |
| 3abc2185 | 232 | if (r == -1) |
| 7cfd531a JM |
233 | errx(1, "directory %s does not exist", |
| 234 | to.p_path); | |
| 3abc2185 CP |
235 | else |
| 236 | errx(1, "%s is not a directory", to.p_path); | |
| 237 | } | |
| 984263bc MD |
238 | } else |
| 239 | /* | |
| 240 | * Case (2). Target is a directory. | |
| 241 | */ | |
| 242 | type = FILE_TO_DIR; | |
| 243 | ||
| 244 | exit (copy(argv, type, fts_options)); | |
| 245 | } | |
| 246 | ||
| 2a47c630 | 247 | static int |
| 7cfd531a | 248 | copy(char *argv[], enum op type, int fts_options) |
| 984263bc MD |
249 | { |
| 250 | struct stat to_stat; | |
| 251 | FTS *ftsp; | |
| 252 | FTSENT *curr; | |
| 7cfd531a JM |
253 | int base = 0, dne, badcp, rval; |
| 254 | size_t nlen; | |
| 984263bc MD |
255 | char *p, *target_mid; |
| 256 | mode_t mask, mode; | |
| 257 | ||
| 258 | /* | |
| 259 | * Keep an inverted copy of the umask, for use in correcting | |
| 260 | * permissions on created directories when not using -p. | |
| 261 | */ | |
| 262 | mask = ~umask(0777); | |
| 263 | umask(~mask); | |
| 264 | ||
| 265 | if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) | |
| 7cfd531a | 266 | err(1, "fts_open"); |
| 984263bc MD |
267 | for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { |
| 268 | switch (curr->fts_info) { | |
| 269 | case FTS_NS: | |
| 270 | case FTS_DNR: | |
| 271 | case FTS_ERR: | |
| 272 | warnx("%s: %s", | |
| 273 | curr->fts_path, strerror(curr->fts_errno)); | |
| e10e866a | 274 | rval = 1; |
| 984263bc MD |
275 | continue; |
| 276 | case FTS_DC: /* Warn, continue. */ | |
| 277 | warnx("%s: directory causes a cycle", curr->fts_path); | |
| e10e866a | 278 | rval = 1; |
| 984263bc | 279 | continue; |
| 7cfd531a JM |
280 | default: |
| 281 | ; | |
| 984263bc MD |
282 | } |
| 283 | ||
| 284 | /* | |
| 285 | * If we are in case (2) or (3) above, we need to append the | |
| 7cfd531a JM |
286 | * source name to the target name. |
| 287 | */ | |
| 984263bc MD |
288 | if (type != FILE_TO_FILE) { |
| 289 | /* | |
| 290 | * Need to remember the roots of traversals to create | |
| 291 | * correct pathnames. If there's a directory being | |
| 292 | * copied to a non-existent directory, e.g. | |
| 293 | * cp -R a/dir noexist | |
| 294 | * the resulting path name should be noexist/foo, not | |
| 295 | * noexist/dir/foo (where foo is a file in dir), which | |
| 296 | * is the case where the target exists. | |
| 297 | * | |
| 298 | * Also, check for "..". This is for correct path | |
| 299 | * concatenation for paths ending in "..", e.g. | |
| 300 | * cp -R .. /tmp | |
| 301 | * Paths ending in ".." are changed to ".". This is | |
| 302 | * tricky, but seems the easiest way to fix the problem. | |
| 303 | * | |
| 304 | * XXX | |
| 305 | * Since the first level MUST be FTS_ROOTLEVEL, base | |
| 306 | * is always initialized. | |
| 307 | */ | |
| 308 | if (curr->fts_level == FTS_ROOTLEVEL) { | |
| 309 | if (type != DIR_TO_DNE) { | |
| 310 | p = strrchr(curr->fts_path, '/'); | |
| 311 | base = (p == NULL) ? 0 : | |
| 312 | (int)(p - curr->fts_path + 1); | |
| 313 | ||
| 314 | if (!strcmp(&curr->fts_path[base], | |
| 315 | "..")) | |
| 316 | base += 1; | |
| 317 | } else | |
| 318 | base = curr->fts_pathlen; | |
| 319 | } | |
| 320 | ||
| 321 | p = &curr->fts_path[base]; | |
| 322 | nlen = curr->fts_pathlen - base; | |
| 323 | target_mid = to.target_end; | |
| 324 | if (*p != '/' && target_mid[-1] != '/') | |
| 325 | *target_mid++ = '/'; | |
| 326 | *target_mid = 0; | |
| 327 | if (target_mid - to.p_path + nlen >= PATH_MAX) { | |
| 328 | warnx("%s%s: name too long (not copied)", | |
| 329 | to.p_path, p); | |
| e10e866a | 330 | rval = 1; |
| 984263bc MD |
331 | continue; |
| 332 | } | |
| 7cfd531a | 333 | (void)strncat(target_mid, p, nlen); |
| 984263bc MD |
334 | to.p_end = target_mid + nlen; |
| 335 | *to.p_end = 0; | |
| 336 | STRIP_TRAILING_SLASH(to); | |
| 337 | } | |
| 338 | ||
| 339 | if (curr->fts_info == FTS_DP) { | |
| 340 | /* | |
| 341 | * We are nearly finished with this directory. If we | |
| 342 | * didn't actually copy it, or otherwise don't need to | |
| 343 | * change its attributes, then we are done. | |
| 344 | */ | |
| 345 | if (!curr->fts_number) | |
| 346 | continue; | |
| 347 | /* | |
| 348 | * If -p is in effect, set all the attributes. | |
| 349 | * Otherwise, set the correct permissions, limited | |
| 350 | * by the umask. Optimise by avoiding a chmod() | |
| 351 | * if possible (which is usually the case if we | |
| 352 | * made the directory). Note that mkdir() does not | |
| 353 | * honour setuid, setgid and sticky bits, but we | |
| 354 | * normally want to preserve them on directories. | |
| 355 | */ | |
| 356 | if (pflag) { | |
| 3abc2185 | 357 | if (setfile(curr->fts_statp, -1)) |
| 984263bc MD |
358 | rval = 1; |
| 359 | } else { | |
| 360 | mode = curr->fts_statp->st_mode; | |
| 361 | if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) || | |
| 362 | ((mode | S_IRWXU) & mask) != (mode & mask)) | |
| 363 | if (chmod(to.p_path, mode & mask) != 0){ | |
| 364 | warn("chmod: %s", to.p_path); | |
| 365 | rval = 1; | |
| 366 | } | |
| 367 | } | |
| 368 | continue; | |
| 369 | } | |
| 370 | ||
| 371 | /* Not an error but need to remember it happened */ | |
| 372 | if (stat(to.p_path, &to_stat) == -1) | |
| 373 | dne = 1; | |
| 374 | else { | |
| 375 | if (to_stat.st_dev == curr->fts_statp->st_dev && | |
| 376 | to_stat.st_ino == curr->fts_statp->st_ino) { | |
| 377 | warnx("%s and %s are identical (not copied).", | |
| 378 | to.p_path, curr->fts_path); | |
| e10e866a | 379 | rval = 1; |
| 984263bc | 380 | if (S_ISDIR(curr->fts_statp->st_mode)) |
| 7cfd531a | 381 | (void)fts_set(ftsp, curr, FTS_SKIP); |
| 984263bc MD |
382 | continue; |
| 383 | } | |
| 384 | if (!S_ISDIR(curr->fts_statp->st_mode) && | |
| 385 | S_ISDIR(to_stat.st_mode)) { | |
| 7cfd531a JM |
386 | warnx("cannot overwrite directory %s with " |
| 387 | "non-directory %s", | |
| 984263bc | 388 | to.p_path, curr->fts_path); |
| e10e866a | 389 | rval = 1; |
| 984263bc MD |
390 | continue; |
| 391 | } | |
| 392 | dne = 0; | |
| 393 | } | |
| 394 | ||
| 395 | switch (curr->fts_statp->st_mode & S_IFMT) { | |
| 396 | case S_IFLNK: | |
| 3abc2185 CP |
397 | /* Catch special case of a non-dangling symlink */ |
| 398 | if ((fts_options & FTS_LOGICAL) || | |
| 399 | ((fts_options & FTS_COMFOLLOW) && | |
| 400 | curr->fts_level == 0)) { | |
| 401 | if (copy_file(curr, dne)) | |
| 402 | badcp = rval = 1; | |
| 403 | } else { | |
| 404 | if (copy_link(curr, !dne)) | |
| 405 | badcp = rval = 1; | |
| 406 | } | |
| 984263bc MD |
407 | break; |
| 408 | case S_IFDIR: | |
| 7cfd531a | 409 | if (!Rflag) { |
| 984263bc MD |
410 | warnx("%s is a directory (not copied).", |
| 411 | curr->fts_path); | |
| 7cfd531a | 412 | (void)fts_set(ftsp, curr, FTS_SKIP); |
| 984263bc MD |
413 | badcp = rval = 1; |
| 414 | break; | |
| 415 | } | |
| 416 | /* | |
| 417 | * If the directory doesn't exist, create the new | |
| 418 | * one with the from file mode plus owner RWX bits, | |
| 419 | * modified by the umask. Trade-off between being | |
| 420 | * able to write the directory (if from directory is | |
| 421 | * 555) and not causing a permissions race. If the | |
| 422 | * umask blocks owner writes, we fail.. | |
| 423 | */ | |
| 424 | if (dne) { | |
| 425 | if (mkdir(to.p_path, | |
| 426 | curr->fts_statp->st_mode | S_IRWXU) < 0) | |
| 427 | err(1, "%s", to.p_path); | |
| 428 | } else if (!S_ISDIR(to_stat.st_mode)) { | |
| 429 | errno = ENOTDIR; | |
| 430 | err(1, "%s", to.p_path); | |
| 431 | } | |
| 432 | /* | |
| 433 | * Arrange to correct directory attributes later | |
| 434 | * (in the post-order phase) if this is a new | |
| 435 | * directory, or if the -p flag is in effect. | |
| 436 | */ | |
| 437 | curr->fts_number = pflag || dne; | |
| 438 | break; | |
| 439 | case S_IFBLK: | |
| 440 | case S_IFCHR: | |
| 441 | if (Rflag) { | |
| 442 | if (copy_special(curr->fts_statp, !dne)) | |
| 443 | badcp = rval = 1; | |
| 444 | } else { | |
| 445 | if (copy_file(curr, dne)) | |
| 446 | badcp = rval = 1; | |
| 447 | } | |
| 448 | break; | |
| 7cfd531a JM |
449 | case S_IFSOCK: |
| 450 | warnx("%s is a socket (not copied).", | |
| 451 | curr->fts_path); | |
| 452 | break; | |
| 984263bc MD |
453 | case S_IFIFO: |
| 454 | if (Rflag) { | |
| 455 | if (copy_fifo(curr->fts_statp, !dne)) | |
| 456 | badcp = rval = 1; | |
| 457 | } else { | |
| 458 | if (copy_file(curr, dne)) | |
| 459 | badcp = rval = 1; | |
| 460 | } | |
| 461 | break; | |
| 462 | default: | |
| 463 | if (copy_file(curr, dne)) | |
| 464 | badcp = rval = 1; | |
| 465 | break; | |
| 466 | } | |
| 467 | if (vflag && !badcp) | |
| 7cfd531a | 468 | (void)printf("%s -> %s\n", curr->fts_path, to.p_path); |
| 984263bc MD |
469 | } |
| 470 | if (errno) | |
| 471 | err(1, "fts_read"); | |
| 13e85e8e | 472 | fts_close(ftsp); |
| 984263bc MD |
473 | return (rval); |
| 474 | } | |
| 475 | ||
| 476 | /* | |
| 477 | * mastercmp -- | |
| 478 | * The comparison function for the copy order. The order is to copy | |
| 479 | * non-directory files before directory files. The reason for this | |
| 480 | * is because files tend to be in the same cylinder group as their | |
| 481 | * parent directory, whereas directories tend not to be. Copying the | |
| 482 | * files first reduces seeking. | |
| 483 | */ | |
| 15938dac | 484 | static int |
| 59b616d3 | 485 | mastercmp(const FTSENT * const *a, const FTSENT * const *b) |
| 984263bc MD |
486 | { |
| 487 | int a_info, b_info; | |
| 488 | ||
| 489 | a_info = (*a)->fts_info; | |
| 490 | if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR) | |
| 491 | return (0); | |
| 492 | b_info = (*b)->fts_info; | |
| 493 | if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR) | |
| 494 | return (0); | |
| 495 | if (a_info == FTS_D) | |
| 496 | return (-1); | |
| 497 | if (b_info == FTS_D) | |
| 498 | return (1); | |
| 499 | return (0); | |
| 500 | } | |
| 779388d9 SS |
501 | |
| 502 | static void | |
| 7cfd531a | 503 | siginfo(int sig __unused) |
| 779388d9 SS |
504 | { |
| 505 | ||
| 506 | info = 1; | |
| 507 | } |