libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / bin / setarch.cpp
blob5f476347d3fb1506ccb36d348ca590a800e3e978
1 /*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <errno.h>
8 #include <getopt.h>
9 #include <pwd.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
15 #include <Architecture.h>
16 #include <Path.h>
17 #include <PathFinder.h>
18 #include <StringList.h>
21 extern const char* __progname;
22 const char* kCommandName = __progname;
25 static const char* kUsage =
26 "Usage: %s [-hl]\n"
27 " %s [-p] <architecture> [ <command> ... ]\n"
28 "Executes the given command or, by default, a shell with a PATH\n"
29 "environment variable modified such that commands for the given\n"
30 "architecture will be preferred, respectively used exclusively in case of\n"
31 "the primary architecture.\n"
32 "\n"
33 "Options:\n"
34 " -h, --help\n"
35 " Print this usage info.\n"
36 " -l, --list-architectures\n"
37 " List all architectures.\n"
38 " -p, --print-path\n"
39 " Only print the modified PATH variable value; don't execute any\n"
40 " command.\n"
44 static void
45 print_usage_and_exit(bool error)
47 fprintf(error ? stderr : stdout, kUsage, kCommandName, kCommandName);
48 exit(error ? 1 : 0);
52 static bool
53 is_primary_architecture(const char* architecture)
55 return strcmp(architecture, get_primary_architecture()) == 0;
59 static void
60 get_bin_directories(const char* architecture, BStringList& _directories)
62 status_t error = BPathFinder::FindPaths(architecture,
63 B_FIND_PATH_BIN_DIRECTORY, NULL, 0, _directories);
64 if (error != B_OK) {
65 fprintf(stderr, "Error: Failed to get bin directories for architecture "
66 "%s: %s\n", architecture, strerror(error));
67 exit(1);
72 static void
73 compute_new_paths(const char* architecture, BStringList& _paths)
75 // get the primary architecture bin paths
76 BStringList primaryBinDirectories;
77 get_bin_directories(get_primary_architecture(), primaryBinDirectories);
79 // get the bin paths to insert
80 BStringList binDirectoriesToInsert;
81 if (!is_primary_architecture(architecture))
82 get_bin_directories(architecture, binDirectoriesToInsert);
84 // split the PATH variable
85 char* pathVariableValue = getenv("PATH");
86 BStringList paths;
87 if (pathVariableValue != NULL
88 && !BString(pathVariableValue).Split(":", true, paths)) {
89 fprintf(stderr, "Error: Out of memory!\n");
90 exit(1);
93 // Filter the paths, removing any path that isn't associated with the
94 // primary architecture. Also find the insertion index for the architecture
95 // bin paths.
96 int32 insertionIndex = -1;
97 int32 count = paths.CountStrings();
98 for (int32 i = 0; i < count; i++) {
99 // We always keep relative paths. Filter absolute ones only.
100 const char* path = paths.StringAt(i);
101 if (path[0] == '/') {
102 // try to normalize the path
103 BPath normalizedPath;
104 if (normalizedPath.SetTo(path, NULL, true) == B_OK)
105 path = normalizedPath.Path();
107 // Check, if this is a primary bin directory. If not, determine the
108 // path's architecture.
109 int32 index = primaryBinDirectories.IndexOf(path);
110 if (index >= 0) {
111 if (insertionIndex < 0)
112 insertionIndex = _paths.CountStrings();
113 } else if (!is_primary_architecture(
114 guess_architecture_for_path(path))) {
115 // a non-primary architecture path -- skip
116 continue;
120 if (!_paths.Add(paths.StringAt(i))) {
121 fprintf(stderr, "Error: Out of memory!\n");
122 exit(1);
126 // Insert the paths for the specified architecture, if any.
127 if (!binDirectoriesToInsert.IsEmpty()) {
128 if (!(insertionIndex < 0
129 ? _paths.Add(binDirectoriesToInsert)
130 : _paths.Add(binDirectoriesToInsert, insertionIndex))) {
131 fprintf(stderr, "Error: Out of memory!\n");
132 exit(1);
139 main(int argc, const char* const* argv)
141 bool printPath = false;
142 bool listArchitectures = false;
144 while (true) {
145 static struct option sLongOptions[] = {
146 { "help", no_argument, 0, 'h' },
147 { "list-architectures", no_argument, 0, 'l' },
148 { "print-path", no_argument, 0, 'p' },
149 { 0, 0, 0, 0 }
152 opterr = 0; // don't print errors
153 int c = getopt_long(argc, (char**)argv, "+hlp",
154 sLongOptions, NULL);
155 if (c == -1)
156 break;
158 switch (c) {
159 case 'h':
160 print_usage_and_exit(false);
161 break;
163 case 'l':
164 listArchitectures = true;
165 break;
167 case 'p':
168 printPath = true;
169 break;
171 default:
172 print_usage_and_exit(true);
173 break;
177 // only one of listArchitectures, printPath may be specified
178 if (listArchitectures && printPath)
179 print_usage_and_exit(true);
181 // get architectures
182 BStringList architectures;
183 status_t error = get_architectures(architectures);
184 if (error != B_OK) {
185 fprintf(stderr, "Error: Failed to get architectures: %s\n",
186 strerror(error));
187 exit(1);
190 // list architectures
191 if (listArchitectures) {
192 if (optind != argc)
193 print_usage_and_exit(true);
195 int32 count = architectures.CountStrings();
196 for (int32 i = 0; i < count; i++)
197 printf("%s\n", architectures.StringAt(i).String());
198 return 0;
201 // The remaining arguments are the architecture and optionally the command
202 // to execute.
203 if (optind >= argc)
204 print_usage_and_exit(true);
205 const char* architecture = optind < argc ? argv[optind++] : NULL;
207 int commandArgCount = argc - optind;
208 const char* const* commandArgs = commandArgCount > 0 ? argv + optind : NULL;
210 if (printPath && commandArgs != NULL)
211 print_usage_and_exit(true);
213 // check the architecture
214 if (!architectures.HasString(architecture)) {
215 fprintf(stderr, "Error: Unsupported architecture \"%s\"\n",
216 architecture);
217 exit(1);
220 // get the new paths
221 BStringList paths;
222 compute_new_paths(architecture, paths);
224 BString pathVariableValue = paths.Join(":");
225 if (!paths.IsEmpty() && pathVariableValue.IsEmpty())
226 fprintf(stderr, "Error: Out of memory!\n");
228 if (printPath) {
229 printf("%s\n", pathVariableValue.String());
230 return 0;
233 // set PATH
234 if (setenv("PATH", pathVariableValue, 1) != 0) {
235 fprintf(stderr, "Error: Failed to set PATH: %s\n", strerror(errno));
236 exit(1);
239 // if no command is given, get the user's shell
240 const char* shellCommand[3];
241 if (commandArgs == NULL) {
242 struct passwd* pwd = getpwuid(geteuid());
243 shellCommand[0] = pwd != NULL ? pwd->pw_shell : "/bin/sh";
244 shellCommand[1] = "-l";
245 shellCommand[2] = NULL;
246 commandArgs = shellCommand;
247 commandArgCount = 2;
250 // exec the command
251 execvp(commandArgs[0], (char* const*)commandArgs);
253 fprintf(stderr, "Error: Executing \"%s\" failed: %s\n", commandArgs[0],
254 strerror(errno));
255 return 1;