]>
Commit | Line | Data |
---|---|---|
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> | |
27 | #include <stdlib.h> | |
28 | #include <stddef.h> | |
29 | #include <unistd.h> | |
30 | #include <spawn.h> | |
31 | #include <sys/types.h> | |
32 | #include <sys/stat.h> | |
33 | #include <sys/param.h> | |
34 | #include <paths.h> | |
35 | #include <err.h> | |
36 | #include <mach/mach.h> | |
37 | #include <mach-o/arch.h> | |
38 | #include <Foundation/Foundation.h> | |
39 | ||
40 | #ifndef ARCH_PROG | |
41 | #define ARCH_PROG "arch" | |
42 | #endif | |
43 | #define CPUDELTA 10 | |
44 | #ifndef MACHINE_PROG | |
45 | #define MACHINE_PROG "machine" | |
46 | #endif | |
47 | ||
48 | #define CPUCOUNT(c) ((c)->ptr - (c)->buf) | |
49 | ||
50 | static NSMutableDictionary *ArchDict; | |
51 | static NSString *KeyExecPath = @"ExecutablePath"; | |
52 | static NSString *KeyPlistVersion = @"PropertyListVersion"; | |
53 | static NSString *KeyPrefOrder = @"PreferredOrder"; | |
54 | static NSString *PlistExtension = @"plist"; | |
55 | static NSString *SettingsDir = @"archSettings"; | |
56 | ||
57 | static const char envname[] = "ARCHPREFERENCE"; | |
58 | ||
59 | typedef struct { | |
60 | cpu_type_t *buf; | |
61 | cpu_type_t *ptr; | |
62 | cpu_type_t *end; | |
63 | int errs; | |
64 | } CPU; | |
65 | ||
66 | typedef struct { | |
67 | char *str; | |
68 | int i; | |
69 | } StrInt; | |
70 | ||
71 | static StrInt initArches[] = { | |
72 | {"i386", CPU_TYPE_I386}, | |
73 | {"ppc", CPU_TYPE_POWERPC}, | |
74 | {"ppc64", CPU_TYPE_POWERPC64}, | |
75 | {"x86_64", CPU_TYPE_X86_64}, | |
76 | {NULL, 0} | |
77 | }; | |
78 | ||
79 | /* | |
80 | * arch - perform the original behavior of the arch and machine commands. | |
81 | * The archcmd flag is non-zero for the arch command, zero for the machine | |
82 | * command. This routine never returns. | |
83 | */ | |
84 | static void __dead2 | |
85 | arch(int archcmd) | |
86 | { | |
87 | const NXArchInfo *arch = NXGetLocalArchInfo(); | |
88 | ||
89 | if(!arch) | |
90 | errx(-1, "Unknown architecture."); | |
91 | if(archcmd) { | |
92 | arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE); | |
93 | if(!arch) | |
94 | errx(-1, "Unknown architecture."); | |
95 | } | |
96 | printf("%s%s", arch->name, (isatty(STDIN_FILENO) ? "\n" : "")); | |
97 | exit(0); | |
98 | } | |
99 | ||
100 | /* | |
101 | * spawnIt - run the posix_spawn command. count is the number of CPU types | |
102 | * in the prefs array. pflag is non-zero to call posix_spawnp; zero means to | |
103 | * call posix_spawn. str is the name/path to pass to posix_spawn{,p}, and | |
104 | * argv and environ are the argument and environment arrays to pass. This | |
105 | * routine never returns. | |
106 | */ | |
107 | static void __dead2 | |
108 | spawnIt(int count, cpu_type_t *prefs, int pflag, const char *str, char **argv, char **environ) | |
109 | { | |
110 | ||
111 | posix_spawnattr_t attr; | |
112 | pid_t pid; | |
113 | int ret; | |
114 | size_t copied; | |
115 | ||
116 | if((ret = posix_spawnattr_init(&attr)) != 0) | |
117 | errc(1, ret, "posix_spawnattr_init"); | |
118 | /* do the equivalent of exec, rather than creating a separate process */ | |
119 | if((ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC)) != 0) | |
120 | errc(1, ret, "posix_spawnattr_setflags"); | |
121 | if((ret = posix_spawnattr_setbinpref_np(&attr, count, prefs, &copied)) != 0) | |
122 | errc(1, ret, "posix_spawnattr_setbinpref_np"); | |
123 | if(copied != count) | |
124 | errx(1, "posix_spawnattr_setbinpref_np only copied %d of %d", (int)copied, count); | |
125 | if(pflag) | |
126 | ret = posix_spawnp(&pid, str, NULL, &attr, argv, environ); | |
127 | else | |
128 | ret = posix_spawn(&pid, str, NULL, &attr, argv, environ); | |
129 | errc(1, ret, "posix_spawn%s: %s", (pflag ? "p" : ""), str); | |
130 | } | |
131 | ||
132 | /* | |
133 | * initCPU - initialize a CPU structure, a dynamically expanding CPU types | |
134 | * array. | |
135 | */ | |
136 | static void | |
137 | initCPU(CPU *cpu) | |
138 | { | |
139 | cpu->buf = (cpu_type_t *)malloc(CPUDELTA * sizeof(cpu_type_t)); | |
140 | if(!cpu->buf) | |
141 | err(1, "Failed to malloc CPU buffer"); | |
142 | cpu->ptr = cpu->buf; | |
143 | cpu->end = cpu->buf + CPUDELTA; | |
144 | cpu->errs = 0; | |
145 | } | |
146 | ||
147 | /* | |
148 | * addCPU - add a new CPU type value to the CPU structure, expanding | |
149 | * the array as necessary. | |
150 | */ | |
151 | static void | |
152 | addCPU(CPU *cpu, int n) | |
153 | { | |
154 | if(cpu->ptr >= cpu->end) { | |
155 | cpu_type_t *new = realloc(cpu->buf, (cpu->end - cpu->buf + CPUDELTA) * sizeof(cpu_type_t)); | |
156 | if(!new) | |
157 | err(1, "Out of memory realloc-ing CPU structure"); | |
158 | ptrdiff_t diff = (void *)new - (void *)cpu->buf; | |
159 | cpu->buf = new; | |
160 | cpu->ptr = (cpu_type_t *)((void *)cpu->ptr + diff); | |
161 | cpu->end = (cpu_type_t *)((void *)cpu->end + diff) + CPUDELTA; | |
162 | } | |
163 | *cpu->ptr++ = n; | |
164 | } | |
165 | ||
166 | /* | |
167 | * addCPUbyname - add a new CPU type, given by name, to the CPU structure, | |
168 | * expanding the array as necessary. The name is converted to a type value | |
169 | * by the ArchDict dictionary. | |
170 | */ | |
171 | static void | |
172 | addCPUbyname(CPU *cpu, const char *name) | |
173 | { | |
174 | NSNumber *n = (NSNumber *)[ArchDict objectForKey: [[NSString stringWithUTF8String: name] lowercaseString]]; | |
175 | if(n == nil) { | |
176 | warnx("Unknown architecture: %s", name); | |
177 | cpu->errs++; | |
178 | return; | |
179 | } | |
180 | addCPU(cpu, [n intValue]); | |
181 | } | |
182 | ||
183 | /* | |
184 | * useEnv - parse the environment variable for CPU preferences. Use name | |
185 | * to look for program-specific preferences, and append any CPU types to cpu. | |
186 | * Returns the number of CPU types. Returns any specified execute path in | |
187 | * execpath. | |
188 | * | |
189 | * The environment variable ARCHPREFERENCE has the format: | |
190 | * spec[;spec]... | |
191 | * a semicolon separated list of specifiers. Each specifier has the format: | |
192 | * [prog:[execpath:]]type[,type]... | |
193 | * a comma separate list of CPU type names, optionally proceeded by a program | |
194 | * name and an execpath. If program name exist, that types only apply to that | |
195 | * program. If execpath is specified, it is returned. If no program name | |
196 | * exists, then it applies to all programs. So ordering of the specifiers is | |
197 | * important, as the default (no program name) specifier must be last. | |
198 | */ | |
199 | static int | |
200 | useEnv(CPU *cpu, const char *name, char **execpath) | |
201 | { | |
202 | char *val = getenv(envname); | |
203 | if(!val) | |
204 | return 0; | |
205 | ||
206 | /* cp will point to the basename of name */ | |
207 | const char *cp = strrchr(name, '/'); | |
208 | if(cp) { | |
209 | cp++; | |
210 | if(!*cp) | |
211 | errx(1, "%s: no name after last slash", name); | |
212 | } else | |
213 | cp = name; | |
214 | /* make a copy of the environment variable value, so we can modify it */ | |
215 | val = strdup(val); | |
216 | if(!val) | |
217 | err(1, "Can't copy environment %s", envname); | |
218 | char *str = val; | |
219 | char *blk; | |
220 | /* for each specifier */ | |
221 | while((blk = strsep(&str, ";")) != NULL) { | |
222 | if(*blk == 0) | |
223 | continue; /* two adjacent semicolons */ | |
224 | /* now split on colons */ | |
225 | char *n = strsep(&blk, ":"); | |
226 | if(blk) { | |
227 | char *p = strsep(&blk, ":"); | |
228 | if(!blk) { /* there is only one colon, so no execpath */ | |
229 | blk = p; | |
230 | p = NULL; | |
231 | } else if(!*p) /* two consecutive colons, so no execpath */ | |
232 | p = NULL; | |
233 | if(!*blk) | |
234 | continue; /* no cpu list, so skip */ | |
235 | /* if the name matches, or there is no name, process the cpus */ | |
236 | if(!*n || strcmp(n, cp) == 0) { | |
237 | if(CPUCOUNT(cpu) <= 0) { /* only if we don't already have cpu types */ | |
238 | char *t; | |
239 | while((t = strsep(&blk, ",")) != NULL) | |
240 | addCPUbyname(cpu, t); | |
241 | } | |
242 | *execpath = (*n ? p : NULL); /* only use the exec path is name is set */ | |
243 | break; | |
244 | } | |
245 | } else { /* no colons at all, so process as default */ | |
246 | if(CPUCOUNT(cpu) <= 0) { /* only if we don't already have cpu types */ | |
247 | blk = n; | |
248 | while((n = strsep(&blk, ",")) != NULL) | |
249 | addCPUbyname(cpu, n); | |
250 | } | |
251 | *execpath = NULL; | |
252 | break; | |
253 | } | |
254 | } | |
255 | if(cpu->errs) /* errors during addCPUbyname are fatal */ | |
256 | exit(1); | |
257 | return CPUCOUNT(cpu); /* return count of cpus */ | |
258 | } | |
259 | ||
260 | /* | |
261 | * spawnFromPreference - called when argv[0] is not "arch" or "machine", or | |
262 | * argv[0] was arch, but no commandline architectures were specified. | |
263 | * If the environment variable ARCHPREFERENCE is specified, and there is a | |
264 | * match to argv[0], use the specified cpu preferences. If no exec path | |
265 | * is specified in ARCHPREFERENCE, or no match is found in ARCHPREFERENCE, | |
266 | * get any additional information from a .plist file with the name of argv[0]. | |
267 | * This routine never returns. | |
268 | */ | |
269 | static void __dead2 | |
270 | spawnFromPreferences(CPU *cpu, int needexecpath, char **argv, char **environ) | |
271 | { | |
272 | char *epath = NULL; | |
273 | int count; | |
274 | const char *prog = strrchr(*argv, '/'); | |
275 | if(prog) | |
276 | prog++; | |
277 | else | |
278 | prog = *argv; | |
279 | if(!*prog) | |
280 | errx(1, "Not program name specified"); | |
281 | ||
282 | /* check the environment variable first */ | |
283 | if((count = useEnv(cpu, prog, &epath)) > 0) { | |
284 | /* if we were called as arch, use posix_spawnp */ | |
285 | if(!needexecpath) | |
286 | spawnIt(count, cpu->buf, 1, (epath ? epath : *argv), argv, environ); | |
287 | /* otherwise, if we have the executable path, call posix_spawn */ | |
288 | if(epath) | |
289 | spawnIt(count, cpu->buf, 0, epath, argv, environ); | |
290 | } | |
291 | ||
292 | /* pathArray is use to build the .plist file path for each domain */ | |
293 | NSMutableArray *pathArray = [NSMutableArray arrayWithCapacity: 3]; | |
294 | if(!pathArray) | |
295 | errx(1, "Can't create NSMutableArray"); | |
296 | [pathArray addObject: @""]; // placeholder for domain directory | |
297 | [pathArray addObject: SettingsDir]; | |
298 | [pathArray addObject: [[NSString stringWithUTF8String: prog] stringByAppendingPathExtension: PlistExtension]]; | |
299 | ||
300 | /* get the list of top level directories for all domains */ | |
301 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask, true); | |
302 | int cnt = [paths count]; | |
303 | if(!cnt) | |
304 | errx(1, "No domains"); | |
305 | ||
306 | /* create the .plist path, and try to read it */ | |
307 | int i; | |
308 | NSString *path = NULL; | |
309 | NSData *data = NULL; | |
310 | for(i = 0; i < cnt; i++) { | |
311 | [pathArray replaceObjectAtIndex: 0 withObject: [paths objectAtIndex: i]]; | |
312 | path = [NSString pathWithComponents: pathArray]; | |
313 | data = [NSData dataWithContentsOfFile: path]; | |
314 | if(data) /* found it */ | |
315 | break; | |
316 | } | |
317 | if(!data) | |
318 | errx(1, "Can't find any plists for %s", prog); | |
319 | ||
320 | /* try to convert the file into a NSDictionary */ | |
321 | NSString *error; | |
322 | id plist = [NSPropertyListSerialization propertyListFromData: data mutabilityOption: NSPropertyListImmutable format: nil errorDescription: &error]; | |
323 | if(!plist) | |
324 | errx(1, "%s: NSPropertyListSerialization error: %s", [path UTF8String], [error UTF8String]); | |
325 | if(![plist isKindOfClass: [NSDictionary class]]) | |
326 | errx(1, "%s: plist not a dictionary", [path UTF8String]); | |
327 | ||
328 | NSString *execpath; | |
329 | int errs = 0; /* scan for all errors and fail later */ | |
330 | do { /* begin block */ | |
331 | /* check the plist version */ | |
332 | NSString *vers = [(NSDictionary *)plist objectForKey: KeyPlistVersion]; | |
333 | if(!vers) { | |
334 | warnx("%s: No key %s", [path UTF8String], [KeyPlistVersion UTF8String]); | |
335 | errs++; | |
336 | } else if(![vers isKindOfClass: [NSString class]]) { | |
337 | warnx("%s: %s is not a string", [path UTF8String], [KeyPlistVersion UTF8String]); | |
338 | errs++; | |
339 | } else if(![vers isEqualToString: @"1.0"]) { | |
340 | warnx("%s: %s not 1.0", [path UTF8String], [KeyPlistVersion UTF8String]); | |
341 | errs++; | |
342 | } | |
343 | /* get the execpath */ | |
344 | execpath = [(NSDictionary *)plist objectForKey: KeyExecPath]; | |
345 | if(!execpath) { | |
346 | warnx("%s: No key %s", [path UTF8String], [KeyExecPath UTF8String]); | |
347 | errs++; | |
348 | } else if(![execpath isKindOfClass: [NSString class]]) { | |
349 | warnx("%s: %s is not a string", [path UTF8String], [KeyExecPath UTF8String]); | |
350 | errs++; | |
351 | } | |
352 | /* if we already got cpu preferences from ARCHPREFERENCE, we are done */ | |
353 | if(count > 0) | |
354 | break; | |
355 | /* otherwise, parse the cpu preferences from the plist */ | |
356 | id p = [(NSDictionary *)plist objectForKey: KeyPrefOrder]; | |
357 | if(!p) { | |
358 | warnx("%s: No key %s", [path UTF8String], [KeyPrefOrder UTF8String]); | |
359 | errs++; | |
360 | } else if(![p isKindOfClass: [NSArray class]]) { | |
361 | warnx("%s: %s is not an array", [path UTF8String], [KeyPrefOrder UTF8String]); | |
362 | errs++; | |
363 | } else if((count = [p count]) == 0) { | |
364 | warnx("%s: no entries in %s", [path UTF8String], [KeyPrefOrder UTF8String]); | |
365 | errs++; | |
366 | } else { | |
367 | /* finally but the cpu type array */ | |
368 | for(i = 0; i < count; i++) { | |
369 | id a = [(NSArray *)p objectAtIndex: i]; | |
370 | NSNumber *n; | |
371 | if(![a isKindOfClass: [NSString class]]) { | |
372 | warnx("%s: entries %d of %s is not a string", [path UTF8String], i, [KeyPrefOrder UTF8String]); | |
373 | errs++; | |
374 | } else if((n = (NSNumber *)[ArchDict objectForKey: [(NSString *)a lowercaseString]]) == nil) { | |
375 | warnx("%s: %s: unknown architecture %s", [path UTF8String], [KeyPrefOrder UTF8String], [(NSString *)a UTF8String]); | |
376 | errs++; | |
377 | } else { | |
378 | addCPU(cpu, [n intValue]); | |
379 | } | |
380 | } | |
381 | } | |
382 | } while(0); /* end block */ | |
383 | if(errs) /* exit if there were any reported errors */ | |
384 | exit(1); | |
385 | ||
386 | /* call posix_spawn */ | |
387 | spawnIt(count, cpu->buf, 0, [execpath fileSystemRepresentation], argv, environ); | |
388 | } | |
389 | ||
390 | static void __dead2 | |
391 | usage(int ret) | |
392 | { | |
393 | fprintf(stderr, | |
394 | "Usage: %s\n" | |
395 | " Display the machine's architecture type\n" | |
396 | "Usage: %s [-h] [[-arch_name | -arch arch_name] ...] prog [arg ...]\n" | |
397 | " Run prog with any arguments, using the given architecture\n" | |
398 | " order. If no architectures are specified, use the\n" | |
399 | " ARCHPREFERENCE environment variable, or a property list file.\n" | |
400 | " -h will print usage message and exit.\n", | |
401 | ARCH_PROG, ARCH_PROG); | |
402 | exit(ret); | |
403 | } | |
404 | ||
405 | /* | |
406 | * wrapped - check the path to see if it is a link to /usr/bin/arch. | |
407 | */ | |
408 | static int | |
409 | wrapped(const char *name) | |
410 | { | |
411 | int lp, ln; | |
412 | char *p; | |
413 | char *bp = NULL; | |
414 | char *cur, *path; | |
415 | char buf[MAXPATHLEN], rpbuf[MAXPATHLEN]; | |
416 | struct stat sb; | |
417 | ||
418 | ln = strlen(name); | |
419 | ||
420 | do { /* begin block */ | |
421 | /* If it's an absolute or relative path name, it's easy. */ | |
422 | if(index(name, '/')) { | |
423 | if(stat(name, &sb) == 0 && S_ISREG(sb.st_mode) && access(name, X_OK) == 0) { | |
424 | bp = (char *)name; | |
425 | break; | |
426 | } | |
427 | errx(1, "%s isn't executable", name); | |
428 | } | |
429 | ||
430 | /* search the PATH, looking for name */ | |
431 | if((path = getenv("PATH")) == NULL) | |
432 | path = _PATH_DEFPATH; | |
433 | ||
434 | cur = alloca(strlen(path) + 1); | |
435 | if(cur == NULL) | |
436 | err(1, "alloca"); | |
437 | strcpy(cur, path); | |
438 | while((p = strsep(&cur, ":")) != NULL) { | |
439 | /* | |
440 | * It's a SHELL path -- double, leading and trailing colons | |
441 | * mean the current directory. | |
442 | */ | |
443 | if(*p == '\0') { | |
444 | p = "."; | |
445 | lp = 1; | |
446 | } else | |
447 | lp = strlen(p); | |
448 | ||
449 | /* | |
450 | * If the path is too long complain. This is a possible | |
451 | * security issue; given a way to make the path too long | |
452 | * the user may execute the wrong program. | |
453 | */ | |
454 | if(lp + ln + 2 > sizeof(buf)) { | |
455 | warn("%s: path too long", p); | |
456 | continue; | |
457 | } | |
458 | bcopy(p, buf, lp); | |
459 | buf[lp] = '/'; | |
460 | bcopy(name, buf + lp + 1, ln); | |
461 | buf[lp + ln + 1] = '\0'; | |
462 | if(stat(buf, &sb) == 0 && S_ISREG(sb.st_mode) && access(buf, X_OK) == 0) { | |
463 | bp = buf; | |
464 | break; | |
465 | } | |
466 | } | |
467 | if(p == NULL) | |
468 | errx(1, "Can't find %s in PATH", name); | |
469 | } while(0); /* end block */ | |
470 | if(realpath(bp, rpbuf) == NULL) | |
471 | errx(1, "realpath failed on %s", bp); | |
472 | return (strcmp(rpbuf, "/usr/bin/" ARCH_PROG) == 0); | |
473 | } | |
474 | ||
475 | /* | |
476 | * spawnFromArgs - called when arch has arguments specified. The arch command | |
477 | * line arguments are: | |
478 | * % arch [[{-xxx | -arch xxx}]...] prog [arg]... | |
479 | * where xxx is a cpu name, and the command to execute and its arguments follow. | |
480 | * If no commandline cpu names are given, the environment variable | |
481 | * ARCHPREFERENCE is used. This routine never returns. | |
482 | */ | |
483 | static void __dead2 | |
484 | spawnFromArgs(CPU *cpu, char **argv, char **environ) | |
485 | { | |
486 | char *ap; | |
487 | ||
488 | /* process cpu options */ | |
489 | for(argv++; *argv && **argv == '-'; argv++) { | |
490 | if(strcmp(*argv, "-arch") == 0) { | |
491 | if(*++argv == NULL) { | |
492 | warnx("-arch without architecture"); | |
493 | usage(1); | |
494 | } | |
495 | ap = *argv; | |
496 | } else if(strcmp(*argv, "-h") == 0) { | |
497 | usage(0); | |
498 | } else | |
499 | ap = *argv + 1; | |
500 | addCPUbyname(cpu, ap); | |
501 | } | |
502 | if(cpu->errs) | |
503 | exit(1); | |
504 | if(!*argv || !**argv) { | |
505 | warnx("No command to execute"); | |
506 | usage(1); | |
507 | } | |
508 | /* if the program is already a link to arch, then force execpath */ | |
509 | int needexecpath = wrapped(*argv); | |
510 | ||
511 | /* | |
512 | * If we don't have any architecutures, try ARCHPREFERENCE and plist | |
513 | * files. | |
514 | */ | |
515 | int count = CPUCOUNT(cpu); | |
516 | if(count <= 0 || needexecpath) | |
517 | spawnFromPreferences(cpu, needexecpath, argv, environ); /* doesn't return */ | |
518 | ||
519 | /* | |
520 | * Call posix_spawnp on the program name. | |
521 | */ | |
522 | spawnIt(count, cpu->buf, 1, *argv, argv, environ); | |
523 | } | |
524 | ||
525 | /* | |
526 | * init - initializes the ArchDict dictionary from the values in initArches, | |
527 | * and the CPU structure. | |
528 | */ | |
529 | static void | |
530 | init(CPU *cpu) | |
531 | { | |
532 | ArchDict = [NSMutableDictionary dictionaryWithCapacity: 4]; | |
533 | if(!ArchDict) | |
534 | errx(1, "Can't create NSMutableDictionary"); | |
535 | StrInt *sp; | |
536 | for(sp = initArches; sp->str; sp++) { | |
537 | NSString *s = [NSString stringWithUTF8String: sp->str]; | |
538 | if(!s) | |
539 | errx(1, "Can't create NSString for %s", sp->str); | |
540 | NSNumber *n = [NSNumber numberWithInt: sp->i]; | |
541 | if(!n) | |
542 | errx(1, "Can't create NSNumber for %s", sp->str); | |
543 | [ArchDict setObject: n forKey: s]; | |
544 | } | |
545 | initCPU(cpu); | |
546 | } | |
547 | ||
548 | /* the main() routine */ | |
549 | int | |
550 | main(int argc, char **argv, char **environ) | |
551 | { | |
552 | const char *prog = getprogname(); | |
553 | int my_name_is_arch; | |
554 | CPU cpu; | |
555 | ||
556 | if(strcmp(prog, MACHINE_PROG) == 0) { | |
557 | if(argc > 1) | |
558 | errx(-1, "no arguments accepted"); | |
559 | arch(0); /* the "machine" command was called */ | |
560 | } else if((my_name_is_arch = (strcmp(prog, ARCH_PROG) == 0))) { | |
561 | if(argc == 1) | |
562 | arch(1); /* the "arch" command with no arguments was called */ | |
563 | } | |
564 | ||
565 | /* set up Objective C autorelease pool */ | |
566 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
567 | init(&cpu); /* initialize */ | |
568 | ||
569 | if(my_name_is_arch) | |
570 | spawnFromArgs(&cpu, argv, environ); | |
571 | else | |
572 | spawnFromPreferences(&cpu, 1, argv, environ); | |
573 | /* should never get here */ | |
574 | [pool release]; | |
575 | errx(1, "returned from spawn"); | |
576 | } |