home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / dirutl / mds_rm.arc / RM.C < prev    next >
C/C++ Source or Header  |  1988-08-30  |  14KB  |  492 lines

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