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
21static const char *kAttrOriginalPath = "_trk/original_path";
22static const char *kTrackerSig = "application/x-vnd.Be-TRAK";
23
24int 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
36status_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
55status_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
110status_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
124status_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
154int 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