"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) <