"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "trashcan-1.2.2d/trashcan.c" of archive trashcan-1.2.2d.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 #include <stdio.h>
    2 #include <unistd.h>
    3 #include <getopt.h>
    4 #include <string.h>
    5 #include <limits.h>
    6 #include <errno.h>
    7 #include <dirent.h>
    8 #include <pwd.h>
    9 #include <fcntl.h>
   10 #include <stdlib.h>
   11 
   12 #ifdef SHELLSMART
   13 #include <unistd.h>
   14 #endif
   15 
   16 #include <sys/types.h>
   17 #include <sys/param.h>
   18 #include <sys/stat.h>
   19 
   20 #include "version.h"
   21 
   22 #ifdef CLASSIC_RM
   23 #define TITLE   "Safe RM (classic, safties disabled)"
   24 #else
   25 #define TITLE   "Safe RM (safties enabled)"
   26 #endif
   27 
   28 #define COPYRIGHT "Copyright (C) Ahmed Masud 1996"
   29 
   30 #ifdef USES_CURSES
   31 #define OPTIONS_STRING "dfirsvRc"
   32 #else
   33 #define OPTIONS_STRING "dfirsvR"
   34 #endif
   35 
   36 #define PN_RM          "rm"
   37 #define PN_DESTROY     "destroy"
   38 #define PN_RESTORE     "restore"
   39 #define PN_PURGE       "purge"
   40 #define PN_LT          "lt"
   41 
   42 #define FATAL(arg,msg) { if (arg) { \
   43     fprintf (stderr, "%s: %s\nTry `%s --help' %s\n", argv[0], \
   44     msg, argv[0], "for more information"); exit(1); }  }
   45 
   46 /* Following options are identical to the regular rm commands in terms of
   47  * their effect except that if the filename 'rm' it saves the contents first.
   48  */
   49 
   50 const unsigned short opt_directory = 0x0001;
   51 const unsigned short opt_force     = 0x0002;
   52 const unsigned short opt_interact  = 0x0004;
   53 const unsigned short opt_recurse   = 0x0008;
   54 const unsigned short opt_verbose   = 0x0010;
   55 
   56 
   57 #define OPT_HELP       1
   58 #define OPT_VERSION    2
   59 #define OPT_DESTROY    3
   60 #define OPT_RESTORE    4
   61 #define OPT_PURGE      5
   62 #define OPT_OVERWRITE  6
   63 
   64 const unsigned short opt_destroy   = 0x0020;
   65 const unsigned short opt_restore   = 0x0040;
   66 const unsigned short opt_purge     = 0x0080;
   67 const unsigned short opt_remove    = 0x0100;
   68 const unsigned short opt_overwrite = 0x0200;
   69 const unsigned short opt_size      = 0x0400;
   70 const unsigned short opt_showtrash = 0x0800;
   71 
   72 #define OPT_BASEOPTS   0x001F
   73 
   74 #define TRASHCAN_PATHS    "paths"
   75 #define TRASHCAN_FILES    "files"
   76 #define TRASHCAN_TEMPL    "trash.XXXXXX"
   77 
   78 #ifdef MAXPATHLEN
   79 #undef MAXPATHLEN
   80 #endif
   81 
   82 #define MAXPATHLEN 255
   83 
   84 #ifdef USES_CURSES
   85 #define OPT_CURSES     'c'
   86 const unsigned short opt_curses    = 0x1000;
   87 #endif
   88 
   89 #ifdef SHELLSMART
   90 #define OPT_SAFE  'S'
   91 const unsigned short opt_safe = 0x2000;
   92 #endif
   93 
   94 #ifndef ENVTRASHSIZE
   95 #define ENVTRASHSIZE "TC_MAXSIZE"
   96 #endif
   97 
   98 #ifndef ENVSHELLSMART
   99 #define ENVSHELLSMART "TC_SHELLSMART"
  100 #endif
  101 
  102 typedef struct {
  103   int count;
  104   char **exp;
  105 } t_pattern;
  106 
  107 /*
  108  * Global variables
  109  */
  110 static char progname[32];
  111 static unsigned filecount = 0;
  112 
  113 
  114 
  115 int destroyfiles(int argc, char **argv, int options);
  116 int destroyfile(const char *file, int options);
  117 int destroydir(const char *dir, int options);
  118 
  119 int removefiles(int argc, char **argv, int options);
  120 int removefile(const char *file, int options,
  121 	       const char *trashcan_paths, const char *trashcan_files);
  122 int removedir(const char *dir, int options,
  123 	      const char *trashcan_paths, const char *trashcan_files);
  124 
  125 int restorefiles(int options);
  126 int restorefile(const char *file, int options,
  127 		const char *trashcan_paths, const char *trashcan_files);
  128 int restoredir(const char *dir, int mode, int options);
  129 int purgetrashcan(int options, const t_pattern *pattern);
  130 int purgefile(const char *file, int options,
  131 	      const char *trashcan_paths, const char *trashcan_files,
  132 	      const t_pattern *pattern);
  133 int match_pattern(const t_pattern *pattern, const char *string);
  134 
  135 int showtrash(int options);
  136 void showhelp(const char *progname);
  137 void showversion(void);
  138 
  139 #ifdef SHELLSMART
  140 int inside_script(int options);
  141 #endif
  142 
  143 
  144 unsigned long diskusage(const char *file); /* returns of a file or a subdirectory */
  145 
  146 int main(int argc, char **argv, char **argp) {
  147   int c;
  148   int options;
  149   int optidx = 0;
  150 
  151   char *pn;  /* pointer to program name in argv[0]; */
  152   char **files;
  153 
  154   options = 0;
  155 
  156   pn = strrchr(argv[0], '/'); /* where is the last slash in the name? */
  157 
  158   if ( pn )
  159     strcpy(progname, pn+1);
  160   else
  161     strcpy(progname, argv[0]);
  162 
  163 /*
  164  * if (!strcmp(progname, PN_RM)) {  This comment is here for sake of
  165  *   options |= 0;                  being complete.
  166  * }
  167  */
  168 #ifdef CLASSIC_RM
  169 	options |= opt_destroy;
  170 	if (!strcmp(progname, PN_DESTROY)) {
  171 		fprintf(stderr, "Safty features are disabled for %s",
  172 			"trashcan, unable to complete request\n" );
  173 	}
  174 #else
  175   if (!strcmp(progname, PN_DESTROY))
  176     options |= opt_destroy;
  177   else if (!strcmp(progname, PN_RESTORE))
  178     options |= opt_restore;
  179   else if (!strcmp(progname, PN_PURGE))
  180     options |= opt_purge;
  181   else if (!strcmp(progname, PN_LT)) /* lists the contents of the trashcan */
  182     options |= opt_showtrash;
  183   else
  184     options |= opt_remove;
  185 #endif
  186 
  187   for ( ;; ) {
  188 
  189     static struct option long_options[] =
  190       {
  191 	/*
  192 	 * Regular rm options.
  193 	 */
  194 
  195 	  { "directory", 0, 0, 'd' },
  196 	  { "force", 0, 0, 'f' },
  197 	  { "interactive", 0, 0, 'i' },
  198 	  { "recursive", 0, 0, 'r' },
  199 	  { "verbose", 0, 0, 'v' },
  200 	  { "help", 0, 0, OPT_HELP },
  201 	  { "version", 0, 0, OPT_VERSION },
  202 
  203 	/*
  204 	 * new safe-rm specific options
  205 	 */
  206 
  207 	  { "destroy", 0, 0, OPT_DESTROY }, /* destroy the files */
  208 	  { "restore", 0, 0, OPT_RESTORE }, /* restore deleted files */
  209 	  { "purge", 0, 0, OPT_PURGE }, /* purge deleted files */
  210 	  { "overwrite", 0, 0, OPT_OVERWRITE}, /* overwrite while restoring */
  211 	  { "size", 0, 0, 's'}, /* display size of the trashcan */
  212 #ifdef USES_CURSES
  213 	  { "curses", 0, 0, OPT_CURSES },
  214 #endif
  215 #ifdef SHELLSMART
  216 	  { "safe", 0, 0, OPT_SAFE },
  217 #endif
  218 	  { 0, 0, 0, 0 }
  219       };
  220 
  221     c = getopt_long (argc, argv, OPTIONS_STRING, long_options, &optidx);
  222 
  223     if ( c == -1 )
  224       break;
  225 
  226     switch (c) {
  227 
  228     case 'd':
  229       /* 'd' is not available while purging or restore */
  230       FATAL(options & (OPT_PURGE | OPT_RESTORE), "unrecognized option");
  231       options |= opt_directory;
  232       break;
  233 
  234     case 'f':
  235       options |= opt_force;
  236       options &= ~opt_interact;
  237       break;
  238 
  239     case 'i':
  240       options |= opt_interact;
  241       options &= ~opt_force;
  242       break;
  243 
  244     case 'r':
  245     case 'R':
  246       options |= opt_recurse;
  247       break;
  248 
  249     case 's':
  250       FATAL(!(options & opt_showtrash), "unrecognized option");
  251       options |= opt_size;
  252       break;
  253 
  254     case 'v':
  255       options |= opt_verbose;
  256       break;
  257 
  258     case 'o':
  259       options |= opt_overwrite;
  260       break;
  261 
  262     case OPT_HELP:
  263       showhelp(argv[0]);
  264       return 0;
  265       break;
  266 
  267     case '?':
  268     case ':':
  269       if ( options & opt_verbose )
  270 	showhelp(argv[0]);
  271 
  272       return -1;
  273       break;
  274 
  275 #ifdef USES_CURSES
  276     case OPT_CURSES:
  277       FATAL(1, "curses support not available (yet)");
  278       break;
  279 #endif
  280 #ifdef SHELLSMART
  281     case OPT_SAFE:
  282       options |= opt_safe;
  283 #endif
  284     case OPT_VERSION:
  285       showversion();
  286       break;
  287 
  288     case OPT_DESTROY:
  289       FATAL (options & opt_purge, "cannot use purge and destroy simeltaneously");
  290       FATAL (options & opt_restore, "cannot use restore and destroy simeltaneously");
  291 
  292       options &= OPT_BASEOPTS;
  293       options |= opt_destroy;
  294       break;
  295 
  296     case OPT_RESTORE:
  297       FATAL (options & opt_destroy, "cannot use restore and destroy simeltaneously");
  298       FATAL (options & opt_purge, "cannot use restore and purge simeltaneously");
  299       options &= OPT_BASEOPTS;
  300       options |= opt_restore;
  301       break;
  302 
  303     case OPT_PURGE:
  304       FATAL (options & opt_destroy, "cannot use purge and destroy simeltaneously");
  305       FATAL (options & opt_restore, "cannot use restore and purge simeltaneously");
  306 
  307       options &= OPT_BASEOPTS;
  308       options |= opt_purge;
  309       break;
  310 
  311     }
  312   }
  313 
  314   files = &argv[optind];
  315 
  316   if (options & opt_showtrash)
  317     return showtrash(options);
  318 
  319 
  320   if (options & opt_overwrite)
  321     FATAL(!(options & opt_restore), "overwrite can only be used while restoring");
  322 
  323   if ( options & opt_restore ) {
  324     return restorefiles(options | (options & opt_force ? 0: opt_interact));
  325   }
  326 
  327   if ( options & opt_purge ) {
  328     t_pattern pattern;
  329     pattern.exp = files;
  330     pattern.count = argc-optind;
  331 
  332     return
  333       purgetrashcan(options|(options & opt_force ? 0: opt_interact),
  334 		    (pattern.count <= 0) ? NULL : &pattern);
  335   }
  336   if ( optind >= argc ) {
  337     /* If no files were specified exit! */
  338     FATAL(!(options & opt_force), "too few arguments");
  339     exit (0); /* without error if forced */
  340   }
  341 
  342   /* If we are not forcing a delete and asking for multiple files for
  343    * deletion, we better ask for it.
  344    */
  345 
  346   if(((argc - optind) > 1) && !(options & opt_force))
  347     options |= opt_interact;
  348 
  349 
  350 #ifdef SHELLSMART
  351   if ( options & opt_remove ) {
  352     if (inside_script(options)) {
  353        options &= ~opt_remove;
  354        options |= opt_destroy;
  355     }
  356   }
  357 #endif
  358 
  359   if ( options & opt_destroy )
  360     return destroyfiles(argc - optind, files, options);
  361 
  362   if ( options & opt_remove )
  363     return removefiles(argc - optind, files, options);
  364 
  365   return 0;
  366 }
  367 
  368 void showversion(void) {
  369   fprintf( stdout, "%s version %s.%s\n", TITLE, VERSION, MINOR);
  370   exit(0);
  371 }
  372 
  373 
  374 void showhelp(const char *progname) {
  375 
  376   if (!strcmp(progname, PN_DESTROY)) {
  377     printf("Usage: %s [OPTION]... [PATH]...\n\n", progname);
  378     printf("   -d, --directory        unlink directory, even if non-empty (not supported!)\n");
  379     printf("   -f, --force            ignore nonexistent files, never prompt\n");
  380     printf("   -i, --interactive      prompt before any removal\n");
  381     printf("   -v, --verbose          explain what is being done\n");
  382     printf("   -r, -R, --recursive    remove the contents of directories recursively\n");
  383     printf("       --help             display this help and exit\n");
  384     printf("       --version          output version information and exit\n\n");
  385     return ;
  386   }
  387 
  388   if (!strcmp(progname, PN_RESTORE)) {
  389     printf("Usage: %s [OPTIONS]\n\n", progname);
  390     printf("   -f, --force            ignore existing files, never prompt\n");
  391     printf("   -i, --interactive      prompt before any replacement (default)\n");
  392     printf("   -o, --overwrite        overwritting existing files\n");
  393     printf("   -v, --verbose          explain what is being done\n");
  394     printf("       --help             display this help and exit\n");
  395     printf("       --version          output version information and exit\n\n");
  396     return ;
  397   }
  398 
  399   if (!strcmp(progname, PN_PURGE)) {
  400     printf("Usage: %s [OPTIONS]\n\n", progname);
  401     printf("   -f, --force            force trashcan purge\n");
  402     printf("   -i, --interactive      prompt before purging a file or directory (default)\n");
  403     printf("   -v, --verbose          explain what is being done\n");
  404     printf("       --help             display this help and exit\n");
  405     printf("       --version          output version information and exit\n\n");
  406     return ;
  407   }
  408 
  409   if (!strcmp(progname, PN_LT)) {
  410     printf("Usage: %s [OPTIONS]\n\n", progname);
  411     printf("   -s, --size             show size of trashcan\n");
  412     printf("       --help             display this help and exit\n");
  413     printf("       --version          output version information and exit\n\n");
  414     return ;
  415   }
  416 
  417   printf("Usage: %s [OPTION]... [PATH]...\n\n", progname);
  418   printf("   -d, --directory        unlink directory, even if non-empty (not supported!)\n");
  419   printf("   -f, --force            ignore nonexistent files, never prompt\n");
  420   printf("   -i, --interactive      prompt before any removal\n");
  421   printf("   -v, --verbose          explain what is being done\n");
  422   printf("   -r, -R, --recursive    remove the contents of directories recursively\n");
  423 #ifdef SHELLSMART
  424   printf("   -S, --safe             run in safe mode even in shell scripts\n");
  425 #endif
  426   printf("       --help             display this help and exit\n");
  427   printf("       --version          output version information and exit\n\n");
  428   return;
  429 }
  430 
  431 
  432 /*
  433  * following are the remove functions which move the files to appropriate
  434  * trashcan directory.
  435  */
  436 
  437 int removefiles(int argc, char **argv, int options)  {
  438 
  439   int retval = 0, i;
  440   char trashcan[MAXPATHLEN],
  441   trashcan_paths[MAXPATHLEN],
  442   trashcan_files[MAXPATHLEN];
  443   int max_trash_size = DEFTRASHSIZE;
  444 
  445   struct passwd *pwd;
  446   struct stat sb;
  447   char *p = getenv(ENVTRASHSIZE);
  448   if ( p!=NULL ) {
  449       if ( atoi(p) > 0 )
  450 	max_trash_size = atoi(p);
  451   }
  452   pwd = getpwuid(geteuid());
  453 
  454   stat(pwd->pw_dir, &sb);
  455 
  456   if((pwd->pw_uid == 0) || !S_ISDIR(sb.st_mode))
  457     sprintf(trashcan, "%s", SYSTRASH);
  458   else
  459     sprintf(trashcan, "%s/%s", pwd->pw_dir, LOCALTRASH);
  460 
  461   if ( stat(trashcan, &sb) < 0 ) {
  462     if ( errno == ENOENT ) {
  463       if ( mkdir ( trashcan, 0700 ) < 0 ) {
  464 	fprintf(stderr, "%s: unable to create trashcan\n", progname);
  465 	return -1;
  466       }
  467     }
  468     else {
  469       fprintf(stderr, "%s: unknown error accessing trashcan\n", progname);
  470       return -1;
  471     }
  472   }
  473 
  474   sprintf(trashcan_paths, "%s/%s", trashcan, TRASHCAN_PATHS);
  475   sprintf(trashcan_files, "%s/%s", trashcan, TRASHCAN_FILES);
  476 
  477   if ( mkdir(trashcan_paths, 0700) < 0 ) {
  478     if ( errno != EEXIST ) {
  479       perror(progname);
  480       return -1;
  481     }
  482   }
  483 
  484   if ( mkdir(trashcan_files, 0700) < 0 ) {
  485     if ( errno != EEXIST ) {
  486       perror(progname);
  487       return -1;
  488     }
  489   }
  490 
  491   for ( i = 0; i < argc; i++ ) {
  492     if ( argv[i][strlen(argv[i])-1] == '/' )
  493       argv[i][strlen(argv[i])-1] = 0;
  494     retval = removefile(argv[i], options, trashcan_paths, trashcan_files);
  495   }
  496 
  497   if ((diskusage(trashcan_files) + diskusage (argv[i]))
  498       > (max_trash_size * 1024)) {
  499     fprintf(stderr, "Your trashcan is FULL, please purge some contents\n");
  500   }
  501   return retval;
  502 
  503 }
  504 
  505 int removefile(const char *file, int options,
  506 	       const char *trashcan_paths, const char *trashcan_files) {
  507 
  508   int infd, outfd, retval;
  509   char temp[MAXPATHLEN];
  510   char t_path[MAXPATHLEN];
  511   char t_file[MAXPATHLEN];
  512   char buf[8192];
  513   struct stat sb;
  514   char errtxt[MAXPATHLEN];
  515   char *p;
  516 
  517   sprintf(errtxt, "%s: %s", progname, file);
  518 
  519   sprintf(temp, "trash.%d.XXXXXX", ++filecount);
  520 
  521   if ( mktemp(temp) == NULL ) {
  522     if (options & opt_force)
  523       return 0;
  524     fprintf (stderr, "%s: unable to create trashcan entry\n", errtxt);
  525     return -1;
  526   }
  527 
  528   sprintf(t_path, "%s/%s", trashcan_paths, temp);
  529   sprintf(t_file, "%s/%s", trashcan_files, temp);
  530 
  531   if ( lstat (file, &sb) < 0 ) {
  532     if ( options & opt_force )
  533       return 0;
  534     perror(errtxt);
  535     return -1;
  536   }
  537 
  538   if ( S_ISDIR (sb.st_mode) ) {
  539     if ( options & opt_recurse )
  540       return removedir(file, options, trashcan_paths, trashcan_files);
  541     if ( options & opt_force )
  542       return 0;
  543     fprintf (stderr, "%s: %s is a directory\n", progname, file);
  544     return -1;
  545   }
  546 
  547   if ( options & opt_interact ) {
  548     fprintf(stderr, "%s: move `%s' to trashcan? ", progname, file);
  549     fgets(temp, sizeof(temp), stdin);
  550     if (temp[0] != 'y')
  551       return 0;
  552   }
  553 
  554   outfd = open(t_file, O_WRONLY|O_CREAT, 0600);
  555 
  556   if ( outfd < 0 ) {
  557     if (options & opt_force)
  558 	return 0;
  559     perror(errtxt);
  560     return -1;
  561   }
  562 
  563   /* if it's not a link read the contents and save them */
  564 
  565   if ( !S_ISLNK (sb.st_mode) ) {
  566 
  567     infd = open(file, O_RDONLY);
  568 
  569     if ( infd < 0 ) {
  570       if (options & opt_force)
  571 	return 0;
  572       perror(errtxt);
  573       return -1;
  574     }
  575 
  576     while (1) {
  577 
  578       retval = read(infd, buf, sizeof(buf));
  579       if ( retval < 0 ) {
  580 	unlink(t_file);
  581 	if (options & opt_force)
  582 	  return 0;
  583 	perror(errtxt);
  584 	return -1;
  585       }
  586 
  587       if ( !retval )
  588 	break;
  589 
  590       write(outfd, buf, retval);
  591 
  592     }
  593 
  594     close(infd);
  595 
  596   }
  597   else { /* it is a link, so save the contents of the link */
  598     memset(temp, 0, sizeof(temp));
  599     readlink(file, temp, sizeof(temp));
  600     write(outfd, temp, strlen(temp));
  601   }
  602 
  603   close(outfd);
  604 
  605   outfd = open(t_path, O_WRONLY | O_CREAT, 0600);
  606 
  607   if (outfd < 0) {
  608     unlink(t_file);
  609     if ( options & opt_force )
  610       return 0;
  611     perror(errtxt);
  612     return -1;
  613   }
  614 
  615   /**
  616    * to take care of  symbolic links, we must retreive the directory name
  617    * from the symbolic link, call realpath on the directory link and then
  618    * concat the name again, this will make sure that the full path of the
  619    * symbolic link is stored and not whatever the link was pointing to.
  620    */
  621 
  622   memset(temp, 0, sizeof(temp));
  623 
  624   p = strrchr(file, '/');
  625 
  626   if ( p!=NULL ) {
  627     memset (buf, 0, sizeof(buf));
  628     strncpy(buf, file, p-file);
  629     realpath(buf, temp);
  630     strcat(temp, p);
  631   }
  632   else {
  633     realpath("./", temp);
  634     strcat(temp, "/");
  635     strcat(temp, file);
  636 
  637   }
  638 
  639   write(outfd, temp, strlen(temp));
  640   sprintf(temp, " %u\n", sb.st_mode);
  641   write(outfd, temp, strlen(temp));
  642   close(outfd);
  643 
  644   if ( options & opt_verbose )
  645     fprintf(stderr, "%s: removing %s\n", progname, file);
  646 
  647   destroyfile(file, (options | opt_force) &  ~(opt_verbose | opt_interact));
  648   return 0;
  649 
  650 }
  651 
  652 int removedir(const char *file, int options,
  653 	      const char *trash_paths, const char *trash_files)  {
  654 
  655   int outfd;
  656   char temp[MAXPATHLEN];
  657   char t_path[MAXPATHLEN];
  658   char errtxt[MAXPATHLEN];
  659   struct stat sb;
  660   struct dirent *d_ent;
  661   DIR *dir;
  662 
  663   if ( !strcmp (file, ".") || !strcmp (file, ".."))
  664     return 0;
  665 
  666   sprintf(errtxt, "%s: %s", progname, file);
  667   sprintf(temp, "trash.%02d.XXXXXX", ++filecount);
  668 
  669   if ( mktemp(temp) == NULL ) {
  670     if (options & opt_force)
  671       return 0;
  672     fprintf (stderr, "%s: unable to create trashcan entry\n", errtxt);
  673     return -1;
  674   }
  675 
  676   sprintf(t_path, "%s/%s", trash_paths, temp);
  677 
  678   outfd  = open(t_path, O_WRONLY | O_CREAT, 0700);
  679 
  680   if ( outfd < 0 ) {
  681     if ( options & opt_force )
  682       return 0;
  683     perror(errtxt);
  684     return -1;
  685   }
  686 
  687   realpath(file, temp);
  688   write (outfd, temp, strlen (temp));
  689   lstat(file, &sb);
  690   sprintf(temp, " %u\n", sb.st_mode);
  691   write(outfd, temp, strlen(temp));
  692   close(outfd);
  693 
  694   if ( options & opt_interact ) {
  695     fprintf(stderr, "%s: descend directory `%s'? ", progname, file);
  696     fgets(temp, sizeof(temp), stdin);
  697     if (temp[0] != 'y')
  698       return 0;
  699   }
  700 
  701   dir = opendir(file);
  702   do {
  703     d_ent = readdir(dir);
  704     if(d_ent == NULL)
  705       break;
  706     if(!strcmp(d_ent->d_name, ".") || !strcmp(d_ent->d_name, ".."))
  707       continue;
  708     /* temp now will contain the dir/file */
  709     sprintf(temp, "%s/%s", file, d_ent->d_name);
  710     removefile(temp, options, trash_paths, trash_files);
  711 
  712   } while(d_ent);
  713 
  714   if ( options & opt_interact ) {
  715     fprintf(stderr, "%s: move `%s' to trashcan? ", progname, file);
  716     fgets(temp, sizeof(temp), stdin);
  717     if (temp[0] != 'y')
  718       return 0;
  719   }
  720 
  721   destroydir(file, (options | opt_force) & ~(opt_verbose | opt_interact));
  722   if ( options & opt_verbose )
  723     fprintf (stderr, "%s removed\n", file);
  724 
  725   return 0;
  726 }
  727 
  728 /* Restore removed files */
  729 
  730 int restorefiles(int options) {
  731 
  732   struct stat sb;
  733   char trashcan[MAXPATHLEN],
  734   trashcan_paths[MAXPATHLEN],
  735   trashcan_files[MAXPATHLEN];
  736   struct passwd *pwd;
  737   struct dirent *d_ent;
  738   DIR *dir;
  739 
  740   pwd = getpwuid(geteuid());
  741 
  742   stat(pwd->pw_dir, &sb);
  743 
  744   if((pwd->pw_uid == 0) || !S_ISDIR(sb.st_mode))
  745     sprintf(trashcan, "%s", SYSTRASH);
  746   else
  747     sprintf(trashcan, "%s/%s", pwd->pw_dir, LOCALTRASH);
  748 
  749   sprintf(trashcan_paths, "%s/%s", trashcan, TRASHCAN_PATHS);
  750   sprintf(trashcan_files, "%s/%s", trashcan, TRASHCAN_FILES);
  751 
  752   dir = opendir ( trashcan_paths );  /* Open the trashcan. */
  753 
  754   do {
  755     d_ent = readdir(dir);
  756     if (d_ent == NULL)
  757       break;
  758     if (!strcmp (d_ent->d_name, ".") || !strcmp (d_ent->d_name, ".."))
  759       continue;
  760     restorefile(d_ent->d_name, options, trashcan_paths, trashcan_files);
  761   } while (d_ent != NULL);
  762 
  763   return 0;
  764 }
  765 
  766 int restorefile(const char *file, int options,
  767 		const char *trashcan_paths, const char *trashcan_files) {
  768 
  769   int fdin, fdout;
  770   FILE *fp;
  771   char buf[8192],
  772   trash_file[MAXPATHLEN],
  773   trash_path[MAXPATHLEN],
  774   filename[MAXPATHLEN],
  775   dirname[MAXPATHLEN], *p;
  776   char errtxt[MAXPATHLEN];
  777   struct stat sb;
  778   int retval = 0;
  779   unsigned filemode;
  780 
  781   sprintf(errtxt, "%s: %s", progname, file);
  782 
  783   sprintf(trash_path, "%s/%s", trashcan_paths, file);
  784   sprintf(trash_file, "%s/%s", trashcan_files, file);
  785 
  786   fp = fopen(trash_path, "r");
  787   fscanf(fp, "%s %u\n", filename, &filemode);
  788   fclose(fp);
  789 
  790   if ( S_ISDIR(filemode) ) {
  791     retval = restoredir(filename, filemode, options);
  792     if (retval == 0)
  793       destroyfile(trash_path, opt_force);
  794     return retval;
  795   }
  796 
  797   p = strrchr(filename, '/');
  798   if ( p != filename ) { /* if it is not the first slash */
  799     strncpy(dirname, filename, p-filename);
  800     if (restoredir(dirname, 0755, options & (~opt_interact | opt_force)) < 0)
  801       return -1;
  802   }
  803 
  804   fdin = open (trash_file, O_RDONLY);
  805 
  806   if ((retval = stat (filename, &sb)) == 0) {
  807     if (!(options & opt_overwrite)) { /* Skip existing file; */
  808       if (options & opt_force) {
  809 	if (options & opt_verbose)
  810 	  fprintf(stderr, "%s: skipping file `%s'\n",
  811 		  progname, filename);
  812 	close(fdin);
  813 	return 0;
  814       }
  815     }
  816 
  817     if (options & opt_interact) {
  818       fprintf(stderr, "%s: overwrite existing file `%s' ? ",
  819 	      progname, filename);
  820     }
  821 
  822   }
  823   else {
  824     if ( options & opt_interact ) {
  825       fprintf(stderr, "%s: restore `%s' ? ",
  826 	      progname, filename);
  827     }
  828   }
  829 
  830   if ( options & opt_interact ) {
  831     fgets(buf, sizeof(buf), stdin);
  832     if(buf[0] != 'y') {
  833       if (options & opt_verbose)
  834 	fprintf(stderr, "%s: skipping file `%s'\n",
  835 		progname, filename);
  836       return -1;
  837     }
  838   }
  839 
  840   if ( options & opt_verbose ) {
  841     fprintf(stderr, (retval == 0) ?
  842 	    "%s: replacing existing `%s' with trashcan copy\n" :
  843 	    "%s: restoring %s\n",
  844 	    progname, filename);
  845   }
  846 
  847   // ensure we are the only one who can write to the file with 0200
  848   if ( !S_ISLNK (filemode) ) {
  849     fdout = open (filename, O_WRONLY | O_CREAT, 0200);
  850     do {
  851       retval = read(fdin, buf, sizeof(buf));
  852       if (retval == 0) /* eof */
  853 	break;
  854       write (fdout, buf, retval);
  855 
  856     } while(1);
  857     close(fdout);
  858   }
  859   else {
  860     retval = read(fdin, buf, sizeof(buf));
  861     if ( options & opt_verbose )
  862       fprintf (stderr, "restoring %s as a symbolic link to %s\n", filename, buf);
  863     if ( symlink(buf, filename) < 0) {
  864       perror(errtxt);
  865       return -1;
  866     }
  867   }
  868   // restore the file to its correct mode
  869   chmod(filename, filemode);
  870   close(fdin);
  871 
  872   destroyfile(trash_path, opt_force);
  873   destroyfile(trash_file, opt_force);
  874 
  875   return 0;
  876 }
  877 
  878 int restoredir(const char *file, int mode, int options) {
  879 
  880   struct stat sb;
  881   char temp[MAXPATHLEN];
  882   char *p1, *p2;
  883   char errtxt[32 + MAXPATHLEN];
  884   char buf[80];
  885   sprintf(errtxt, "%s: mkdir ", progname);
  886 
  887   p1 = (char *) file;
  888 
  889   if ( options & opt_interact ) {
  890     fprintf(stderr, "%s: restore directory `%s'?",
  891 	    progname, file);
  892     fgets(buf, sizeof(buf), stdin);
  893     if(buf[0] != 'y') {
  894       if (options & opt_verbose)
  895 	fprintf(stderr, "%s: skipping directory `%s'\n",
  896 		progname, file);
  897       return -1;
  898     }
  899   }
  900 
  901   do {
  902 
  903     int done = 0;
  904     p2 = strchr(p1+1, '/'); /* +1 skips leading slash */
  905 
  906     if (p2 == NULL) {
  907       p2 = p1+strlen(p1);
  908       done = 1;
  909     }
  910 
  911     memset(temp, 0, sizeof(temp));
  912     strncpy ( temp, file, p2-file );
  913 
  914     if ( stat(temp, &sb) < 0 ) {
  915       if ( errno == ENOENT ) { /* file does not exist */
  916 	if ( options & opt_verbose )
  917 	  fprintf (stderr, "%s: restoring %s\n", progname, temp);
  918 	if ( mkdir ( temp, 0700 ) < 0 ) {
  919 	  if ( options & opt_force )
  920 	    return -1;
  921 	  perror (errtxt);
  922 	  return -1; /* Unable to create a parent directory */
  923 	}
  924 	/* make directory was successful so do nothing and just go on */
  925       }
  926       else {
  927 	if ( options & opt_force )
  928 	  return -1;
  929 	perror(errtxt);
  930 	return -1;
  931       }
  932     }
  933 
  934     if ( done )
  935       break;
  936 
  937     p1 = p2; /* we are on to the next portion */
  938 
  939   } while (p2 != NULL);
  940 
  941   return 0;
  942 }
  943 
  944 /* purge the trashcan */
  945 
  946 int purgetrashcan(int options, const t_pattern *pattern) {
  947 
  948   struct stat sb;
  949   char trashcan[MAXPATHLEN],
  950   trashcan_paths[MAXPATHLEN],
  951   trashcan_files[MAXPATHLEN];
  952   struct passwd *pwd;
  953   struct dirent *d_ent;
  954   DIR *dir;
  955 
  956   pwd = getpwuid(geteuid());
  957 
  958   stat(pwd->pw_dir, &sb);
  959 
  960   if((pwd->pw_uid == 0) || !S_ISDIR(sb.st_mode))
  961     sprintf(trashcan, "%s", SYSTRASH);
  962   else
  963     sprintf(trashcan, "%s/%s", pwd->pw_dir, LOCALTRASH);
  964 
  965   sprintf(trashcan_paths, "%s/%s", trashcan, TRASHCAN_PATHS);
  966   sprintf(trashcan_files, "%s/%s", trashcan, TRASHCAN_FILES);
  967 
  968   dir = opendir ( trashcan_paths );  /* Open the trashcan. */
  969 
  970   do {
  971     d_ent = readdir(dir);
  972     if (d_ent == NULL)
  973       break;
  974     if (!strcmp (d_ent->d_name, ".") || !strcmp (d_ent->d_name, ".."))
  975       continue;
  976     purgefile(d_ent->d_name, options, trashcan_paths, trashcan_files, pattern);
  977   } while (d_ent != NULL);
  978 
  979   return 0;
  980 }
  981 
  982 int purgefile(const char *file, int options,
  983 	      const char *trashcan_paths, const char *trashcan_files,
  984 	      const t_pattern *pattern) {
  985 
  986   char trash_path[MAXPATHLEN];
  987   char trash_file[MAXPATHLEN];
  988   char filename[MAXPATHLEN];
  989 
  990   FILE *fp;
  991   unsigned mode;
  992 
  993   sprintf(trash_path, "%s/%s", trashcan_paths, file);
  994   sprintf(trash_file, "%s/%s", trashcan_files, file);
  995 
  996   fp = fopen(trash_path, "r");
  997   fscanf (fp, "%s %u", filename, &mode);
  998   fclose(fp);
  999 
 1000   if (match_pattern(pattern, filename)) { /* Filename doesn't match */
 1001     return 0;
 1002   }
 1003 
 1004   if ( options & opt_interact ) {
 1005     char buf[32];
 1006     fprintf(stderr, "%s: remove `%s' from trashcan? ",
 1007 	    progname, filename);
 1008     fgets(buf, sizeof(buf), stdin);
 1009     if ( buf[0] != 'y' ) {
 1010       if ( options & opt_verbose )
 1011 	fprintf(stderr, "%s: skipping `%s'\n", progname, filename);
 1012       return -1;
 1013     }
 1014   }
 1015 
 1016   if ( options & opt_verbose )
 1017     fprintf(stderr, "%s: purging `%s'\n", progname, filename);
 1018 
 1019   unlink(trash_path);
 1020   unlink(trash_file);
 1021 
 1022   return 0;
 1023 
 1024 }
 1025 
 1026 int match_pattern(const t_pattern *pattern, const char *string) {
 1027   /*  int i;
 1028       char pat_str[256];    pattern string for sscanf ;
 1029       */
 1030 
 1031   return 0; /* Success! */
 1032 }
 1033 
 1034 int showtrash(int options) {
 1035 
 1036   struct stat sb;
 1037   char trashcan[MAXPATHLEN],
 1038   trashcan_paths[MAXPATHLEN],
 1039   trashcan_files[MAXPATHLEN];
 1040   struct passwd *pwd;
 1041   struct dirent *d_ent;
 1042   DIR *dir;
 1043   int max_trash_size = DEFTRASHSIZE;
 1044   char *p = getenv(ENVTRASHSIZE);
 1045   if ( p!=NULL ) {
 1046 	if ( atoi(p) > 0 )
 1047 		max_trash_size = atoi(p);
 1048   }
 1049   pwd = getpwuid(geteuid());
 1050 
 1051   stat(pwd->pw_dir, &sb);
 1052 
 1053   if((pwd->pw_uid == 0) || !S_ISDIR(sb.st_mode))
 1054     sprintf(trashcan, "%s", SYSTRASH);
 1055   else
 1056     sprintf(trashcan, "%s/%s", pwd->pw_dir, LOCALTRASH);
 1057 
 1058   sprintf(trashcan_paths, "%s/%s", trashcan, TRASHCAN_PATHS);
 1059   sprintf(trashcan_files, "%s/%s", trashcan, TRASHCAN_FILES);
 1060 
 1061   if ( options & opt_size ) {
 1062     unsigned long ts;
 1063     ts = diskusage(trashcan_files)/1024;
 1064     fprintf (stdout, "%lu %u %3.2f%%\n", ts,
 1065 	     max_trash_size, (double)((double)ts / (double)max_trash_size * 100.0));
 1066     return 0;
 1067   }
 1068 
 1069   dir = opendir ( trashcan_paths );  /* Open the trashcan. */
 1070 
 1071   do {
 1072     int mode;
 1073     FILE *fp;
 1074     char filename[MAXPATHLEN];
 1075     char trash_path[MAXPATHLEN];
 1076 
 1077     d_ent = readdir(dir);
 1078 
 1079     if (d_ent == NULL)
 1080       break;
 1081 
 1082     if (!strcmp (d_ent->d_name, ".") || !strcmp (d_ent->d_name, ".."))
 1083       continue;
 1084 
 1085     sprintf(trash_path, "%s/%s", trashcan_paths, d_ent->d_name);
 1086 
 1087     fp = fopen(trash_path, "r");
 1088     memset(filename, 0, sizeof(filename));
 1089     fscanf(fp, "%s %u\n", filename, &mode);
 1090     fclose(fp);
 1091 
 1092     printf("%s\n", filename);
 1093 
 1094   } while (d_ent != NULL);
 1095 
 1096   return 0;
 1097 }
 1098 /* following destroy functions loose the files permanently! */
 1099 
 1100 int destroyfiles(int argc, char **argv, int options) {
 1101   int retval = 0, i;
 1102   for ( i = 0; i < argc; i++ ) {
 1103     if ( argv[i][strlen(argv[i])-1] == '/' )
 1104       argv[i][strlen(argv[i])-1] = 0;
 1105     retval = destroyfile(argv[i], options);
 1106   }
 1107   return retval;
 1108 }
 1109 
 1110 int destroydir(const char *file, int options) {
 1111   DIR *dir;
 1112   struct dirent *d_ent;
 1113   char subfile[MAXPATHLEN];
 1114 
 1115   if ( !strcmp (file, ".") || !strcmp (file, ".."))
 1116     return 0;
 1117 
 1118   if (rmdir (file) < 0 ) {
 1119 
 1120     if(errno != ENOTEMPTY)
 1121       return -1;
 1122 
 1123     if (!(options & opt_force)) {
 1124       options |= opt_interact;
 1125       fprintf(stderr, "%s: descend directory %s? ", progname, file);
 1126       /* we can use subfile as a temporary for now! */
 1127       fgets(subfile, sizeof(subfile), stdin);
 1128       if (subfile[0] != 'y') {
 1129 	return 0;
 1130       }
 1131     }
 1132 
 1133     dir = opendir(file);
 1134 
 1135     if ( dir == NULL )
 1136       return -1;
 1137 
 1138     /* If we are not forcing it is better to ask! */
 1139     if (!(options & opt_force))
 1140       options |= opt_recurse;
 1141 
 1142     do {
 1143       d_ent = readdir(dir);
 1144       if ( d_ent == NULL )
 1145 	break;
 1146       if (!strcmp(d_ent->d_name, ".") || !strcmp(d_ent->d_name, ".."))
 1147 	continue;
 1148       sprintf(subfile, "%s/%s", file, d_ent->d_name);
 1149       destroyfile(subfile, options);
 1150     } while(d_ent != NULL);
 1151 
 1152   closedir(dir);
 1153 
 1154     if (rmdir (file) < 0 )
 1155       if(errno != ENOTEMPTY)
 1156       return -1;
 1157 
 1158   }
 1159 
 1160   if ( options & opt_verbose )
 1161     fprintf ( stderr, "%s\n", file);
 1162 
 1163   return 0;
 1164 }
 1165 
 1166 
 1167 int destroyfile(const char *file, int options) {
 1168   struct stat fs;
 1169   int retval;
 1170   char errstr[32+MAXPATHLEN];
 1171   char yn[16];
 1172 
 1173   retval = lstat(file, &fs);
 1174 
 1175   sprintf(errstr, "%s: %s", progname, file);
 1176 
 1177   if (retval < 0)  {
 1178     if (options & opt_force) {
 1179       return 0;
 1180     }
 1181     perror(errstr);
 1182     return -1;
 1183   }
 1184 
 1185   if (S_ISDIR(fs.st_mode)) {
 1186     if (options & opt_recurse) {
 1187       if ( destroydir(file, options) < 0) {
 1188 	perror(errstr);
 1189 	return -1;
 1190       }
 1191       return 0;
 1192     }
 1193     fprintf(stderr, "%s: is a directory\n", errstr);
 1194     return -1;
 1195   }
 1196   else  {
 1197     if ( options & opt_interact ) {
 1198       fprintf(stderr, "%s: remove %s ? ", progname, file);
 1199       fgets(yn, sizeof(yn), stdin);
 1200       if ( yn[0] != 'y' )
 1201 	return 0;
 1202     }
 1203 
 1204     if ( unlink (file) < 0 ) {
 1205       if ( options & opt_force )
 1206 	return 0;
 1207       perror(errstr);
 1208       return -1;
 1209     }
 1210   }
 1211   if ( options & opt_verbose )
 1212     fprintf ( stderr, "%s\n", file);
 1213 
 1214   return 0;
 1215 }
 1216 
 1217 /* Miscellaneous */
 1218 
 1219 unsigned long diskusage(const char *file) {
 1220   unsigned long total = 0;
 1221   struct stat sb;
 1222 
 1223   if ( stat(file, &sb) < 0 ) {
 1224     return 0;
 1225   }
 1226 
 1227   if (S_ISDIR(sb.st_mode)) {
 1228     DIR *dir;
 1229     struct dirent *d_ent;
 1230 
 1231     dir = opendir(file);
 1232     do {
 1233       char tmp[MAXPATHLEN];
 1234       d_ent = readdir ( dir );
 1235       if ( !d_ent )
 1236 	break;
 1237       if ( !strcmp (d_ent->d_name, ".") || !strcmp (d_ent->d_name, ".."))
 1238 	continue;
 1239       sprintf(tmp, "%s/%s", file, d_ent->d_name);
 1240       total += diskusage(tmp);
 1241     } while (d_ent);
 1242 
 1243     closedir(dir);
 1244   }
 1245   else {
 1246     return sb.st_size;
 1247   }
 1248   return total;
 1249 }
 1250 
 1251 #ifdef SHELLSMART
 1252 int inside_script(int options) {
 1253   int i = getppid();
 1254   int j = getpgid(i);
 1255   int k = getpgid(getpid());
 1256   char *p = getenv (ENVSHELLSMART);
 1257 
 1258   if ( options & opt_safe ) /* safe mode */
 1259 	return 0;
 1260   if ( p != NULL && (strcasecmp(p, "false") == 0)) {
 1261 	return 0;
 1262   }
 1263 
 1264   if (options & opt_verbose)
 1265 	  fprintf(stderr, "A shell was%sdetected, destroying files.\n",
 1266 		((i==j)&&(j==k))? " ": " not ");
 1267   return (i==j) && (j==k);  /* can only happen in a shell script */
 1268 }
 1269 #endif