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