1/*
2 * Copyright 2004-2006, Jérôme Duval. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "ExpanderRules.h"
8
9#include <stdlib.h>
10#include <stdio.h>
11#include <unistd.h>
12
13#include <FindDirectory.h>
14#include <NodeInfo.h>
15#include <Path.h>
16
17#include <compat/sys/stat.h>
18
19#include "ExpanderSettings.h"
20
21
22static const char* const kRulesDirectoryPath = "expander/rules";
23static const char* const kUserRulesFileName = "rules";
24
25
26// #pragma mark - ExpanderRule
27
28
29ExpanderRule::ExpanderRule(const char* mimeType,
30	const BString& filenameExtension, const BString& listingCommand,
31	const BString& expandCommand)
32	:
33	fMimeType(mimeType),
34	fFilenameExtension(filenameExtension),
35	fListingCmd(listingCommand),
36	fExpandCmd(expandCommand)
37{
38}
39
40
41// #pragma mark - ExpanderRules
42
43
44ExpanderRules::ExpanderRules()
45{
46	// Load the rules files first, then add the built-in rules. This way the
47	// built-ins can be overridden, if the files contain matching rules.
48	_LoadRulesFiles();
49
50	_AddRule("", ".tar.gz", "tar -ztvf %s", "tar -zxf %s");
51	_AddRule("", ".tar.bz2", "tar -jtvf %s", "tar -jxf %s");
52	_AddRule("", ".tar.Z", "tar -Ztvf %s", "tar -Zxf %s");
53	_AddRule("", ".tgz", "tar -ztvf %s", "tar -zxf %s");
54	_AddRule("application/x-tar", ".tar", "tar -tvf %s", "tar -xf %s");
55	_AddRule("application/x-gzip", ".gz", "echo %s | sed 's/.gz$//g'",
56		"gunzip -c %s > `echo %s | sed 's/.gz$//g'`");
57	_AddRule("application/x-bzip2", ".bz2", "echo %s | sed 's/.bz2$//g'",
58		"bunzip2 -k %s");
59	_AddRule("application/zip", ".zip", "unzip -l %s", "unzip -o %s");
60	_AddRule("application/x-zip-compressed", ".zip", "unzip -l %s",
61		"unzip -o %s");
62	_AddRule("application/x-rar", ".rar", "unrar v %s", "unrar x -y %s");
63	_AddRule("application/x-vnd.haiku-package", ".hpkg", "package list %s",
64		"package extract %s");
65}
66
67
68ExpanderRules::~ExpanderRules()
69{
70	void* item;
71	while ((item = fList.RemoveItem((int32)0)))
72		delete (ExpanderRule*)item;
73}
74
75
76ExpanderRule*
77ExpanderRules::MatchingRule(BString& fileName, const char* filetype)
78{
79	int32 count = fList.CountItems();
80	int32 length = fileName.Length();
81	for (int32 i = 0; i < count; i++) {
82		ExpanderRule* rule = (ExpanderRule*)fList.ItemAt(i);
83		if (rule->MimeType().IsValid() && rule->MimeType() == filetype)
84			return rule;
85
86		int32 extensionPosition = fileName.FindLast(rule->FilenameExtension());
87		if (extensionPosition != -1 && extensionPosition
88				== (length - rule->FilenameExtension().Length())) {
89			return rule;
90		}
91	}
92
93	return NULL;
94}
95
96
97ExpanderRule*
98ExpanderRules::MatchingRule(const entry_ref* ref)
99{
100	BEntry entry(ref, true);
101	BNode node(&entry);
102	BNodeInfo nodeInfo(&node);
103	char type[B_MIME_TYPE_LENGTH];
104	nodeInfo.GetType(type);
105	BString fileName(ref->name);
106
107	return MatchingRule(fileName, type);
108}
109
110
111void
112ExpanderRules::_LoadRulesFiles()
113{
114	// load the user editable rules first
115	BPath path;
116	if (ExpanderSettings::GetSettingsDirectoryPath(path) == B_OK
117		&& path.Append(kUserRulesFileName) == B_OK) {
118		_LoadRulesFile(path.Path());
119	}
120
121	// load the rules files from the data directories
122	const directory_which kDirectories[] = {
123		B_USER_NONPACKAGED_DATA_DIRECTORY,
124		B_USER_DATA_DIRECTORY,
125		B_SYSTEM_NONPACKAGED_DATA_DIRECTORY,
126		B_SYSTEM_DATA_DIRECTORY
127	};
128
129	for (size_t i = 0; i < sizeof(kDirectories) / sizeof(kDirectories[0]);
130			i++) {
131		BDirectory directory;
132		if (find_directory(kDirectories[i], &path) != B_OK
133			|| path.Append(kRulesDirectoryPath) != B_OK
134			|| directory.SetTo(path.Path()) != B_OK) {
135			continue;
136		}
137
138		entry_ref entry;
139		while (directory.GetNextRef(&entry) == B_OK) {
140			BPath filePath;
141			if (filePath.SetTo(path.Path(), entry.name) == B_OK)
142				_LoadRulesFile(filePath.Path());
143		}
144	}
145}
146
147
148void
149ExpanderRules::_LoadRulesFile(const char* path)
150{
151	FILE* file = fopen(path, "r");
152	if (file == NULL)
153		return;
154
155	char buffer[1024];
156	BString strings[4];
157	while (fgets(buffer, 1024 - 1, file) != NULL) {
158		int32 i = 0, j = 0;
159		int32 firstQuote = -1;
160		while (buffer[i] != '#' && buffer[i] != '\n' && j < 4) {
161			if ((j == 0 || j > 1) && buffer[i] == '"') {
162				if (firstQuote >= 0) {
163					strings[j++].SetTo(&buffer[firstQuote+1],
164						i - firstQuote - 1);
165					firstQuote = -1;
166				} else
167					firstQuote = i;
168			} else if (j == 1 && (buffer[i] == ' ' || buffer[i] == '\t')) {
169				if (firstQuote >= 0) {
170					if (firstQuote + 1 != i) {
171						strings[j++].SetTo(&buffer[firstQuote+1],
172							i - firstQuote - 1);
173						firstQuote = -1;
174					} else
175						firstQuote = i;
176				} else
177					firstQuote = i;
178			}
179			i++;
180		}
181
182		if (j == 4)
183			_AddRule(strings[0], strings[1], strings[2], strings[3]);
184	}
185
186	fclose(file);
187}
188
189
190bool
191ExpanderRules::_AddRule(const char* mimeType, const BString& filenameExtension,
192	const BString& listingCommand, const BString& expandCommand)
193{
194	ExpanderRule* rule = new(std::nothrow) ExpanderRule(mimeType,
195		filenameExtension, listingCommand, expandCommand);
196	if (rule == NULL)
197		return false;
198
199	if (!fList.AddItem(rule)) {
200		delete rule;
201		return false;
202	}
203
204	return true;
205}
206
207
208// #pragma mark - RuleRefFilter
209
210
211RuleRefFilter::RuleRefFilter(ExpanderRules& rules)
212	:
213	BRefFilter(),
214	fRules(rules)
215{
216}
217
218
219bool
220RuleRefFilter::Filter(const entry_ref* ref, BNode* node, struct stat_beos* stat,
221	const char* filetype)
222{
223	if (node->IsDirectory() || node->IsSymLink())
224		return true;
225
226	BString fileName(ref->name);
227	return fRules.MatchingRule(fileName, filetype) != NULL;
228}
229