Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 /*-
0002  * Copyright (c) 1990, 1993, 1994
0003  *  The Regents of the University of California.  All rights reserved.
0004  *
0005  * Redistribution and use in source and binary forms, with or without
0006  * modification, are permitted provided that the following conditions
0007  * are met:
0008  * 1. Redistributions of source code must retain the above copyright
0009  *    notice, this list of conditions and the following disclaimer.
0010  * 2. Redistributions in binary form must reproduce the above copyright
0011  *    notice, this list of conditions and the following disclaimer in the
0012  *    documentation and/or other materials provided with the distribution.
0013  * 4. Neither the name of the University nor the names of its contributors
0014  *    may be used to endorse or promote products derived from this software
0015  *    without specific prior written permission.
0016  *
0017  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
0018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
0021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0027  * SUCH DAMAGE.
0028  */
0029 
0030 #ifdef HAVE_CONFIG_H
0031 #include "config.h"
0032 #endif
0033 
0034 #if 0
0035 #ifndef lint
0036 static const char copyright[] =
0037 "@(#) Copyright (c) 1990, 1993, 1994\n\
0038     The Regents of the University of California.  All rights reserved.\n";
0039 #endif /* not lint */
0040 
0041 #ifndef lint
0042 static char sccsid[] = "@(#)rm.c    8.5 (Berkeley) 4/18/94";
0043 #endif /* not lint */
0044 #endif
0045 #if 0
0046 #include <sys/cdefs.h>
0047 __FBSDID("$FreeBSD: src/bin/rm/rm.c,v 1.58 2006/10/31 02:22:36 delphij Exp $");
0048 #endif
0049 
0050 #include <rtems.h>
0051 #include <rtems/inttypes.h>
0052 #include <rtems/shell.h>
0053 #include <rtems/shellconfig.h>
0054 #define __need_getopt_newlib
0055 #include <getopt.h>
0056 
0057 #include <sys/stat.h>
0058 #include <sys/param.h>
0059 
0060 #include "err.h"
0061 #include <errno.h>
0062 #include <fcntl.h>
0063 #include "fts.h"
0064 #include <grp.h>
0065 #include <pwd.h>
0066 #include <stdio.h>
0067 #include <stdlib.h>
0068 #include <string.h>
0069 #include <unistd.h>
0070 
0071 #include "internal.h"
0072 
0073 #define rindex(s,c) strrchr(s,c)
0074 
0075 /* RTEMS specific changes */
0076 typedef struct {
0077   int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
0078   int rflag, Iflag;
0079   uid_t uid;
0080   int exit_code;
0081   jmp_buf exit_jmp;
0082 } rtems_shell_rm_globals;
0083 
0084 #define dflag     globals->dflag
0085 #define eval      globals->eval
0086 #define fflag     globals->fflag
0087 #define iflag     globals->iflag
0088 #define Pflag     globals->Pflag
0089 #define vflag     globals->vflag
0090 #define Wflag     globals->Wflag
0091 #define stdin_ok  globals->stdin_ok
0092 #define rflag     globals->rflag
0093 #define Iflag     globals->Iflag
0094 #define xuid       globals->uid
0095 #define exit_jump &(globals->exit_jmp)
0096 
0097 #include <setjmp.h>
0098 
0099 #define exit(ec) rtems_shell_rm_exit(globals, ec)
0100 static void
0101 rtems_shell_rm_exit (rtems_shell_rm_globals* globals, int code)
0102 {
0103   globals->exit_code = code;
0104   longjmp (globals->exit_jmp, 1);
0105 }
0106 
0107 static int main_rm(rtems_shell_rm_globals* globals, int argc, char *argv[]);
0108 
0109 int
0110 rtems_shell_main_rm(int argc, char *argv[])
0111 {
0112   rtems_shell_rm_globals rm_globals;
0113   memset (&rm_globals, 0, sizeof (rm_globals));
0114   rm_globals.exit_code = 1;
0115   if (setjmp (rm_globals.exit_jmp) == 0)
0116     return main_rm (&rm_globals, argc, argv);
0117   return rm_globals.exit_code;
0118 }
0119 
0120 #define check(a1, a2, a3)    check_rm(globals, a1, a2, a3)
0121 #define check2(a1)           check2_rm(globals, a1)
0122 #define checkdot(a1)         checkdot_rm(globals, a1)
0123 #define checkslash(a1)       checkslash_rm(globals, a1)
0124 #define rm_file(a1)          rm_file_rm(globals, a1)
0125 #define rm_overwrite(a1, a2) rm_overwrite_rm(globals, a1, a2)
0126 #define rm_tree(a1)          rm_tree_rm(globals, a1)
0127 #define usage()              usage_rm(globals)
0128 
0129 
0130 /* RTEMS changes */
0131 
0132 static int  check_rm(rtems_shell_rm_globals* globals, char *, char *, struct stat *);
0133 static int  check2_rm(rtems_shell_rm_globals* globals, char **);
0134 static void checkdot_rm(rtems_shell_rm_globals* globals, char **);
0135 static void checkslash_rm(rtems_shell_rm_globals* globals, char **);
0136 static void rm_file_rm(rtems_shell_rm_globals* globals, char **);
0137 static int  rm_overwrite_rm(rtems_shell_rm_globals* globals, char *, struct stat *);
0138 static void rm_tree_rm(rtems_shell_rm_globals* globals, char **);
0139 static void usage_rm(rtems_shell_rm_globals* globals);
0140 
0141 /*
0142  * rm --
0143  *  This rm is different from historic rm's, but is expected to match
0144  *  POSIX 1003.2 behavior.  The most visible difference is that -f
0145  *  has two specific effects now, ignore non-existent files and force
0146  *  file removal.
0147  */
0148 int
0149 main_rm(rtems_shell_rm_globals* globals, int argc, char *argv[])
0150 {
0151     int ch;
0152     char *p;
0153 
0154   struct getopt_data getopt_reent;
0155   memset(&getopt_reent, 0, sizeof(getopt_data));
0156 
0157     /*
0158      * Test for the special case where the utility is called as
0159      * "unlink", for which the functionality provided is greatly
0160      * simplified.
0161      */
0162     if ((p = rindex(argv[0], '/')) == NULL)
0163         p = argv[0];
0164     else
0165         ++p;
0166     if (strcmp(p, "unlink") == 0) {
0167         while (getopt_r(argc, argv, "", &getopt_reent) != -1)
0168             usage();
0169         argc -= getopt_reent.optind;
0170         argv += getopt_reent.optind;
0171         if (argc != 1)
0172             usage();
0173         rm_file(&argv[0]);
0174         exit(eval);
0175     }
0176 
0177     Pflag = rflag = 0;
0178     while ((ch = getopt_r(argc, argv, "dfiIPRrvW", &getopt_reent)) != -1)
0179         switch(ch) {
0180         case 'd':
0181             dflag = 1;
0182             break;
0183         case 'f':
0184             fflag = 1;
0185             iflag = 0;
0186             break;
0187         case 'i':
0188             fflag = 0;
0189             iflag = 1;
0190             break;
0191         case 'I':
0192             Iflag = 1;
0193             break;
0194         case 'P':
0195             Pflag = 1;
0196             break;
0197         case 'R':
0198         case 'r':           /* Compatibility. */
0199             rflag = 1;
0200             break;
0201         case 'v':
0202             vflag = 1;
0203             break;
0204         case 'W':
0205             Wflag = 1;
0206             break;
0207         default:
0208             usage();
0209         }
0210     argc -= getopt_reent.optind;
0211     argv += getopt_reent.optind;
0212 
0213     if (argc < 1) {
0214         if (fflag)
0215             return (0);
0216         usage();
0217     }
0218 
0219     checkdot(argv);
0220     if (getenv("POSIXLY_CORRECT") == NULL)
0221         checkslash(argv);
0222     xuid = geteuid();
0223 
0224     if (*argv) {
0225         stdin_ok = isatty(STDIN_FILENO);
0226 
0227         if (Iflag) {
0228             if (check2(argv) == 0)
0229                 exit (1);
0230         }
0231         if (rflag)
0232             rm_tree(argv);
0233         else
0234             rm_file(argv);
0235     }
0236 
0237     exit (eval);
0238   return 0;
0239 }
0240 
0241 void
0242 rm_tree_rm(rtems_shell_rm_globals* globals, char **argv)
0243 {
0244     FTS *fts;
0245     FTSENT *p;
0246     int needstat;
0247     int flags;
0248     int rval;
0249 
0250     /*
0251      * Remove a file hierarchy.  If forcing removal (-f), or interactive
0252      * (-i) or can't ask anyway (stdin_ok), don't stat the file.
0253      */
0254     needstat = !xuid || (!fflag && !iflag && stdin_ok);
0255 
0256     /*
0257      * If the -i option is specified, the user can skip on the pre-order
0258      * visit.  The fts_number field flags skipped directories.
0259      */
0260 #define SKIPPED 1
0261 
0262     flags = FTS_PHYSICAL;
0263     if (!needstat)
0264         flags |= FTS_NOSTAT;
0265     if (Wflag)
0266         flags |= FTS_WHITEOUT;
0267     if (!(fts = fts_open(argv, flags, NULL))) {
0268         if (fflag && errno == ENOENT)
0269             return;
0270         err(exit_jump, 1, "fts_open");
0271     }
0272     while ((p = fts_read(fts)) != NULL) {
0273         switch (p->fts_info) {
0274         case FTS_DNR:
0275             if (!fflag || p->fts_errno != ENOENT) {
0276                 warnx("%s: %s",
0277                     p->fts_path, strerror(p->fts_errno));
0278                 eval = 1;
0279             }
0280             continue;
0281         case FTS_ERR:
0282             errx(exit_jump, 1, "%s: %s", p->fts_path, strerror(p->fts_errno));
0283         case FTS_NS:
0284             /*
0285              * Assume that since fts_read() couldn't stat the
0286              * file, it can't be unlinked.
0287              */
0288             if (!needstat)
0289                 break;
0290             if (!fflag || p->fts_errno != ENOENT) {
0291                 warnx("%s: %s",
0292                     p->fts_path, strerror(p->fts_errno));
0293                 eval = 1;
0294             }
0295             continue;
0296         case FTS_D:
0297             /* Pre-order: give user chance to skip. */
0298             if (!fflag && !check(p->fts_path, p->fts_accpath,
0299                 p->fts_statp)) {
0300                 (void)fts_set(fts, p, FTS_SKIP);
0301                 p->fts_number = SKIPPED;
0302             }
0303 #if RTEMS_REMOVED
0304             else if (!xuid &&
0305                  (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
0306                  !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
0307                  chflags(p->fts_accpath,
0308                      p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
0309                 goto err;
0310 #endif
0311             continue;
0312         case FTS_DP:
0313             /* Post-order: see if user skipped. */
0314             if (p->fts_number == SKIPPED)
0315                 continue;
0316             break;
0317         default:
0318             if (!fflag &&
0319                 !check(p->fts_path, p->fts_accpath, p->fts_statp))
0320                 continue;
0321         }
0322 
0323         rval = 0;
0324 #if RTEMS_REMOVED
0325         if (!xuid &&
0326             (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
0327             !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
0328             rval = chflags(p->fts_accpath,
0329                        p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
0330 #endif
0331         if (rval == 0) {
0332             /*
0333              * If we can't read or search the directory, may still be
0334              * able to remove it.  Don't print out the un{read,search}able
0335              * message unless the remove fails.
0336              */
0337             switch (p->fts_info) {
0338             case FTS_DP:
0339             case FTS_DNR:
0340                 rval = rmdir(p->fts_accpath);
0341                 if (rval == 0 || (fflag && errno == ENOENT)) {
0342                     if (rval == 0 && vflag)
0343                         (void)printf("%s\n",
0344                             p->fts_path);
0345                     continue;
0346                 }
0347                 break;
0348 
0349 #if RTEMS_REMOVED
0350             case FTS_W:
0351                 rval = undelete(p->fts_accpath);
0352                 if (rval == 0 && (fflag && errno == ENOENT)) {
0353                     if (vflag)
0354                         (void)printf("%s\n",
0355                             p->fts_path);
0356                     continue;
0357                 }
0358                 break;
0359 #endif
0360 
0361             case FTS_NS:
0362                 /*
0363                  * Assume that since fts_read() couldn't stat
0364                  * the file, it can't be unlinked.
0365                  */
0366                 if (fflag)
0367                     continue;
0368                 /* FALLTHROUGH */
0369             default:
0370                 if (Pflag)
0371                     if (!rm_overwrite(p->fts_accpath, NULL))
0372                         continue;
0373                 rval = unlink(p->fts_accpath);
0374                 if (rval == 0 || (fflag && errno == ENOENT)) {
0375                     if (rval == 0 && vflag)
0376                         (void)printf("%s\n",
0377                             p->fts_path);
0378                     continue;
0379                 }
0380             }
0381         }
0382 #if RTEMS_REMOVED
0383 err:
0384 #endif
0385         warn("%s", p->fts_path);
0386         eval = 1;
0387     }
0388     if (errno)
0389         err(exit_jump, 1, "fts_read");
0390     fts_close(fts);
0391 }
0392 
0393 #define S_ISWHT(m) (0)
0394 
0395 void
0396 rm_file_rm(rtems_shell_rm_globals* globals, char **argv)
0397 {
0398     struct stat sb;
0399     int rval;
0400     char *f;
0401 
0402     /*
0403      * Remove a file.  POSIX 1003.2 states that, by default, attempting
0404      * to remove a directory is an error, so must always stat the file.
0405      */
0406     while ((f = *argv++) != NULL) {
0407         /* Assume if can't stat the file, can't unlink it. */
0408         if (lstat(f, &sb)) {
0409 #if RTEMS_REMOVED
0410             if (Wflag) {
0411                 sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
0412             } else {
0413 #endif
0414                 if (!fflag || errno != ENOENT) {
0415                     warn("%s", f);
0416                     eval = 1;
0417                 }
0418                 continue;
0419 #if RTEMS_REMOVED
0420             }
0421 #endif
0422         } else if (Wflag) {
0423             warnx("%s: %s", f, strerror(EEXIST));
0424             eval = 1;
0425             continue;
0426         }
0427 
0428         if (S_ISDIR(sb.st_mode) && !dflag) {
0429             warnx("%s: is a directory", f);
0430             eval = 1;
0431             continue;
0432         }
0433         if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
0434             continue;
0435         rval = 0;
0436 #if RTEMS_REMOVED
0437         if (!xuid && !S_ISWHT(sb.st_mode) &&
0438             (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
0439             !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
0440             rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
0441 #endif
0442         if (rval == 0) {
0443             if (S_ISWHT(sb.st_mode))
0444 #if RTEMS_REMOVED
0445                 rval = undelete(f);
0446 #endif
0447       ;
0448             else if (S_ISDIR(sb.st_mode))
0449                 rval = rmdir(f);
0450             else {
0451                 if (Pflag)
0452                     if (!rm_overwrite(f, &sb))
0453                         continue;
0454                 rval = unlink(f);
0455             }
0456         }
0457         if (rval && (!fflag || errno != ENOENT)) {
0458             warn("%s", f);
0459             eval = 1;
0460         }
0461         if (vflag && rval == 0)
0462             (void)printf("%s\n", f);
0463     }
0464 }
0465 
0466 /*
0467  * rm_overwrite --
0468  *  Overwrite the file 3 times with varying bit patterns.
0469  *
0470  * XXX
0471  * This is a cheap way to *really* delete files.  Note that only regular
0472  * files are deleted, directories (and therefore names) will remain.
0473  * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
0474  * System V file system).  In a logging file system, you'll have to have
0475  * kernel support.
0476  */
0477 int
0478 rm_overwrite_rm(rtems_shell_rm_globals* globals, char *file, struct stat *sbp)
0479 {
0480     struct stat sb;
0481 #if RTEMS_REMOVED
0482     struct statfs fsb;
0483 #endif
0484     off_t len;
0485     int bsize, fd, wlen;
0486     char *buf = NULL;
0487 
0488     fd = -1;
0489     if (sbp == NULL) {
0490         if (lstat(file, &sb))
0491             goto err;
0492         sbp = &sb;
0493     }
0494     if (!S_ISREG(sbp->st_mode))
0495         return (1);
0496     if (sbp->st_nlink > 1 && !fflag) {
0497         warnx("%s (inode %" PRIuino_t
0498             "): not overwritten due to multiple links", file,
0499             sbp->st_ino);
0500         return (0);
0501     }
0502     if ((fd = open(file, O_WRONLY, 0)) == -1)
0503         goto err;
0504 #if RTEMS_REMOVED
0505     if (fstatfs(fd, &fsb) == -1)
0506         goto err;
0507     bsize = MAX(fsb.f_iosize, 1024);
0508 #endif
0509   bsize = 1024;
0510     if ((buf = malloc(bsize)) == NULL)
0511         err(exit_jump, 1, "%s: malloc", file);
0512 
0513 #define PASS(byte) {                            \
0514     memset(buf, byte, bsize);                   \
0515     for (len = sbp->st_size; len > 0; len -= wlen) {        \
0516         wlen = len < bsize ? len : bsize;           \
0517         if (write(fd, buf, wlen) != wlen)           \
0518             goto err;                   \
0519     }                               \
0520 }
0521     PASS(0xff);
0522     if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
0523         goto err;
0524     PASS(0x00);
0525     if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
0526         goto err;
0527     PASS(0xff);
0528     if (!fsync(fd) && !close(fd)) {
0529         free(buf);
0530         return (1);
0531     }
0532 
0533 err:    eval = 1;
0534     if (buf)
0535         free(buf);
0536     if (fd != -1)
0537         close(fd);
0538     warn("%s", file);
0539     return (0);
0540 }
0541 
0542 #if RTEMS_REMOVED
0543 char *fflagstostr(u_long flags);
0544 #endif
0545 
0546 int
0547 check_rm(rtems_shell_rm_globals* globals, char *path, char *name, struct stat *sp)
0548 {
0549     int ch, first;
0550     char modep[15], *flagsp;
0551 
0552     /* Check -i first. */
0553     if (iflag)
0554         (void)fprintf(stderr, "remove %s? ", path);
0555     else {
0556         /*
0557          * If it's not a symbolic link and it's unwritable and we're
0558          * talking to a terminal, ask.  Symbolic links are excluded
0559          * because their permissions are meaningless.  Check stdin_ok
0560          * first because we may not have stat'ed the file.
0561          */
0562 #if RTEMS_REMOVED
0563         if (!stdin_ok || S_ISLNK(sp->st_mode) ||
0564             (!access(name, W_OK) &&
0565             !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
0566             (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !xuid)))
0567 #endif
0568         if (!stdin_ok || S_ISLNK(sp->st_mode) ||
0569             (!access(name, W_OK)))
0570             return (1);
0571         strmode(sp->st_mode, modep);
0572 #if RTEMS_REMOVED
0573         if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
0574             err(exit_jump, 1, "fflagstostr");
0575 #else
0576     flagsp = "no supported";
0577 #endif
0578         if (Pflag)
0579             errx(exit_jump, 1,
0580                 "%s: -P was specified, but file is not writable",
0581                 path);
0582         (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
0583             modep + 1, modep[9] == ' ' ? "" : " ",
0584             user_from_uid(sp->st_uid, 0),
0585             group_from_gid(sp->st_gid, 0),
0586             *flagsp ? flagsp : "", *flagsp ? " " : "",
0587             path);
0588 #if RTEMS_REMOVED
0589         free(flagsp);
0590 #endif
0591     }
0592     (void)fflush(stderr);
0593 
0594     first = ch = getchar();
0595     while (ch != '\n' && ch != EOF)
0596         ch = getchar();
0597     return (first == 'y' || first == 'Y');
0598 }
0599 
0600 #define ISSLASH(a)  ((a)[0] == '/' && (a)[1] == '\0')
0601 void
0602 checkslash_rm(rtems_shell_rm_globals* globals, char **argv)
0603 {
0604     char **t, **u;
0605     int complained;
0606 
0607     complained = 0;
0608     for (t = argv; *t;) {
0609         if (ISSLASH(*t)) {
0610             if (!complained++)
0611                 warnx("\"/\" may not be removed");
0612             eval = 1;
0613             for (u = t; u[0] != NULL; ++u)
0614                 u[0] = u[1];
0615         } else {
0616             ++t;
0617         }
0618     }
0619 }
0620 
0621 int
0622 check2_rm(rtems_shell_rm_globals* globals, char **argv)
0623 {
0624     struct stat st;
0625     int first;
0626     int ch;
0627     int fcount = 0;
0628     int dcount = 0;
0629     int i;
0630     const char *dname = NULL;
0631 
0632     for (i = 0; argv[i]; ++i) {
0633         if (lstat(argv[i], &st) == 0) {
0634             if (S_ISDIR(st.st_mode)) {
0635                 ++dcount;
0636                 dname = argv[i];    /* only used if 1 dir */
0637             } else {
0638                 ++fcount;
0639             }
0640         }
0641     }
0642     first = 0;
0643     while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') {
0644         if (dcount && rflag) {
0645             fprintf(stderr, "recursively remove");
0646             if (dcount == 1)
0647                 fprintf(stderr, " %s", dname);
0648             else
0649                 fprintf(stderr, " %d dirs", dcount);
0650             if (fcount == 1)
0651                 fprintf(stderr, " and 1 file");
0652             else if (fcount > 1)
0653                 fprintf(stderr, " and %d files", fcount);
0654         } else if (dcount + fcount > 3) {
0655             fprintf(stderr, "remove %d files", dcount + fcount);
0656         } else {
0657             return(1);
0658         }
0659         fprintf(stderr, "? ");
0660         fflush(stderr);
0661 
0662         first = ch = getchar();
0663         while (ch != '\n' && ch != EOF)
0664             ch = getchar();
0665         if (ch == EOF)
0666             break;
0667     }
0668     return (first == 'y' || first == 'Y');
0669 }
0670 
0671 #define ISDOT(a)    ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
0672 void
0673 checkdot_rm(rtems_shell_rm_globals* globals, char **argv)
0674 {
0675     char *p, **save, **t;
0676     int complained;
0677 
0678     complained = 0;
0679     for (t = argv; *t;) {
0680         if ((p = strrchr(*t, '/')) != NULL)
0681             ++p;
0682         else
0683             p = *t;
0684         if (ISDOT(p)) {
0685             if (!complained++)
0686                 warnx("\".\" and \"..\" may not be removed");
0687             eval = 1;
0688             for (save = t; (t[0] = t[1]) != NULL; ++t)
0689                 continue;
0690             t = save;
0691         } else
0692             ++t;
0693     }
0694 }
0695 
0696 void
0697 usage_rm(rtems_shell_rm_globals* globals)
0698 {
0699 
0700     (void)fprintf(stderr, "%s\n%s\n",
0701         "usage: rm [-f | -i] [-dIPRrvW] file ...",
0702         "       unlink file");
0703     exit(1);
0704 }
0705 
0706 rtems_shell_cmd_t rtems_shell_RM_Command = {
0707   "rm",                                      /* name */
0708   "[-f | -i] [-dIPRrvW] file ...",           /* usage */
0709   "files",                                   /* topic */
0710   rtems_shell_main_rm,                       /* command */
0711   NULL,                                      /* alias */
0712   NULL                                       /* next */
0713 };