1 /*
2 * [un]trash command for Haiku
3 * Copyright (c) 2004, Francois Revol - revol@free.fr
4 * provided under the MIT licence
5 */
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <app/Message.h>
11 #include <app/Messenger.h>
12 #include <kernel/fs_attr.h>
13 #include <kernel/fs_info.h>
14 #include <storage/Directory.h>
15 #include <storage/Entry.h>
16 #include <storage/FindDirectory.h>
17 #include <storage/Node.h>
18 #include <storage/Path.h>
19 #include <support/TypeConstants.h>
20
21 static const char *kAttrOriginalPath = "_trk/original_path";
22 static const char *kTrackerSig = "application/x-vnd.Be-TRAK";
23
usage(int ret)24 int usage(int ret)
25 {
26 printf("\nSend files to trash, or restore them.\nUsage:\n");
27 printf("trash [--restore|--empty|--list] file ...\n");
28 printf("\t--restore\trestore files (act as untrash)\n");
29 printf("\t--empty\t\tempty the Trash\n");
30 printf("\t--list\t\tlist what's already in the Trash\n");
31 printf("untrash [--all] [file ...]\n");
32 //printf("restore [--all] [file ...]\n");
33 return ret;
34 }
35
untrash(const char * f)36 status_t untrash(const char *f)
37 {
38 status_t err;
39 char original_path[B_PATH_NAME_LENGTH];
40 BPath path(f);
41 BNode node(f);
42 err = node.InitCheck();
43 if (err)
44 return err;
45 err = node.ReadAttr(kAttrOriginalPath, B_STRING_TYPE, 0LL, original_path, B_PATH_NAME_LENGTH);
46 if (err < 0)
47 return err;
48 err = rename(path.Path(), original_path);
49 if (err < 0)
50 return err;
51 node.RemoveAttr(kAttrOriginalPath);
52 return 0;
53 }
54
trash(const char * f)55 status_t trash(const char *f)
56 {
57 status_t err;
58 attr_info ai;
59 dev_t dev = -1;
60 int nr;
61 const char *original_path;
62 char trash_dir[B_PATH_NAME_LENGTH];
63 char trashed_file[B_PATH_NAME_LENGTH];
64 dev = dev_for_path(f);
65 err = find_directory(B_TRASH_DIRECTORY, dev, false, trash_dir, B_PATH_NAME_LENGTH);
66 if (err < 0)
67 return err;
68 BNode node(f);
69 err = node.InitCheck();
70 if (err < 0)
71 return err;
72 err = node.GetAttrInfo(kAttrOriginalPath, &ai);
73 if (err == B_OK)
74 return EALREADY;
75 if (!strncmp(f, trash_dir, strlen(trash_dir)))
76 return EALREADY;
77 entry_ref er;
78 err = get_ref_for_path(f, &er);
79 if (err < 0)
80 return err;
81 BPath orgPath(&er);
82 err = orgPath.InitCheck();
83 if (err < 0)
84 return err;
85 original_path = orgPath.Path();
86 BDirectory trashDir(trash_dir);
87 err = trashDir.InitCheck();
88 if (err < 0)
89 return err;
90 for (nr = 0; ; nr++) {
91 if (nr > INT_MAX - 1)
92 return B_ERROR;
93 if (nr)
94 snprintf(trashed_file, B_PATH_NAME_LENGTH-1, "%s/%s %d", trash_dir, er.name, nr);
95 else
96 snprintf(trashed_file, B_PATH_NAME_LENGTH-1, "%s/%s", trash_dir, er.name);
97 if (!trashDir.Contains(trashed_file))
98 break;
99 }
100 err = rename(original_path, trashed_file);
101 if (err < 0)
102 return err;
103
104 err = node.WriteAttr(kAttrOriginalPath, B_STRING_TYPE, 0LL, original_path, strlen(original_path)+1);
105 if (err < 0)
106 return err;
107 return 0;
108 }
109
show_trashed_file(const char * f)110 status_t show_trashed_file(const char *f)
111 {
112 status_t err;
113 char original_path[B_PATH_NAME_LENGTH];
114 BPath path(f);
115 BNode node(f);
116 err = node.ReadAttr(kAttrOriginalPath, B_STRING_TYPE, 0LL, original_path, B_PATH_NAME_LENGTH);
117 if (err < 0)
118 return 0;
119 //printf("%s\n\t[from] %s\n", f, original_path);
120 printf("%s\n\tas: %s\n", original_path, f);
121 return 0;
122 }
123
foreach_in_trash(status_t (* iterator)(const char *))124 status_t foreach_in_trash(status_t (*iterator)(const char *))
125 {
126 status_t err;
127 dev_t dev;
128 char trash_dir[B_PATH_NAME_LENGTH];
129 for (dev = 0; ; ) {
130 if (next_dev(&dev) < B_OK)
131 break;
132 //for each in trash_dir
133 err = find_directory(B_TRASH_DIRECTORY, dev, false, trash_dir, B_PATH_NAME_LENGTH);
134 if (err)
135 continue; /* skip trashless volumes */
136 BDirectory trashDir(trash_dir);
137 err = trashDir.InitCheck();
138 if (err < 0)
139 return err;
140 entry_ref er;
141 while (trashDir.GetNextRef(&er) == B_OK) {
142 BPath path(&er);
143 if ((err = path.InitCheck()))
144 return err;
145 err = iterator(path.Path());
146 if (err)
147 return err;
148 }
149 }
150 return B_OK;
151 }
152
153
main(int argc,char ** argv)154 int main(int argc, char **argv)
155 {
156 int dountrash = 0;
157 int i = 1;
158 int err = 0;
159 if (strstr(argv[0], "untrash") || strstr(argv[0], "restore"))
160 dountrash = 1;
161 if (argc < 2)
162 return usage(1);
163 if (!strcmp(argv[1], "--help"))
164 return usage(0);
165 if (!strcmp(argv[1], "--restore")) {
166 dountrash = 1;
167 i++;
168 }
169 if (!dountrash && !strcmp(argv[1], "--empty")) {
170 /* XXX: clean that */
171 BMessage msg(B_DELETE_PROPERTY);
172 msg.AddSpecifier("Trash");
173 BMessenger msgr(kTrackerSig);
174 err = msgr.SendMessage(&msg);
175 if (err < 0) {
176 fprintf(stderr, "Emptying Trash: %s\n", strerror(err));
177 return 1;
178 }
179 return 0;
180 }
181 if (dountrash && !strcmp(argv[i], "--all")) {
182 /* restore all trashed files */
183 err = foreach_in_trash(untrash);
184 if (err) {
185 fprintf(stderr, "untrash: %s\n", strerror(err));
186 return 1;
187 }
188 return 0;
189 }
190 if (!strcmp(argv[i], "--list")) {
191 err = foreach_in_trash(show_trashed_file);
192 return 0;
193 }
194 /* restore files... */
195 if (dountrash) {
196 for (; i < argc; i++) {
197 err = untrash(argv[i]);
198 if (err) {
199 fprintf(stderr, "%s: %s\n", argv[i], strerror(err));
200 return 1;
201 }
202 }
203 return err;
204 }
205 /* or trash them */
206 for (i = 1; i < argc; i++) {
207 err = trash(argv[i]);
208 if (err) {
209 fprintf(stderr, "%s: %s\n", argv[i], strerror(err));
210 return 1;
211 }
212 }
213
214 return err;
215
216 }
217