2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
15 #include <Architecture.h>
17 #include <PathFinder.h>
18 #include <StringList.h>
21 extern const char* __progname
;
22 const char* kCommandName
= __progname
;
25 static const char* kUsage
=
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"
35 " Print this usage info.\n"
36 " -l, --list-architectures\n"
37 " List all architectures.\n"
39 " Only print the modified PATH variable value; don't execute any\n"
45 print_usage_and_exit(bool error
)
47 fprintf(error
? stderr
: stdout
, kUsage
, kCommandName
, kCommandName
);
53 is_primary_architecture(const char* architecture
)
55 return strcmp(architecture
, get_primary_architecture()) == 0;
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
);
65 fprintf(stderr
, "Error: Failed to get bin directories for architecture "
66 "%s: %s\n", architecture
, strerror(error
));
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");
87 if (pathVariableValue
!= NULL
88 && !BString(pathVariableValue
).Split(":", true, paths
)) {
89 fprintf(stderr
, "Error: Out of memory!\n");
93 // Filter the paths, removing any path that isn't associated with the
94 // primary architecture. Also find the insertion index for the architecture
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
);
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
120 if (!_paths
.Add(paths
.StringAt(i
))) {
121 fprintf(stderr
, "Error: Out of memory!\n");
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");
139 main(int argc
, const char* const* argv
)
141 bool printPath
= false;
142 bool listArchitectures
= false;
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' },
152 opterr
= 0; // don't print errors
153 int c
= getopt_long(argc
, (char**)argv
, "+hlp",
160 print_usage_and_exit(false);
164 listArchitectures
= true;
172 print_usage_and_exit(true);
177 // only one of listArchitectures, printPath may be specified
178 if (listArchitectures
&& printPath
)
179 print_usage_and_exit(true);
182 BStringList architectures
;
183 status_t error
= get_architectures(architectures
);
185 fprintf(stderr
, "Error: Failed to get architectures: %s\n",
190 // list architectures
191 if (listArchitectures
) {
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());
201 // The remaining arguments are the architecture and optionally the command
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",
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");
229 printf("%s\n", pathVariableValue
.String());
234 if (setenv("PATH", pathVariableValue
, 1) != 0) {
235 fprintf(stderr
, "Error: Failed to set PATH: %s\n", strerror(errno
));
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
;
251 execvp(commandArgs
[0], (char* const*)commandArgs
);
253 fprintf(stderr
, "Error: Executing \"%s\" failed: %s\n", commandArgs
[0],