home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / bin / rm / rm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-20  |  7.1 KB  |  295 lines

  1. /*-
  2.  * Copyright (c) 1990 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. char copyright[] =
  36. "@(#) Copyright (c) 1990 The Regents of the University of California.\n\
  37.  All rights reserved.\n";
  38. #endif /* not lint */
  39.  
  40. #ifndef lint
  41. static char sccsid[] = "@(#)rm.c    4.27 (Berkeley) 1/27/92";
  42. #endif /* not lint */
  43.  
  44. #include <sys/types.h>
  45. #include <sys/stat.h>
  46. #include <sys/errno.h>
  47. #include <fts.h>
  48. #include <unistd.h>
  49. #include <stdio.h>
  50. #include <string.h>
  51. #include <stdlib.h>
  52.  
  53. int dflag, fflag, iflag, retval, stdin_ok;
  54.  
  55. /*
  56.  * rm --
  57.  *    This rm is different from historic rm's, but is expected to match
  58.  *    POSIX 1003.2 behavior.  The most visible difference is that -f
  59.  *    has two specific effects now, ignore non-existent files and force
  60.  *     file removal.
  61.  */
  62.  
  63. main(argc, argv)
  64.     int argc;
  65.     char **argv;
  66. {
  67.     extern char *optarg;
  68.     extern int optind;
  69.     int ch, rflag;
  70.  
  71.     rflag = 0;
  72.     while ((ch = getopt(argc, argv, "dfiRr")) != EOF)
  73.         switch(ch) {
  74.         case 'd':
  75.             dflag = 1;
  76.             break;
  77.         case 'f':
  78.             fflag = 1;
  79.             iflag = 0;
  80.             break;
  81.         case 'i':
  82.             fflag = 0;
  83.             iflag = 1;
  84.             break;
  85.         case 'R':
  86.         case 'r':            /* compatibility */
  87.             rflag = 1;
  88.             break;
  89.         case '?':
  90.         default:
  91.             usage();
  92.         }
  93.     argc -= optind;
  94.     argv += optind;
  95.  
  96.     if (argc < 1)
  97.         usage();
  98.  
  99.     checkdot(argv);
  100.     if (!*argv)
  101.         exit(retval);
  102.  
  103.     stdin_ok = isatty(STDIN_FILENO);
  104.  
  105.     if (rflag)
  106.         rmtree(argv);
  107.     else
  108.         rmfile(argv);
  109.     exit(retval);
  110. }
  111.  
  112. rmtree(argv)
  113.     char **argv;
  114. {
  115.     register FTS *fts;
  116.     register FTSENT *p;
  117.     register int needstat;
  118.     struct stat sb;
  119.  
  120.     /*
  121.      * Remove a file hierarchy.  If forcing removal (-f), or interactive
  122.      * (-i) or can't ask anyway (stdin_ok), don't stat the file.
  123.      */
  124.     needstat = !fflag && !iflag && stdin_ok;
  125.  
  126.     /*
  127.      * If the -i option is specified, the user can skip on the pre-order
  128.      * visit.  The fts_number field flags skipped directories.
  129.      */
  130. #define    SKIPPED    1
  131.  
  132.     if (!(fts = fts_open(argv,
  133.         needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT,
  134.         (int (*)())NULL))) {
  135.         (void)fprintf(stderr, "rm: %s.\n", strerror(errno));
  136.         exit(1);
  137.     }
  138.     while (p = fts_read(fts)) {
  139.         switch(p->fts_info) {
  140.         case FTS_DNR:
  141.         case FTS_ERR:
  142.             error(p->fts_path, errno);
  143.             exit(1);
  144.         /*
  145.          * FTS_NS: assume that if can't stat the file, it can't be
  146.          * unlinked.
  147.          */
  148.         case FTS_NS:
  149.             if (!needstat)
  150.                 break;
  151.             if (!fflag || errno != ENOENT)
  152.                 error(p->fts_path, errno);
  153.             continue;
  154.         /* Pre-order: give user chance to skip. */
  155.         case FTS_D:
  156.             if (iflag && !check(p->fts_path, p->fts_accpath,
  157.                 p->fts_statp)) {
  158.                 (void)fts_set(fts, p, FTS_SKIP);
  159.                 p->fts_number = SKIPPED;
  160.             }
  161.             continue;
  162.         /* Post-order: see if user skipped. */
  163.         case FTS_DP:
  164.             if (p->fts_number == SKIPPED)
  165.                 continue;
  166.             break;
  167.         }
  168.  
  169.         if (!fflag &&
  170.             !check(p->fts_path, p->fts_accpath, p->fts_statp))
  171.             continue;
  172.  
  173.         /*
  174.          * If we can't read or search the directory, may still be
  175.          * able to remove it.  Don't print out the un{read,search}able
  176.          * message unless the remove fails.
  177.          */
  178.         if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) {
  179.             if (!rmdir(p->fts_accpath))
  180.                 continue;
  181.             if (errno == ENOENT) {
  182.                 if (fflag)
  183.                     continue;
  184.             } else if (p->fts_info != FTS_DP)
  185.                 (void)fprintf(stderr,
  186.                     "rm: unable to read %s.\n", p->fts_path);
  187.         } else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT)
  188.             continue;
  189.         error(p->fts_path, errno);
  190.     }
  191. }
  192.  
  193. rmfile(argv)
  194.     char **argv;
  195. {
  196.     register int df;
  197.     register char *f;
  198.     struct stat sb;
  199.  
  200.     df = dflag;
  201.     /*
  202.      * Remove a file.  POSIX 1003.2 states that, by default, attempting
  203.      * to remove a directory is an error, so must always stat the file.
  204.      */
  205.     while (f = *argv++) {
  206.         /* Assume if can't stat the file, can't unlink it. */
  207.         if (lstat(f, &sb)) {
  208.             if (!fflag || errno != ENOENT)
  209.                 error(f, errno);
  210.             continue;
  211.         }
  212.         if (S_ISDIR(sb.st_mode) && !df) {
  213.             (void)fprintf(stderr, "rm: %s: is a directory\n", f);
  214.             retval = 1;
  215.             continue;
  216.         }
  217.         if (!fflag && !check(f, f, &sb))
  218.             continue;
  219.         if ((S_ISDIR(sb.st_mode) ? rmdir(f) : unlink(f)) &&
  220.             (!fflag || errno != ENOENT))
  221.             error(f, errno);
  222.     }
  223. }
  224.  
  225. check(path, name, sp)
  226.     char *path, *name;
  227.     struct stat *sp;
  228. {
  229.     register int first, ch;
  230.     char modep[15], *user_from_uid(), *group_from_gid();
  231.  
  232.     /* Check -i first. */
  233.     if (iflag)
  234.         (void)fprintf(stderr, "remove %s? ", path);
  235.     else {
  236.         /*
  237.          * If it's not a symbolic link and it's unwritable and we're
  238.          * talking to a terminal, ask.  Symbolic links are excluded
  239.          * because their permissions are meaningless.
  240.          */
  241.         if (S_ISLNK(sp->st_mode) || !stdin_ok || !access(name, W_OK))
  242.             return(1);
  243.         strmode(sp->st_mode, modep);
  244.         (void)fprintf(stderr, "override %s%s%s/%s for %s? ",
  245.             modep + 1, modep[9] == ' ' ? "" : " ",
  246.             user_from_uid(sp->st_uid, 0),
  247.             group_from_gid(sp->st_gid, 0), path);
  248.     }
  249.     (void)fflush(stderr);
  250.  
  251.     first = ch = getchar();
  252.     while (ch != '\n' && ch != EOF)
  253.         ch = getchar();
  254.     return(first == 'y');
  255. }
  256.  
  257. #define ISDOT(a)    ((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2]))
  258. checkdot(argv)
  259.     char **argv;
  260. {
  261.     register char *p, **t, **save;
  262.     int complained;
  263.  
  264.     complained = 0;
  265.     for (t = argv; *t;) {
  266.         if (p = rindex(*t, '/'))
  267.             ++p;
  268.         else
  269.             p = *t;
  270.         if (ISDOT(p)) {
  271.             if (!complained++)
  272.                 (void)fprintf(stderr,
  273.                 "rm: \".\" and \"..\" may not be removed.\n");
  274.             retval = 1;
  275.             for (save = t; t[0] = t[1]; ++t);
  276.             t = save;
  277.         } else
  278.             ++t;
  279.     }
  280. }
  281.  
  282. error(name, val)
  283.     char *name;
  284.     int val;
  285. {
  286.     (void)fprintf(stderr, "rm: %s: %s.\n", name, strerror(val));
  287.     retval = 1;
  288. }
  289.  
  290. usage()
  291. {
  292.     (void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n");
  293.     exit(1);
  294. }
  295.