1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <getopt.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include <package/PackageResolvableExpression.h>
13#include <package/manager/Exceptions.h>
14#include <Path.h>
15#include <PathFinder.h>
16#include <StringList.h>
17
18
19using namespace BPackageKit::BManager::BPrivate;
20
21
22extern const char* __progname;
23const char* kCommandName = __progname;
24
25
26struct DirectoryConstantEntry {
27	const char*			string;
28	path_base_directory	constant;
29	const char*			description;
30};
31
32#define DEFINE_CONSTANT(constant, description)	\
33	{ #constant, constant, description }
34
35static const DirectoryConstantEntry kDirectoryConstants[] = {
36	DEFINE_CONSTANT(B_FIND_PATH_INSTALLATION_LOCATION_DIRECTORY,
37		"the installation location"),
38	DEFINE_CONSTANT(B_FIND_PATH_ADD_ONS_DIRECTORY,
39		"the add-ons directory"),
40	DEFINE_CONSTANT(B_FIND_PATH_APPS_DIRECTORY,
41		"the applications directory"),
42	DEFINE_CONSTANT(B_FIND_PATH_BIN_DIRECTORY,
43		"the command line programs directory"),
44	DEFINE_CONSTANT(B_FIND_PATH_BOOT_DIRECTORY,
45		"the boot data directory"),
46	DEFINE_CONSTANT(B_FIND_PATH_CACHE_DIRECTORY,
47		"the cache directory"),
48	DEFINE_CONSTANT(B_FIND_PATH_DATA_DIRECTORY,
49		"the data directory"),
50	DEFINE_CONSTANT(B_FIND_PATH_DEVELOP_DIRECTORY,
51		"the develop directory"),
52	DEFINE_CONSTANT(B_FIND_PATH_DEVELOP_LIB_DIRECTORY,
53		"the development libraries directory"),
54	DEFINE_CONSTANT(B_FIND_PATH_DOCUMENTATION_DIRECTORY,
55		"the documentation directory"),
56	DEFINE_CONSTANT(B_FIND_PATH_ETC_DIRECTORY,
57		"the Unix etc directory (global settings)"),
58	DEFINE_CONSTANT(B_FIND_PATH_FONTS_DIRECTORY,
59		"the fonts directory"),
60	DEFINE_CONSTANT(B_FIND_PATH_HEADERS_DIRECTORY,
61		"the development headers directory"),
62	DEFINE_CONSTANT(B_FIND_PATH_LIB_DIRECTORY,
63		"the libraries directory"),
64	DEFINE_CONSTANT(B_FIND_PATH_LOG_DIRECTORY,
65		"the logging directory"),
66	DEFINE_CONSTANT(B_FIND_PATH_MEDIA_NODES_DIRECTORY,
67		"the media node add-ons directory"),
68	DEFINE_CONSTANT(B_FIND_PATH_PACKAGES_DIRECTORY,
69		"the packages directory"),
70	DEFINE_CONSTANT(B_FIND_PATH_PREFERENCES_DIRECTORY,
71		"the preference applications directory"),
72	DEFINE_CONSTANT(B_FIND_PATH_SERVERS_DIRECTORY,
73		"the server programs directory"),
74	DEFINE_CONSTANT(B_FIND_PATH_SETTINGS_DIRECTORY,
75		"the global settings directory"),
76	DEFINE_CONSTANT(B_FIND_PATH_SOUNDS_DIRECTORY,
77		"the sound files directory"),
78	DEFINE_CONSTANT(B_FIND_PATH_SPOOL_DIRECTORY,
79		"the (mail) spool directory"),
80	DEFINE_CONSTANT(B_FIND_PATH_TRANSLATORS_DIRECTORY,
81		"the translator add-ons directory"),
82	DEFINE_CONSTANT(B_FIND_PATH_VAR_DIRECTORY,
83		"the Unix var directory (global writable data)"),
84	DEFINE_CONSTANT(B_FIND_PATH_PACKAGE_PATH,
85		"the path of the package the file specified via -p belongs to"),
86};
87
88static const size_t kDirectoryConstantCount
89	= sizeof(kDirectoryConstants) / sizeof(kDirectoryConstants[0]);
90
91
92static const char* kUsage =
93	"Usage: %s [ <options> ] [ <kind> [<subpath>] ]\n"
94	"Prints paths specified by directory constant <kind>. <subpath>, if\n"
95	"specified, is appended to each path. By default a path is printed for\n"
96	"each existing installation location (one per line); the options can\n"
97	"modify this behavior.\n"
98	"\n"
99	"Options:\n"
100	"  -a <architecture>\n"
101	"    If the path(s) specified by <kind> are architecture specific, use\n"
102	"    architecture <architecture>. If not specified, the primary\n"
103	"    architecture is used, unless the -p/--path option is specified, in\n"
104	"    which case the architecture associated with the given <path> is\n"
105	"    used.\n"
106	"  -c <separator>\n"
107	"    Concatenate the resulting paths, separated only by <separator>,\n"
108	"    instead of printing a path per line.\n"
109	"  -d, --dependency <dependency>\n"
110	"    Modifies the behavior of the -p option. Use the installation "
111		"location\n"
112	"    where the dependency <dependency> of the package that the entry\n"
113	"    referred to by <path> belongs to is installed.\n"
114	"  -e, --existing\n"
115	"    Print only paths that refer to existing entries.\n"
116	"  -h, --help\n"
117	"    Print this usage info.\n"
118	"  -l, --list\n"
119	"    Print a list of the possible constants for the <kind> parameter.\n"
120	"  -p, --path <path>\n"
121	"    Print only one path, the one for the installation location that\n"
122	"    contains the path <path>.\n"
123	"  -r, --resolvable <expression>\n"
124	"    Print only one path, the one for the installation location for the\n"
125	"    package providing the resolvable matching the expression\n"
126	"    <expression>. The expressions can be a simple resolvable name or\n"
127	"    a resolvable name with operator and version (e.g.\n"
128	"    \"cmd:perl >= 5\"; must be one argument).\n"
129	"  -R, --reverse\n"
130	"    Print paths in reverse order, i.e. from most general to most\n"
131	"    specific.\n"
132;
133
134
135static void
136print_usage_and_exit(bool error)
137{
138    fprintf(error ? stderr : stdout, kUsage, kCommandName);
139    exit(error ? 1 : 0);
140}
141
142
143int
144main(int argc, const char* const* argv)
145{
146	const char* architecture = NULL;
147	const char* dependency = NULL;
148	const char* referencePath = NULL;
149	const char* resolvable = NULL;
150	bool existingOnly = false;
151	bool reverseOrder = false;
152	const char* separator = NULL;
153
154	while (true) {
155		static struct option sLongOptions[] = {
156			{ "architecture", required_argument, 0, 'a' },
157			{ "dependency", required_argument, 0, 'd' },
158			{ "help", no_argument, 0, 'h' },
159			{ "list", no_argument, 0, 'l' },
160			{ "path", required_argument, 0, 'p' },
161			{ "resolvable", required_argument, 0, 'pr' },
162			{ "reverse", no_argument, 0, 'R' },
163			{ 0, 0, 0, 0 }
164		};
165
166		opterr = 0; // don't print errors
167		int c = getopt_long(argc, (char**)argv, "+a:c:d:ehlp:r:R",
168			sLongOptions, NULL);
169		if (c == -1)
170			break;
171
172		switch (c) {
173			case 'a':
174				architecture = optarg;
175				break;
176
177			case 'c':
178				separator = optarg;
179				break;
180
181			case 'd':
182				dependency = optarg;
183				break;
184
185			case 'e':
186				existingOnly = true;
187				break;
188
189			case 'h':
190				print_usage_and_exit(false);
191				break;
192
193			case 'l':
194				for (size_t i = 0; i < kDirectoryConstantCount; i++) {
195					const DirectoryConstantEntry& entry
196						= kDirectoryConstants[i];
197					printf("%s\n    - %s\n", entry.string, entry.description);
198				}
199				exit(0);
200
201			case 'p':
202				referencePath = optarg;
203				break;
204
205			case 'r':
206				resolvable = optarg;
207				break;
208
209			case 'R':
210				reverseOrder = true;
211				break;
212
213			default:
214				print_usage_and_exit(true);
215				break;
216		}
217	}
218
219	// The remaining arguments are the kind constant and optionally the subpath.
220	if (optind >= argc || optind + 2 < argc)
221		print_usage_and_exit(true);
222
223	const char* kindConstant = argv[optind++];
224
225	const char* subPath = NULL;
226	if (optind < argc)
227		subPath = argv[optind++];
228
229	// only one of path or resolvable may be specified
230	if (referencePath != NULL && resolvable != NULL)
231		print_usage_and_exit(true);
232
233	// resolve the directory constant
234	path_base_directory baseDirectory = B_FIND_PATH_IMAGE_PATH;
235	bool found = false;
236	for (size_t i = 0; i < kDirectoryConstantCount; i++) {
237		const DirectoryConstantEntry& entry = kDirectoryConstants[i];
238		if (strcmp(kindConstant, entry.string) == 0) {
239			found = true;
240			baseDirectory = entry.constant;
241			break;
242		}
243	}
244
245	if (!found) {
246		fprintf(stderr, "Error: Unsupported directory constant \"%s\".\n",
247			kindConstant);
248		exit(1);
249	}
250
251	if (referencePath != NULL || resolvable != NULL) {
252		try {
253			BPathFinder pathFinder;
254			if (referencePath != NULL) {
255				pathFinder.SetTo(referencePath, dependency);
256			} else {
257				pathFinder.SetTo(
258					BPackageKit::BPackageResolvableExpression(resolvable),
259					dependency);
260			}
261
262			BPath path;
263			status_t error = pathFinder.FindPath(architecture, baseDirectory,
264				subPath, existingOnly ? B_FIND_PATH_EXISTING_ONLY : 0, path);
265			if (error != B_OK) {
266				fprintf(stderr, "Error: Failed to find path: %s\n",
267					strerror(error));
268				exit(1);
269			}
270
271			printf("%s\n", path.Path());
272		} catch(BFatalErrorException& exception) {
273			if (!exception.Details().IsEmpty())
274				fprintf(stderr, "%s", exception.Details().String());
275			if (exception.Error() == B_OK) {
276				fprintf(stderr, "Error: %s\n", exception.Message().String());
277			} else {
278				fprintf(stderr, "Error: %s: %s\n", exception.Message().String(),
279					strerror(exception.Error()));
280			}
281			return 1;
282		}
283	} else {
284		BStringList paths;
285		status_t error = BPathFinder::FindPaths(architecture, baseDirectory,
286			subPath, existingOnly ? B_FIND_PATH_EXISTING_ONLY : 0, paths);
287		if (error != B_OK) {
288			fprintf(stderr, "Error: Failed to find paths: %s\n",
289				strerror(error));
290			exit(1);
291		}
292
293		int32 count = paths.CountStrings();
294		if (reverseOrder) {
295			for (int32 i = 0; i < count / 2; i++)
296				paths.Swap(i, count - i - 1);
297		}
298
299		if (separator != NULL) {
300			BString result = paths.Join(separator);
301			if (result.IsEmpty()) {
302				fprintf(stderr, "Error: Out of memory!\n");
303				exit(1);
304			}
305			printf("%s\n", result.String());
306		} else {
307			for (int32 i = 0; i < count; i++)
308				printf("%s\n", paths.StringAt(i).String());
309		}
310	}
311
312	return 0;
313}
314