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 <Architecture.h>
13#include <Path.h>
14#include <PathFinder.h>
15#include <StringList.h>
16
17
18extern const char* __progname;
19const char* kCommandName = __progname;
20
21
22static const char* kUsage =
23	"Usage: %s [ <options> ] [ <path> ]\n"
24	"Prints the architecture currently set via the PATH environment variable,\n"
25	"when no arguments are given. When <path> is specified, the architecture\n"
26	"associated with that path is printed. The options allow to print the\n"
27	"primary architecture or the secondary architectures.\n"
28	"\n"
29	"Options:\n"
30	"  -h, --help\n"
31	"    Print this usage info.\n"
32	"  -p, --primary\n"
33	"    Print the primary architecture.\n"
34	"  -s, --secondary\n"
35	"    Print all secondary architectures for which support is installed.\n"
36;
37
38
39static void
40print_usage_and_exit(bool error)
41{
42    fprintf(error ? stderr : stdout, kUsage, kCommandName);
43    exit(error ? 1 : 0);
44}
45
46
47static BString
48get_current_architecture()
49{
50	// get the system installation location path
51	BPath systemPath;
52	if (find_directory(B_SYSTEM_DIRECTORY, &systemPath) != B_OK)
53		return BString();
54
55	// get all architectures
56	BStringList architectures;
57	get_architectures(architectures);
58	if (architectures.CountStrings() < 2)
59		return BString();
60
61	// get the system bin directory for each architecture
62	BStringList binDirectories;
63	BPathFinder pathFinder(systemPath.Path());
64	int32 architectureCount = architectures.CountStrings();
65	for (int32 i = 0; i < architectureCount; i++) {
66		BPath path;
67		if (pathFinder.FindPath(architectures.StringAt(i),
68				B_FIND_PATH_BIN_DIRECTORY, NULL, 0, path) != B_OK
69			|| !binDirectories.Add(path.Path())) {
70			return BString();
71		}
72	}
73
74	// Get and split the PATH environmental variable value. The first system
75	// bin path we encounter implies the architecture.
76	char* pathVariableValue = getenv("PATH");
77	BStringList paths;
78	if (pathVariableValue != NULL
79		&& BString(pathVariableValue).Split(":", true, paths)) {
80		int32 count = paths.CountStrings();
81		for (int32 i = 0; i < count; i++) {
82			// normalize the path, but skip a relative one
83			BPath path;
84			if (paths.StringAt(i)[0] != '/'
85				|| path.SetTo(paths.StringAt(i), NULL, true) != B_OK) {
86				continue;
87			}
88
89			int32 index = binDirectories.IndexOf(path.Path());
90			if (index >= 0)
91				return architectures.StringAt(index);
92		}
93	}
94
95	return BString();
96}
97
98
99int
100main(int argc, const char* const* argv)
101{
102	bool printPrimary = false;
103	bool printSecondary = false;
104
105	while (true) {
106		static struct option sLongOptions[] = {
107			{ "help", no_argument, 0, 'h' },
108			{ "primary", no_argument, 0, 'p' },
109			{ "secondary", no_argument, 0, 's' },
110			{ 0, 0, 0, 0 }
111		};
112
113		opterr = 0; // don't print errors
114		int c = getopt_long(argc, (char**)argv, "+hps",
115			sLongOptions, NULL);
116		if (c == -1)
117			break;
118
119		switch (c) {
120			case 'h':
121				print_usage_and_exit(false);
122				break;
123
124			case 'p':
125				printPrimary = true;
126				break;
127
128			case 's':
129				printSecondary = true;
130				break;
131
132			default:
133				print_usage_and_exit(true);
134				break;
135		}
136	}
137
138	// The remaining argument is the optional path.
139	const char* path = optind < argc ? argv[optind++] : NULL;
140	if (optind < argc)
141		print_usage_and_exit(true);
142
143	// only one of path, printPrimary, printSecondary may be specified
144	if (int(path != NULL) + int(printPrimary) + int(printSecondary) > 1)
145		print_usage_and_exit(true);
146
147	if (path != NULL) {
148		// architecture for given path
149		printf("%s\n", guess_architecture_for_path(path));
150	} else if (printPrimary) {
151		// primary architecture
152		printf("%s\n", get_primary_architecture());
153	} else if (printSecondary) {
154		// secondary architectures
155		BStringList architectures;
156		get_secondary_architectures(architectures);
157		int32 count = architectures.CountStrings();
158		for (int32 i = 0; i < count; i++)
159			printf("%s\n", architectures.StringAt(i).String());
160	} else {
161		// current architecture as implied by PATH
162		BString architecture = get_current_architecture();
163		printf("%s\n",
164			architecture.IsEmpty()
165				? get_primary_architecture() : architecture.String());
166	}
167
168	return 0;
169}
170