xref: /haiku/src/bin/trash.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
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 
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 
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 
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 	BPath orgPath(&er);
80 	err = orgPath.InitCheck();
81 	if (err < 0)
82 		return err;
83 	original_path = orgPath.Path();
84 	BDirectory trashDir(trash_dir);
85 	err = trashDir.InitCheck();
86 	if (err < 0)
87 		return err;
88 	for (nr = 0; ; nr++) {
89 		if (nr > INT_MAX - 1)
90 			return B_ERROR;
91 		if (nr)
92 			snprintf(trashed_file, B_PATH_NAME_LENGTH-1, "%s/%s %d", trash_dir, er.name, nr);
93 		else
94 			snprintf(trashed_file, B_PATH_NAME_LENGTH-1, "%s/%s", trash_dir, er.name);
95 		if (!trashDir.Contains(trashed_file))
96 			break;
97 	}
98 	err = rename(original_path, trashed_file);
99 	if (err < 0)
100 		return err;
101 
102 	err = node.WriteAttr(kAttrOriginalPath, B_STRING_TYPE, 0LL, original_path, strlen(original_path)+1);
103 	if (err < 0)
104 		return err;
105 	return 0;
106 }
107 
108 status_t show_trashed_file(const char *f)
109 {
110 	status_t err;
111 	char original_path[B_PATH_NAME_LENGTH];
112 	BPath path(f);
113 	BNode node(f);
114 	err = node.ReadAttr(kAttrOriginalPath, B_STRING_TYPE, 0LL, original_path, B_PATH_NAME_LENGTH);
115 	if (err < 0)
116 		return 0;
117 	//printf("%s\n\t[from] %s\n", f, original_path);
118 	printf("%s\n\tas: %s\n", original_path, f);
119 	return 0;
120 }
121 
122 status_t foreach_in_trash(status_t (*iterator)(const char *))
123 {
124 	status_t err;
125 	dev_t dev;
126 	char trash_dir[B_PATH_NAME_LENGTH];
127 	for (dev = 0; ; ) {
128 		if (next_dev(&dev) < B_OK)
129 			break;
130 		//for each in trash_dir
131 		err = find_directory(B_TRASH_DIRECTORY, dev, false, trash_dir, B_PATH_NAME_LENGTH);
132 		if (err)
133 			continue; /* skip trashless volumes */
134 		BDirectory trashDir(trash_dir);
135 		err = trashDir.InitCheck();
136 		if (err < 0)
137 			return err;
138 		entry_ref er;
139 		while (trashDir.GetNextRef(&er) == B_OK) {
140 			BPath path(&er);
141 			if ((err = path.InitCheck()))
142 				return err;
143 			err = iterator(path.Path());
144 			if (err)
145 				return err;
146 		}
147 	}
148 	return B_OK;
149 }
150 
151 
152 int main(int argc, char **argv)
153 {
154 	int dountrash = 0;
155 	int i = 1;
156 	int err = 0;
157 	if (strstr(argv[0], "untrash") || strstr(argv[0], "restore"))
158 		dountrash = 1;
159 	if (argc < 2)
160 		return usage(1);
161 	if (!strcmp(argv[1], "--help"))
162 		return usage(0);
163 	if (!strcmp(argv[1], "--restore")) {
164 		dountrash = 1;
165 		i++;
166 	}
167 	if (!dountrash && !strcmp(argv[1], "--empty")) {
168 		/* XXX: clean that */
169 		BMessage msg(B_DELETE_PROPERTY);
170 		msg.AddSpecifier("Trash");
171 		BMessenger msgr(kTrackerSig);
172 		err = msgr.SendMessage(&msg);
173 		if (err < 0) {
174 			fprintf(stderr, "Emptying Trash: %s\n", strerror(err));
175 			return 1;
176 		}
177 		return 0;
178 	}
179 	if (dountrash && !strcmp(argv[i], "--all")) {
180 		/* restore all trashed files */
181 		err = foreach_in_trash(untrash);
182 		if (err) {
183 			fprintf(stderr, "untrash: %s\n", strerror(err));
184 			return 1;
185 		}
186 		return 0;
187 	}
188 	if (!strcmp(argv[i], "--list")) {
189 		err = foreach_in_trash(show_trashed_file);
190 		return 0;
191 	}
192 	/* restore files... */
193 	if (dountrash) {
194 		for (; i < argc; i++) {
195 			err = untrash(argv[i]);
196 			if (err) {
197 				fprintf(stderr, "%s: %s\n", argv[i], strerror(err));
198 				return 1;
199 			}
200 		}
201 		return err;
202 	}
203 	/* or trash them */
204 	for (i = 1; i < argc; i++) {
205 		err = trash(argv[i]);
206 		if (err) {
207 			fprintf(stderr, "%s: %s\n", argv[i], strerror(err));
208 			return 1;
209 		}
210 	}
211 
212 	return err;
213 
214 }
215