home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / dirutl / rm.arc / RM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1987-04-29  |  12.6 KB  |  468 lines

  1. /*
  2.  *      RM.C                        Version 3.0
  3.  *
  4.  * Description:
  5.  * This program simulates the UNIX "rm" command. It allows one to remove
  6.  * files and/or directory structures with ease. The options are:
  7.  *
  8.  *    -i    Inquire before delete mode.
  9.  *     -r    Recursively delete directory structures.
  10.  *    -a    Inquire before deleting files that have the archive bit set.
  11.  *    -f    Remove without prompting or complaining.
  12.  *
  13.  * The option flags apply only to those items that follow them on the
  14.  * command line. For instance:
  15.  *
  16.  *            rm dumb.txt -r dumbdir -i a:gooddir
  17.  *
  18.  * will delete "dumb.txt", then recursively delete "dumbdir" and all it's
  19.  * contents, then ask before recursively deleting "a:gooddir". If you say
  20.  * "yes", it will then stop on each item in "a:gooddir" and ask before
  21.  * deleting it. If you leave anything un-deleted (i.e. the directory is
  22.  * not empty) then the directory chain down to the leftover items will
  23.  * not be removed even if you asked it to do so. If you respond "no" to
  24.  * removing a directory, that directory and all it's contents are left
  25.  * alone.
  26.  *
  27.  * It will always ask before deleting files with special attributes, such
  28.  * as a READONLY marked file, unless the -f option is used. The -i and -a
  29.  * options are mutually exclusive with the -f option (i.e. they will cancel
  30.  * the -f option and -f will cancel both -a and -i options for those items
  31.  * following them on the command line).
  32.  *
  33.  * Command line style arguments may also be passed through the environment
  34.  * variable RMOPTS. For example, "set RMOPTS = -i" will cause "rm" to always
  35.  * prompt before deleting. File names can also be given in RMOPTS and will
  36.  * make "rm" always try to delete the named files. NOTE: arguments given in
  37.  * RMOPTS will always be executed first. True command line arguments will
  38.  * follow those given in RMOPTS.
  39.  *
  40.  * UNIX is a trademark of AT&T.
  41.  *
  42.  *             Use at your own risk!
  43.  *
  44.  * Environment:
  45.  * MS/PC-DOS V2.0 or higher.
  46.  *
  47.  * Compile:
  48.  * Using Microsoft C V4.0 -- "cl -F F000 rm.c -o rm"
  49.  *    (-F is used to expand the stack size)
  50.  *
  51.  * Bugs (features):
  52.  * 1) It will not delete the current directory or the root directory.
  53.  * 2) It will not delete directories with anything left in them.
  54.  * 3) To remove everything, you must type "*.*" not just "*".
  55.  *
  56.  *    Copyright: April 1987        By: Mark D. Salzman
  57.  *
  58.  * Permission is granted to distribute this free of charge so long as
  59.  * this notice remains intact.
  60.  */
  61.  
  62. #include <dos.h>
  63. #include <stdio.h>
  64. #include <stdlib.h>
  65. #include <direct.h>
  66. #include <string.h>
  67. #include <process.h>
  68.  
  69. #define TRUE    1
  70. #define FALSE    0
  71.  
  72. /* define the attribute bit masks */
  73. #define    A_READONLY    0x01
  74. #define A_HIDDEN    0x02
  75. #define A_SYSTEM    0x04
  76. #define A_VOLUME    0x08
  77. #define A_SUBDIR    0x10
  78. #define A_ARCHIVE    0x20
  79.  
  80. /* define the default search mask */
  81. #define S_MASK    (A_HIDDEN | A_SYSTEM | A_SUBDIR)
  82.  
  83. #define DOSLINE    128        /* Length of the DOS command line */
  84.  
  85. struct dosdta            /* DOS Data Transfer Area table */
  86.     {
  87.     char reserved[21];        /* Used in FIND NEXT operation */
  88.     char attrib;        /* File attributes byte */
  89.     int tstamp;            /* File time stamp */
  90.     int dstamp;            /* File date stamp */
  91.     long fsize;            /* File size in bytes */
  92.     char fname[13];        /* File name and extension (string) */
  93.     };
  94.  
  95. int inquire, recurse, archive, quiet;    /* Global option flags */
  96.  
  97. int name;            /* Flag to see if a name was ever given. */
  98.  
  99. /*
  100.  * The main part initializes the option flags, checks for the environment
  101.  * variable RMOPTS, parses it's contents if it exists, parses the command
  102.  * line, and passes the whole mess to "checkargs" for processing.
  103.  */
  104. main (argc, argv)
  105. int argc;
  106. char *argv[];
  107. {
  108.     char envline[DOSLINE], *envargs[DOSLINE/2], *env;
  109.     int envcnt;
  110.  
  111.     inquire = FALSE;        /* Initialize flags */
  112.     recurse = FALSE;
  113.     archive = FALSE;
  114.     quiet = FALSE;
  115.     name = FALSE;
  116.     envcnt = 0;
  117.     if ((env = getenv("RMOPTS")) != NULL)    /* Does RMOPTS exist? */
  118.     {
  119.     strcpy(envline, env);            /* If so, copy it's value */
  120.     envcnt = parse(envline, envargs);    /* and parse it like the */
  121.     if (envcnt != 0)            /* command line. */
  122.         checkargs(envcnt, envargs, 0);
  123.     }
  124.     if ((argc < 2) && (envcnt == 0))    /* If no arguments, print usage. */
  125.     usage();
  126.     checkargs(argc, argv, 1);        /* Parse the real command line */
  127.     if (name == FALSE) usage();        /* If no names given, print usage */
  128. }
  129.  
  130. /*
  131.  * This procedure checks the arguments for option flags and item names,
  132.  * sets the options as they come up, and passes the item names to the
  133.  * delete function.
  134.  */
  135. checkargs(ac, av, start)
  136. int ac;
  137. char *av[];
  138. int start;
  139. {
  140.     int cnt, cn;
  141.  
  142.     for (cnt = start ; cnt < ac ; ++cnt)    /* Check the arguments */
  143.     {                    /* If it is an option flag */
  144.     if((av[cnt][0] == '-') || (av[cnt][0] == '/'))
  145.         for(cn = 1 ; cn < strlen(av[cnt]) ; ++cn)
  146.         switch (av[cnt][cn])
  147.             {
  148.             case 'i' :    inquire = TRUE;
  149.                 quiet = FALSE;
  150.                 break;
  151.             case 'r' :    recurse = TRUE;
  152.                 break;
  153.             case 'a' :    archive = TRUE;
  154.                 quiet = FALSE;
  155.                 break;
  156.             case 'f' :    quiet = TRUE;
  157.                 inquire = FALSE;
  158.                 archive = FALSE;
  159.                 break;
  160.             default :
  161.             fprintf(stderr,"Unknown Option Flag '%s'\n",av[cnt]);
  162.             usage();
  163.             }
  164.     else
  165.         {            /* Otherwise treat it as a name */
  166.         name = TRUE;
  167.         delete(av[cnt]);
  168.         }
  169.     }
  170.  
  171. }
  172.  
  173. /*
  174.  * This routine expands a item name (possibly containing "*" or "?") and calls
  175.  * "rmitem" to delete all matching files (or directories if -r is given).
  176.  */
  177. delete(arg)
  178. char *arg;
  179. {
  180.     char dirspec[DOSLINE];   /* String used to contain the directory path */
  181.     char *tmp; 
  182.     struct dosdta dta;
  183.  
  184.     /* Determine the path by stripping off the file name part */
  185.     strcpy(dirspec, arg);
  186.     if((tmp = strrchr(dirspec,'\\')) == NULL)
  187.     {
  188.     if((tmp = strrchr(dirspec,':')) == NULL) strset(dirspec,'\0');
  189.     else strset(++tmp,'\0');
  190.     }
  191.     else strset(++tmp,'\0');
  192.  
  193.     /* Now expand the file name and delete all matching items */
  194.     if(find_first(arg, &dta, S_MASK) != 0)
  195.     {
  196.     if(quiet == FALSE) fprintf(stderr,"'%s' NOT FOUND\n",arg);
  197.     }
  198.     else
  199.     {
  200.     rmitem(dirspec, &dta);
  201.     while( find_next(&dta) == 0 )
  202.         rmitem(dirspec, &dta);
  203.     }
  204. }
  205.  
  206. /*
  207.  * This routine takes a DTA for an item and determines how to remove
  208.  * that item from the directory specified by "dirspec".
  209.  */
  210. rmitem(dirspec,dta)
  211. char *dirspec;
  212. struct dosdta *dta;
  213. {
  214.     char filespec[DOSLINE];
  215.  
  216.     /* Don't try to delete the DOS special files "." & ".." */
  217.     if ((strcmp(dta->fname, ".") != 0) && (strcmp(dta->fname, "..") != 0))
  218.     {
  219.     strcpy(filespec, dirspec);  /* Append the name to the path */
  220.     strcat(filespec, dta->fname);
  221.     if (dta->attrib & A_SUBDIR)  /* if it is a directory ... */
  222.         {
  223.         if (recurse)    /* and we want to remove it ... */
  224.         {
  225.         if (inquire)    /* and we want to ask first ... */
  226.             {
  227.             fprintf(stderr,"Remove directory '%s' (y/n)? ",filespec);
  228.             if (yesno()) deldir(filespec);
  229.             }
  230.         else deldir(filespec);
  231.         }
  232.         else
  233.         if(quiet == FALSE)
  234.             fprintf(stderr,"'%s' is a directory\n",filespec);
  235.         }
  236.     else        /* assume it is a normal file */
  237.         {
  238.         if (inquire)
  239.         {
  240.         fprintf(stderr,"Remove file '%s' (y/n)? ",filespec);
  241.         if (yesno()) rmfile(filespec, dta);
  242.         }
  243.         else rmfile(filespec, dta);
  244.         }
  245.     }
  246. }
  247.  
  248. /*
  249.  * This routine attempts to recursively delete a directory.
  250.  */
  251. deldir(arg)
  252. char *arg;    /* The full path name of the directory */
  253. {
  254.     char tempspec[DOSLINE];
  255.  
  256.     if (rmdir(arg) != 0)        /* If not empty ... */
  257.     {
  258.     strcpy(tempspec, arg);        /* Create new search string */
  259.     strcat(tempspec,"\\*.*");
  260.     delete(tempspec);        /* Then recurse into it to empty it */
  261.     if ((rmdir(arg) != 0) && (quiet == FALSE))
  262.         fprintf(stderr,"'%s' NOT REMOVED\n",arg);
  263.     }
  264.  
  265. }
  266.  
  267. /*
  268.  * This routine attempts to remove a file. It checks to see if any of the
  269.  * special file attributes are set and if so, it checks to see if you still
  270.  * want to delete it.
  271.  */
  272. rmfile(arg, dta)
  273. char *arg;        /* The full path name of the file */
  274. struct dosdta *dta;    /* Pointer to the DTA for the file */
  275. {
  276.     int killit, override;
  277.  
  278.     if(quiet)        /* If in quiet mode, kill everything reguardless */
  279.     {
  280.     killit = TRUE;
  281.     override = TRUE;
  282.     }
  283.     else        /* Otherwise check special conditions first */
  284.     {
  285.     killit = TRUE;
  286.     override = FALSE;
  287.     if ((dta->attrib & A_READONLY) != 0)
  288.         {
  289.         fprintf(stderr,"'%s' is marked READONLY, delete anyway? (y/n) ",arg);
  290.         killit = yesno();
  291.         override = killit;
  292.         }
  293.     if ((archive) && ((dta->attrib & A_ARCHIVE) != 0) && (killit))
  294.         {
  295.         fprintf(stderr,"'%s' has not been backed up, delete anyway? (y/n) ",arg);
  296.         killit = yesno();
  297.         }
  298.     if (((dta->attrib & A_SYSTEM) != 0) && (killit))
  299.         {
  300.         fprintf(stderr,"'%s' is a SYSTEM file, delete anyway? (y/n) ",arg);
  301.         killit = yesno();
  302.         }
  303.     if (((dta->attrib & A_HIDDEN) != 0) && (killit))
  304.         {
  305.         fprintf(stderr,"'%s' is a HIDDEN file, delete anyway? (y/n) ",arg);
  306.         killit = yesno();
  307.         }
  308.     }
  309.     if (killit)        /* Do we still want to remove it? */
  310.     {
  311.     if((override) && ((dta->attrib & A_READONLY) != 0))
  312.         chmod(arg,(dta->attrib & !(A_READONLY)));
  313.     if ((remove(arg) != 0) && (quiet == FALSE))
  314.         fprintf(stderr,"'%s' NOT REMOVED\n",arg);
  315.     }
  316. }
  317.  
  318.  
  319. /*
  320.  * Find the first entry in the directory specified by the "path".
  321.  */
  322. find_first(path, dta, mask)
  323. char *path;            /* Path name to start search */
  324. struct dosdta *dta;        /* Location of the DTA to use */
  325. int mask;            /* File search attributes mask */
  326. {
  327.     union REGS ir, or;
  328.  
  329.     ir.x.ax = 0x1A00;        /* DOS function 1A */
  330.     ir.x.dx = (unsigned)dta;
  331.     intdos(&ir, &or);        /* Set the DTA address */
  332.  
  333.     ir.x.ax = 0x4E00;        /* DOS function 4E */
  334.     ir.x.cx = mask;
  335.     ir.x.dx = (unsigned)path;
  336.     intdos(&ir, &or);        /* Do the search & fill the DTA */
  337.  
  338.     return(or.x.ax);        /* Return errors if any */
  339. }
  340.  
  341. /*
  342.  * Find the next file in the directory. Must be used only after Find_first.
  343.  */
  344. find_next(dta)
  345. struct dosdta *dta;            /* Location of the DTA */
  346. {
  347.     union REGS ir, or;
  348.  
  349.     ir.x.ax = 0x1A00;        /* DOS function 1A */
  350.     ir.x.dx = (unsigned)dta;
  351.     intdos(&ir, &or);        /* Set the DTA address */
  352.  
  353.     ir.x.ax = 0x4F00;        /* DOS function 4F */
  354.     intdos(&ir, &or);        /* Do the search & fill the DTA */
  355.  
  356.     return(or.x.ax);        /* Return errors if any */
  357. }
  358.  
  359. /*
  360.  * This routine is used to change file attributes.
  361.  */
  362. chmod(arg, mask)
  363. char *arg;
  364. int mask;
  365. {
  366.     union REGS ir, or;
  367.  
  368.     ir.h.ah = 0x43;            /* DOS function 43 */
  369.     ir.h.al = 0x01;            /* Set (not get) attribute bits */
  370.     ir.x.cx = mask;
  371.     ir.x.dx = (unsigned)arg;
  372.     intdos(&ir, &or);
  373.  
  374.     return(or.x.ax);        /* Return errors if any */
  375. }
  376.  
  377. /* Parse: This function takes an input line and breaks it up into an
  378.  *      array of lines. Normal whitespace chars are used to divide
  379.  *      the line. The number of lines is returned. Zero is returned
  380.  *      if there were no non-whitespace chars or if there was an
  381.  *      unmatched quote situation.
  382.  */
  383. parse(in,v)
  384. char    *in,*v[];
  385. {
  386.     char    last_quote;
  387.     int        c = 0;
  388.     int        newword = TRUE;
  389.     int        literal = FALSE;
  390.     int        quote = FALSE;
  391.  
  392.     while (*in!='\0')
  393.         {
  394.     switch(*in)    /* if its a space and a quote is not open */
  395.         {
  396.         case '\n':    *in='\0';
  397.             newword=TRUE;
  398.             break;
  399.         case '\'':
  400.         case '"':
  401.         if (((last_quote==*in)&&(literal==TRUE)) || (literal==FALSE))
  402.             {
  403.             literal=(literal==FALSE)?TRUE:FALSE;
  404.             last_quote=(literal==TRUE)?*in:'\0';
  405.             *in='\0';
  406.             newword=TRUE;
  407.                 break;
  408.             }
  409.  
  410.         case ' ':
  411.         case '\t':
  412.         {    /* convert all unquoted whitespace into null */
  413.                 if(literal == FALSE)
  414.             {
  415.                 *in='\0';
  416.                 newword=TRUE;
  417.             break;
  418.             }
  419.         }
  420.         default:
  421.         if (newword==TRUE)
  422.             {
  423.                 v[c++]=in;
  424.             newword=FALSE;
  425.             }
  426.             }
  427.         in++;
  428.         }
  429.     if (literal==TRUE)
  430.         {
  431.         printf("mismatched %c.\n",last_quote);
  432.         c=0;
  433.         }
  434.     return(c);
  435. }
  436.  
  437. /*
  438.  * Get a Yes/No response from the terminal and clear the input line.
  439.  */
  440. yesno() 
  441. {
  442.     switch(getchar())
  443.     {
  444.     case 'y' :
  445.     case 'Y' : while(getchar() != '\n');
  446.            return(TRUE);
  447.            break;
  448.     case '\n': return(FALSE);
  449.            break;
  450.     default  : while(getchar() != '\n');
  451.            return(FALSE);
  452.            break;
  453.     }
  454. }
  455.  
  456. /*
  457.  * Print a program usage message, and exit.
  458.  */
  459. usage()
  460. {
  461.     printf("\nUSAGE: rm [-i] [-a] [-f] [-r] file_or_directory_name(s)\n\n");
  462.     printf("   -i    Inquire before delete mode (default no)\n");
  463.     printf("   -a    Inquire before deleting non-archived files\n");
  464.     printf("   -f    Remove without prompting or complaining\n");
  465.     printf("   -r    Recursively delete directory structures\n");
  466.     exit(0);
  467. }
  468.