]> git.saurik.com Git - apple/system_cmds.git/blame - arch.tproj/arch.m
system_cmds-541.tar.gz
[apple/system_cmds.git] / arch.tproj / arch.m
CommitLineData
34d340d7
A
1/*
2 * Copyright (c) 1999, 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/cdefs.h>
25#include <stdio.h>
26#include <string.h>
8459d725 27#include <stdbool.h>
34d340d7
A
28#include <stdlib.h>
29#include <stddef.h>
30#include <unistd.h>
31#include <spawn.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/param.h>
35#include <paths.h>
36#include <err.h>
37#include <mach/mach.h>
38#include <mach-o/arch.h>
39#include <Foundation/Foundation.h>
ef8ad44b 40#include <TargetConditionals.h>
34d340d7
A
41
42#ifndef ARCH_PROG
43#define ARCH_PROG "arch"
44#endif
45#define CPUDELTA 10
46#ifndef MACHINE_PROG
47#define MACHINE_PROG "machine"
48#endif
49
50#define CPUCOUNT(c) ((c)->ptr - (c)->buf)
51
52static NSMutableDictionary *ArchDict;
53static NSString *KeyExecPath = @"ExecutablePath";
54static NSString *KeyPlistVersion = @"PropertyListVersion";
55static NSString *KeyPrefOrder = @"PreferredOrder";
56static NSString *PlistExtension = @"plist";
57static NSString *SettingsDir = @"archSettings";
58
59static const char envname[] = "ARCHPREFERENCE";
60
61typedef struct {
62 cpu_type_t *buf;
63 cpu_type_t *ptr;
64 cpu_type_t *end;
65 int errs;
66} CPU;
67
68typedef struct {
8459d725
A
69 const char *arch;
70 int cpu;
71} CPUTypes;
34d340d7 72
8459d725
A
73static const CPUTypes initArches[] = {
74#if defined(__i386__) || defined(__x86_64__)
34d340d7 75 {"i386", CPU_TYPE_I386},
34d340d7 76 {"x86_64", CPU_TYPE_X86_64},
8459d725
A
77#elif defined(__arm__)
78 {"arm", CPU_TYPE_ARM},
79#else
80#error "Unsupported architecture"
81#endif
82 {NULL, 0} // sentinel
34d340d7
A
83};
84
8459d725
A
85/*
86 * ignore contains architectures supported by previous releases, but
87 * now unsupported. The seen field will be set to true if the corresponding
88 * architecture was specified.
89 */
90
91typedef struct {
92 const char *arch;
93 bool seen;
94} Ignore;
95
96static Ignore ignore[] = {
97#if defined(__i386__) || defined(__x86_64__)
98 {"ppc", false},
99 {"ppc64", false},
100#endif
101 {NULL, false} // sentinel
102};
103
104/* The total number of architectures specified */
105static int archCount = 0;
106
107/* environment SPI */
108char **_copyenv(char **env);
109int _setenvp(const char *name, const char *value, int rewrite, char ***envp, void *state);
110int _unsetenvp(const char *name, char ***envp, void *state);
111
112/* copy of environment */
113char **envCopy = NULL;
114extern char **environ;
115
116/*
117 * The native 32 and 64-bit architectures (this is relative to the architecture
118 * the arch command is running. NULL means unsupported.
119 */
120#if defined(__i386__) || defined(__x86_64__)
121#define NATIVE_32 "i386"
122#define NATIVE_64 "x86_64"
123#elif defined(__arm__)
124#define NATIVE_32 "arm"
125#define NATIVE_64 NULL
126#else
127#error "Unsupported architecture"
128#endif
129bool native32seen = false;
130bool native64seen = false;
131
34d340d7
A
132/*
133 * arch - perform the original behavior of the arch and machine commands.
134 * The archcmd flag is non-zero for the arch command, zero for the machine
135 * command. This routine never returns.
136 */
137static void __dead2
138arch(int archcmd)
139{
140 const NXArchInfo *arch = NXGetLocalArchInfo();
141
142 if(!arch)
143 errx(-1, "Unknown architecture.");
144 if(archcmd) {
145 arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE);
146 if(!arch)
147 errx(-1, "Unknown architecture.");
148 }
149 printf("%s%s", arch->name, (isatty(STDIN_FILENO) ? "\n" : ""));
150 exit(0);
151}
152
153/*
8459d725
A
154 * spawnIt - run the posix_spawn command. cpu is the auto-sizing CPU structure.
155 * pflag is non-zero to call posix_spawnp; zero means to call posix_spawn.
156 * str is the name/path to pass to posix_spawn{,p}, and argv are
157 * the argument arrays to pass. This routine never returns.
34d340d7
A
158 */
159static void __dead2
8459d725 160spawnIt(CPU *cpu, int pflag, const char *str, char **argv)
34d340d7 161{
34d340d7
A
162 posix_spawnattr_t attr;
163 pid_t pid;
164 int ret;
165 size_t copied;
8459d725
A
166 const Ignore *ip;
167 int count = CPUCOUNT(cpu);
168 cpu_type_t *prefs = cpu->buf;
169
170 if(count == 0) {
171 if(archCount == 0) // shouldn't happen
172 errx(1, "spawnIt called with no architectures specified");
173 for(ip = ignore; ip->arch; ip++) {
174 if(ip->seen)
175 warnx("Unsupported architecture: %s", ip->arch);
176 }
177 if(native32seen)
178 warnx("Unsupported native 32-bit architecture");
179 if(native64seen)
180 warnx("Unsupported native 64-bit architecture");
181 exit(1);
182 }
183 for(ip = ignore; ip->arch; ip++) {
184 if(ip->seen)
185 fprintf(stderr, "warning: unsupported architecture: %s\n", ip->arch);
186 }
187 if(native32seen)
188 fprintf(stderr, "warning: unsupported native 32-bit architecture\n");
189 if(native64seen)
190 fprintf(stderr, "warning: unsupported native 64-bit architecture\n");
34d340d7
A
191
192 if((ret = posix_spawnattr_init(&attr)) != 0)
193 errc(1, ret, "posix_spawnattr_init");
194 /* do the equivalent of exec, rather than creating a separate process */
195 if((ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC)) != 0)
196 errc(1, ret, "posix_spawnattr_setflags");
197 if((ret = posix_spawnattr_setbinpref_np(&attr, count, prefs, &copied)) != 0)
198 errc(1, ret, "posix_spawnattr_setbinpref_np");
199 if(copied != count)
200 errx(1, "posix_spawnattr_setbinpref_np only copied %d of %d", (int)copied, count);
201 if(pflag)
8459d725 202 ret = posix_spawnp(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ);
34d340d7 203 else
8459d725 204 ret = posix_spawn(&pid, str, NULL, &attr, argv, envCopy ? envCopy : environ);
34d340d7
A
205 errc(1, ret, "posix_spawn%s: %s", (pflag ? "p" : ""), str);
206}
207
208/*
209 * initCPU - initialize a CPU structure, a dynamically expanding CPU types
210 * array.
211 */
212static void
213initCPU(CPU *cpu)
214{
215 cpu->buf = (cpu_type_t *)malloc(CPUDELTA * sizeof(cpu_type_t));
216 if(!cpu->buf)
217 err(1, "Failed to malloc CPU buffer");
218 cpu->ptr = cpu->buf;
219 cpu->end = cpu->buf + CPUDELTA;
220 cpu->errs = 0;
221}
222
223/*
224 * addCPU - add a new CPU type value to the CPU structure, expanding
225 * the array as necessary.
226 */
227static void
228addCPU(CPU *cpu, int n)
229{
8459d725
A
230 archCount++;
231 if(n <= 0) { // ignore values
232 ignore[-n].seen = true; // negate to get real index
233 return;
234 }
34d340d7
A
235 if(cpu->ptr >= cpu->end) {
236 cpu_type_t *new = realloc(cpu->buf, (cpu->end - cpu->buf + CPUDELTA) * sizeof(cpu_type_t));
237 if(!new)
238 err(1, "Out of memory realloc-ing CPU structure");
239 ptrdiff_t diff = (void *)new - (void *)cpu->buf;
240 cpu->buf = new;
241 cpu->ptr = (cpu_type_t *)((void *)cpu->ptr + diff);
242 cpu->end = (cpu_type_t *)((void *)cpu->end + diff) + CPUDELTA;
243 }
244 *cpu->ptr++ = n;
245}
246
247/*
248 * addCPUbyname - add a new CPU type, given by name, to the CPU structure,
249 * expanding the array as necessary. The name is converted to a type value
250 * by the ArchDict dictionary.
251 */
252static void
253addCPUbyname(CPU *cpu, const char *name)
254{
255 NSNumber *n = (NSNumber *)[ArchDict objectForKey: [[NSString stringWithUTF8String: name] lowercaseString]];
256 if(n == nil) {
257 warnx("Unknown architecture: %s", name);
258 cpu->errs++;
259 return;
260 }
261 addCPU(cpu, [n intValue]);
262}
263
264/*
265 * useEnv - parse the environment variable for CPU preferences. Use name
266 * to look for program-specific preferences, and append any CPU types to cpu.
267 * Returns the number of CPU types. Returns any specified execute path in
268 * execpath.
269 *
270 * The environment variable ARCHPREFERENCE has the format:
271 * spec[;spec]...
272 * a semicolon separated list of specifiers. Each specifier has the format:
273 * [prog:[execpath:]]type[,type]...
274 * a comma separate list of CPU type names, optionally proceeded by a program
275 * name and an execpath. If program name exist, that types only apply to that
276 * program. If execpath is specified, it is returned. If no program name
277 * exists, then it applies to all programs. So ordering of the specifiers is
278 * important, as the default (no program name) specifier must be last.
279 */
280static int
281useEnv(CPU *cpu, const char *name, char **execpath)
282{
283 char *val = getenv(envname);
284 if(!val)
285 return 0;
286
287 /* cp will point to the basename of name */
288 const char *cp = strrchr(name, '/');
289 if(cp) {
290 cp++;
291 if(!*cp)
292 errx(1, "%s: no name after last slash", name);
293 } else
294 cp = name;
295 /* make a copy of the environment variable value, so we can modify it */
296 val = strdup(val);
297 if(!val)
298 err(1, "Can't copy environment %s", envname);
299 char *str = val;
300 char *blk;
301 /* for each specifier */
302 while((blk = strsep(&str, ";")) != NULL) {
303 if(*blk == 0)
304 continue; /* two adjacent semicolons */
305 /* now split on colons */
306 char *n = strsep(&blk, ":");
307 if(blk) {
308 char *p = strsep(&blk, ":");
309 if(!blk) { /* there is only one colon, so no execpath */
310 blk = p;
311 p = NULL;
312 } else if(!*p) /* two consecutive colons, so no execpath */
313 p = NULL;
314 if(!*blk)
315 continue; /* no cpu list, so skip */
316 /* if the name matches, or there is no name, process the cpus */
317 if(!*n || strcmp(n, cp) == 0) {
8459d725 318 if(archCount <= 0) { /* only if we haven't processed architectures */
34d340d7
A
319 char *t;
320 while((t = strsep(&blk, ",")) != NULL)
321 addCPUbyname(cpu, t);
322 }
323 *execpath = (*n ? p : NULL); /* only use the exec path is name is set */
324 break;
325 }
326 } else { /* no colons at all, so process as default */
8459d725 327 if(archCount <= 0) { /* only if we haven't processed architectures */
34d340d7
A
328 blk = n;
329 while((n = strsep(&blk, ",")) != NULL)
330 addCPUbyname(cpu, n);
331 }
332 *execpath = NULL;
333 break;
334 }
335 }
336 if(cpu->errs) /* errors during addCPUbyname are fatal */
337 exit(1);
8459d725 338 return archCount; /* return count of architectures */
34d340d7
A
339}
340
341/*
342 * spawnFromPreference - called when argv[0] is not "arch" or "machine", or
343 * argv[0] was arch, but no commandline architectures were specified.
344 * If the environment variable ARCHPREFERENCE is specified, and there is a
345 * match to argv[0], use the specified cpu preferences. If no exec path
346 * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE,
347 * get any additional information from a .plist file with the name of argv[0].
348 * This routine never returns.
349 */
350static void __dead2
8459d725 351spawnFromPreferences(CPU *cpu, int needexecpath, char **argv)
34d340d7
A
352{
353 char *epath = NULL;
354 int count;
355 const char *prog = strrchr(*argv, '/');
356 if(prog)
357 prog++;
358 else
359 prog = *argv;
360 if(!*prog)
361 errx(1, "Not program name specified");
362
363 /* check the environment variable first */
364 if((count = useEnv(cpu, prog, &epath)) > 0) {
365 /* if we were called as arch, use posix_spawnp */
366 if(!needexecpath)
8459d725 367 spawnIt(cpu, 1, (epath ? epath : *argv), argv);
34d340d7
A
368 /* otherwise, if we have the executable path, call posix_spawn */
369 if(epath)
8459d725 370 spawnIt(cpu, 0, epath, argv);
34d340d7
A
371 }
372
373 /* pathArray is use to build the .plist file path for each domain */
374 NSMutableArray *pathArray = [NSMutableArray arrayWithCapacity: 3];
375 if(!pathArray)
376 errx(1, "Can't create NSMutableArray");
377 [pathArray addObject: @""]; // placeholder for domain directory
378 [pathArray addObject: SettingsDir];
379 [pathArray addObject: [[NSString stringWithUTF8String: prog] stringByAppendingPathExtension: PlistExtension]];
380
381 /* get the list of top level directories for all domains */
382 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, true);
383 int cnt = [paths count];
384 if(!cnt)
385 errx(1, "No domains");
386
387 /* create the .plist path, and try to read it */
388 int i;
389 NSString *path = NULL;
390 NSData *data = NULL;
391 for(i = 0; i < cnt; i++) {
392 [pathArray replaceObjectAtIndex: 0 withObject: [paths objectAtIndex: i]];
393 path = [NSString pathWithComponents: pathArray];
394 data = [NSData dataWithContentsOfFile: path];
395 if(data) /* found it */
396 break;
397 }
398 if(!data)
399 errx(1, "Can't find any plists for %s", prog);
400
401 /* try to convert the file into a NSDictionary */
402 NSString *error;
403 id plist = [NSPropertyListSerialization propertyListFromData: data mutabilityOption: NSPropertyListImmutable format: nil errorDescription: &error];
404 if(!plist)
405 errx(1, "%s: NSPropertyListSerialization error: %s", [path UTF8String], [error UTF8String]);
406 if(![plist isKindOfClass: [NSDictionary class]])
407 errx(1, "%s: plist not a dictionary", [path UTF8String]);
408
409 NSString *execpath;
410 int errs = 0; /* scan for all errors and fail later */
411 do { /* begin block */
412 /* check the plist version */
413 NSString *vers = [(NSDictionary *)plist objectForKey: KeyPlistVersion];
414 if(!vers) {
415 warnx("%s: No key %s", [path UTF8String], [KeyPlistVersion UTF8String]);
416 errs++;
417 } else if(![vers isKindOfClass: [NSString class]]) {
418 warnx("%s: %s is not a string", [path UTF8String], [KeyPlistVersion UTF8String]);
419 errs++;
420 } else if(![vers isEqualToString: @"1.0"]) {
421 warnx("%s: %s not 1.0", [path UTF8String], [KeyPlistVersion UTF8String]);
422 errs++;
423 }
424 /* get the execpath */
425 execpath = [(NSDictionary *)plist objectForKey: KeyExecPath];
426 if(!execpath) {
427 warnx("%s: No key %s", [path UTF8String], [KeyExecPath UTF8String]);
428 errs++;
429 } else if(![execpath isKindOfClass: [NSString class]]) {
430 warnx("%s: %s is not a string", [path UTF8String], [KeyExecPath UTF8String]);
431 errs++;
432 }
433 /* if we already got cpu preferences from ARCHPREFERENCE, we are done */
434 if(count > 0)
435 break;
436 /* otherwise, parse the cpu preferences from the plist */
437 id p = [(NSDictionary *)plist objectForKey: KeyPrefOrder];
438 if(!p) {
439 warnx("%s: No key %s", [path UTF8String], [KeyPrefOrder UTF8String]);
440 errs++;
441 } else if(![p isKindOfClass: [NSArray class]]) {
442 warnx("%s: %s is not an array", [path UTF8String], [KeyPrefOrder UTF8String]);
443 errs++;
444 } else if((count = [p count]) == 0) {
445 warnx("%s: no entries in %s", [path UTF8String], [KeyPrefOrder UTF8String]);
446 errs++;
447 } else {
8459d725 448 /* finally build the cpu type array */
34d340d7
A
449 for(i = 0; i < count; i++) {
450 id a = [(NSArray *)p objectAtIndex: i];
451 NSNumber *n;
452 if(![a isKindOfClass: [NSString class]]) {
453 warnx("%s: entries %d of %s is not a string", [path UTF8String], i, [KeyPrefOrder UTF8String]);
454 errs++;
455 } else if((n = (NSNumber *)[ArchDict objectForKey: [(NSString *)a lowercaseString]]) == nil) {
456 warnx("%s: %s: unknown architecture %s", [path UTF8String], [KeyPrefOrder UTF8String], [(NSString *)a UTF8String]);
457 errs++;
458 } else {
459 addCPU(cpu, [n intValue]);
460 }
461 }
462 }
463 } while(0); /* end block */
464 if(errs) /* exit if there were any reported errors */
465 exit(1);
466
467 /* call posix_spawn */
8459d725 468 spawnIt(cpu, 0, [execpath fileSystemRepresentation], argv);
34d340d7
A
469}
470
471static void __dead2
472usage(int ret)
473{
474 fprintf(stderr,
475 "Usage: %s\n"
476 " Display the machine's architecture type\n"
8459d725 477 "Usage: %s {-arch_name | -arch arch_name} ... [-c] [-d envname] ... [-e envname=value] ... [-h] prog [arg ...]\n"
34d340d7
A
478 " Run prog with any arguments, using the given architecture\n"
479 " order. If no architectures are specified, use the\n"
480 " ARCHPREFERENCE environment variable, or a property list file.\n"
8459d725
A
481 " -c will clear out all environment variables before running prog.\n"
482 " -d will delete the given environment variable before running prog.\n"
483 " -e will add the given environment variable/value before running prog.\n"
34d340d7
A
484 " -h will print usage message and exit.\n",
485 ARCH_PROG, ARCH_PROG);
486 exit(ret);
487}
488
489/*
490 * wrapped - check the path to see if it is a link to /usr/bin/arch.
491 */
492static int
493wrapped(const char *name)
494{
495 int lp, ln;
496 char *p;
497 char *bp = NULL;
498 char *cur, *path;
499 char buf[MAXPATHLEN], rpbuf[MAXPATHLEN];
500 struct stat sb;
501
502 ln = strlen(name);
503
504 do { /* begin block */
505 /* If it's an absolute or relative path name, it's easy. */
506 if(index(name, '/')) {
507 if(stat(name, &sb) == 0 && S_ISREG(sb.st_mode) && access(name, X_OK) == 0) {
508 bp = (char *)name;
509 break;
510 }
511 errx(1, "%s isn't executable", name);
512 }
513
514 /* search the PATH, looking for name */
515 if((path = getenv("PATH")) == NULL)
516 path = _PATH_DEFPATH;
517
518 cur = alloca(strlen(path) + 1);
519 if(cur == NULL)
520 err(1, "alloca");
521 strcpy(cur, path);
522 while((p = strsep(&cur, ":")) != NULL) {
523 /*
524 * It's a SHELL path -- double, leading and trailing colons
525 * mean the current directory.
526 */
527 if(*p == '\0') {
528 p = ".";
529 lp = 1;
530 } else
531 lp = strlen(p);
532
533 /*
534 * If the path is too long complain. This is a possible
535 * security issue; given a way to make the path too long
536 * the user may execute the wrong program.
537 */
538 if(lp + ln + 2 > sizeof(buf)) {
539 warn("%s: path too long", p);
540 continue;
541 }
542 bcopy(p, buf, lp);
543 buf[lp] = '/';
544 bcopy(name, buf + lp + 1, ln);
545 buf[lp + ln + 1] = '\0';
546 if(stat(buf, &sb) == 0 && S_ISREG(sb.st_mode) && access(buf, X_OK) == 0) {
547 bp = buf;
548 break;
549 }
550 }
551 if(p == NULL)
552 errx(1, "Can't find %s in PATH", name);
553 } while(0); /* end block */
554 if(realpath(bp, rpbuf) == NULL)
555 errx(1, "realpath failed on %s", bp);
556 return (strcmp(rpbuf, "/usr/bin/" ARCH_PROG) == 0);
557}
558
559/*
560 * spawnFromArgs - called when arch has arguments specified. The arch command
561 * line arguments are:
562 * % arch [[{-xxx | -arch xxx}]...] prog [arg]...
563 * where xxx is a cpu name, and the command to execute and its arguments follow.
564 * If no commandline cpu names are given, the environment variable
565 * ARCHPREFERENCE is used. This routine never returns.
566 */
8459d725
A
567
568#define MATCHARG(a,m) ({ \
569 const char *arg = *(a); \
570 if(arg[1] == '-') arg++; \
571 strcmp(arg, (m)) == 0; \
572})
573
574#define MATCHARGWITHVALUE(a,m,n,e) ({ \
575 const char *ret = NULL; \
576 const char *arg = *(a); \
577 if(arg[1] == '-') arg++; \
578 if(strcmp(arg, (m)) == 0) { \
579 if(*++(a) == NULL) { \
580 warnx(e); \
581 usage(1); \
582 } \
583 ret = *(a); \
584 } else if(strncmp(arg, (m), (n)) == 0 && arg[n] == '=') { \
585 ret = arg + (n) + 1; \
586 } \
587 ret; \
588})
589
590#define MAKEENVCOPY(e) \
591 if(!envCopy) { \
592 envCopy = _copyenv(environ); \
593 if(envCopy == NULL) \
594 errx(1, (e)); \
595 }
596
34d340d7 597static void __dead2
8459d725 598spawnFromArgs(CPU *cpu, char **argv)
34d340d7 599{
8459d725 600 const char *ap, *ret;
34d340d7 601
8459d725 602 /* process arguments */
34d340d7 603 for(argv++; *argv && **argv == '-'; argv++) {
8459d725
A
604 if((ret = MATCHARGWITHVALUE(argv, "-arch", 5, "-arch without architecture"))) {
605 ap = ret;
606 } else if(MATCHARG(argv, "-32")) {
607 ap = NATIVE_32;
608 if(!ap) {
609 native32seen = true;
610 archCount++;
611 continue;
612 }
613 } else if(MATCHARG(argv, "-64")) {
614 ap = NATIVE_64;
615 if(!ap) {
616 native64seen = true;
617 archCount++;
618 continue;
619 }
620 } else if(MATCHARG(argv, "-c")) {
621 free(envCopy);
622 envCopy = _copyenv(NULL); // create empty environment
623 if(!envCopy)
624 errx(1, "Out of memory processing -c");
625 continue;
626 } else if((ret = MATCHARGWITHVALUE(argv, "-d", 2, "-d without envname"))) {
627 MAKEENVCOPY("Out of memory processing -d");
628 _unsetenvp(ret, &envCopy, NULL);
629 continue;
630 } else if((ret = MATCHARGWITHVALUE(argv, "-e", 2, "-e without envname=value"))) {
631 MAKEENVCOPY("Out of memory processing -e");
632 const char *cp = strchr(ret, '=');
633 if(!cp) {
634 warnx("-e %s: no equal sign", ret);
34d340d7
A
635 usage(1);
636 }
8459d725
A
637 cp++; // skip to value
638 /*
639 * _setenvp() only uses the name before any equal sign found in
640 * the first argument.
641 */
642 _setenvp(ret, cp, 1, &envCopy, NULL);
643 continue;
644 } else if(MATCHARG(argv, "-h")) {
34d340d7 645 usage(0);
8459d725 646 } else {
34d340d7 647 ap = *argv + 1;
8459d725
A
648 if(*ap == '-') ap++;
649 }
34d340d7
A
650 addCPUbyname(cpu, ap);
651 }
652 if(cpu->errs)
653 exit(1);
654 if(!*argv || !**argv) {
655 warnx("No command to execute");
656 usage(1);
657 }
658 /* if the program is already a link to arch, then force execpath */
659 int needexecpath = wrapped(*argv);
660
661 /*
662 * If we don't have any architecutures, try ARCHPREFERENCE and plist
663 * files.
664 */
8459d725
A
665 if(archCount <= 0 || needexecpath)
666 spawnFromPreferences(cpu, needexecpath, argv); /* doesn't return */
34d340d7
A
667
668 /*
669 * Call posix_spawnp on the program name.
670 */
8459d725 671 spawnIt(cpu, 1, *argv, argv);
34d340d7
A
672}
673
674/*
675 * init - initializes the ArchDict dictionary from the values in initArches,
676 * and the CPU structure.
677 */
678static void
679init(CPU *cpu)
680{
681 ArchDict = [NSMutableDictionary dictionaryWithCapacity: 4];
682 if(!ArchDict)
683 errx(1, "Can't create NSMutableDictionary");
8459d725
A
684 const CPUTypes *cp;
685 for(cp = initArches; cp->arch; cp++) {
686 NSString *s = [NSString stringWithUTF8String: cp->arch];
687 if(!s)
688 errx(1, "Can't create NSString for %s", cp->arch);
689 NSNumber *n = [NSNumber numberWithInt: cp->cpu];
690 if(!n)
691 errx(1, "Can't create NSNumber for %s", cp->arch);
692 [ArchDict setObject: n forKey: s];
693 }
694 /*
695 * The valid architecture numbers above are one or greater. The ignore
696 * values are the negative of the index in the ignore array.
697 */
698 const Ignore *ip;
699 int i;
700 for(ip = ignore, i = 0; ip->arch; ip++, i--) { // i is negative of index
701 NSString *s = [NSString stringWithUTF8String: ip->arch];
34d340d7 702 if(!s)
8459d725
A
703 errx(1, "Can't create NSString for %s", ip->arch);
704 NSNumber *n = [NSNumber numberWithInt: i];
34d340d7 705 if(!n)
8459d725 706 errx(1, "Can't create NSNumber for %s", ip->arch);
34d340d7
A
707 [ArchDict setObject: n forKey: s];
708 }
709 initCPU(cpu);
710}
711
712/* the main() routine */
713int
8459d725 714main(int argc, char **argv)
34d340d7
A
715{
716 const char *prog = getprogname();
717 int my_name_is_arch;
718 CPU cpu;
719
720 if(strcmp(prog, MACHINE_PROG) == 0) {
721 if(argc > 1)
722 errx(-1, "no arguments accepted");
723 arch(0); /* the "machine" command was called */
724 } else if((my_name_is_arch = (strcmp(prog, ARCH_PROG) == 0))) {
725 if(argc == 1)
726 arch(1); /* the "arch" command with no arguments was called */
727 }
728
729 /* set up Objective C autorelease pool */
730 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
731 init(&cpu); /* initialize */
732
733 if(my_name_is_arch)
8459d725 734 spawnFromArgs(&cpu, argv);
34d340d7 735 else
8459d725 736 spawnFromPreferences(&cpu, 1, argv);
34d340d7
A
737 /* should never get here */
738 [pool release];
739 errx(1, "returned from spawn");
740}