xref: /haiku/src/bin/trash.cpp (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 	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 
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 
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 
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