]>
Commit | Line | Data |
---|---|---|
3d9156a7 A |
1 | /* |
2 | * Copyright (c) 2004 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 <stdbool.h> | |
25 | #include <stdlib.h> | |
26 | #include <stdio.h> | |
27 | #include <fcntl.h> | |
28 | #include <sys/types.h> | |
29 | #include <sys/mman.h> | |
30 | #include <sys/stat.h> | |
31 | #include <string.h> | |
32 | #include <pthread.h> | |
33 | ||
3d9156a7 A |
34 | /* Catastrophic errors only, including "out of memory" */ |
35 | static bool parse_error = false; | |
36 | static bool bootstrapping = false; | |
37 | static bool parsed = false; | |
38 | #define UNIX2003_DEFAULT_MODE true | |
39 | static bool unix2003_mode = UNIX2003_DEFAULT_MODE; | |
40 | ||
41 | static pthread_once_t threadsafe = PTHREAD_ONCE_INIT; | |
42 | ||
43 | /* There was once a lot of parsing and the ability to set diffrent commands | |
44 | to diffrent modes. That is gone, but some of the scaffolding remains */ | |
45 | ||
46 | static void check_env_var(void) { | |
47 | char *mode = getenv("COMMAND_MODE"); | |
48 | ||
49 | if (mode) { | |
50 | if (!strcasecmp(mode, "legacy")) { | |
51 | unix2003_mode = false; | |
52 | } else { | |
53 | if (!strcasecmp(mode, "unix2003")) { | |
54 | unix2003_mode = true; | |
55 | } else { | |
56 | parse_error = true; | |
57 | unix2003_mode = UNIX2003_DEFAULT_MODE; | |
58 | } | |
59 | } | |
60 | } | |
61 | } | |
62 | ||
63 | /* Function is expected to be something like libc/malloc for a libc call, | |
64 | or bin/date for command line utilities. Modes are currently: | |
65 | Legacy - pre-tiger behaviour, presumably UNIX2003 incompatable | |
66 | UNIX2003 - Unix 2003 spec compliant | |
67 | Bootstrap - only seen by (parts of) libc. The compat_mode system is | |
68 | still starting up. This will be seen while compat_mode parses it's | |
69 | config file, or reads the cache file, and only by libc functions it calls. | |
70 | Error - the conf file could not be parsed, either due to a severe | |
71 | syntax error, an I/O error, or an out of memory condition | |
72 | ||
73 | mode names are case insensitatave. You can use | ^ & and even ! | |
74 | but no () yet, and that stuff hasn't been tested much yet, nor | |
75 | has it been optimised. | |
76 | */ | |
77 | ||
78 | bool | |
79 | compat_mode(const char *function, const char *mode) { | |
80 | if (!parsed && !bootstrapping) { | |
81 | pthread_once(&threadsafe, check_env_var); | |
82 | parsed = true; | |
83 | } | |
84 | ||
85 | bool want2003 = !strcasecmp("unix2003", mode); | |
86 | ||
87 | if (want2003) { | |
88 | return unix2003_mode; | |
89 | } | |
90 | ||
91 | bool want_legacy = !strcasecmp("legacy", mode); | |
92 | ||
93 | if (want_legacy) { | |
94 | return !unix2003_mode; | |
95 | } | |
96 | ||
97 | bool want_bootstrap = !strcasecmp("bootstrap", mode); | |
98 | ||
99 | if (want_bootstrap) { | |
100 | return bootstrapping; | |
101 | } | |
102 | ||
103 | bool want_error = !strcasecmp("error", mode); | |
104 | ||
105 | if (want_error) { | |
106 | return parse_error; | |
107 | } | |
108 | ||
109 | char *op = NULL; | |
110 | ||
111 | if (op = strpbrk(mode, "!^&|")) { | |
112 | if (*op == '!') { | |
113 | if (op != mode) goto syn_error; | |
114 | return !compat_mode(function, mode +1); | |
115 | } | |
116 | ||
117 | /* XXX char tmp[] would be better for left_arg, but | |
118 | we are not sure what the max size should be... is | |
119 | alloca(3) frowned on? */ | |
120 | int left_sz = 1 + (op - mode); | |
121 | char *left_arg = malloc(left_sz); | |
122 | strlcpy(left_arg, mode, left_sz); | |
123 | bool left = compat_mode(function, left_arg); | |
124 | free(left_arg); | |
125 | bool right = compat_mode(function, op +1); | |
126 | ||
127 | /* XXX check leftOPright syntax errors */ | |
128 | ||
129 | switch(*op) { | |
130 | case '^': | |
131 | return left ^ right; | |
132 | case '&': | |
133 | return left && right; | |
134 | case '|': | |
135 | return left || right; | |
136 | default: | |
137 | goto syn_error; | |
138 | } | |
139 | } | |
140 | ||
141 | syn_error: | |
142 | fprintf(stderr, "invalid mode %s (while checking for %s)\n", | |
143 | mode, function); | |
144 | ||
145 | return false; | |
146 | } | |
147 | ||
148 | #ifdef SELF_TEST_COMPAT_MODE | |
149 | ||
150 | #include <assert.h> | |
151 | ||
152 | #define NEXPECTED 3 | |
153 | ||
154 | typedef struct { | |
155 | char *mode; | |
156 | /* [0] is unix2003 mode, [1] is legacy, [2] is Invalid_Mode */ | |
157 | bool expected[NEXPECTED]; | |
158 | } testcase; | |
159 | ||
160 | testcase cases[] = { | |
161 | { "unix2003", {true, false, true}}, | |
162 | { "legacy", {false, true, false}}, | |
163 | { "bootstrap", {false, false, false}}, | |
164 | { "unix2003|legacy", {true, true, true}}, | |
165 | { "unix2003&legacy", {false, false, false}}, | |
166 | { "unix2003|legacy|bootstrap", {true, true, true}}, | |
167 | { "unix2003&legacy&bootstrap", {false, false, false}}, | |
168 | { "!unix2003", {false, true, false}}, | |
169 | { "!legacy", {true, false, true}}, | |
170 | /* XXX ! with compound statments */ | |
171 | { "unix2003^bootstrap", {true, false, true}}, | |
172 | { "unix2003^legacy", {true, true, true}}, | |
173 | { "&unix2003", {false, false, false}}, | |
174 | { "unix2003&", {false, false, false}}, | |
175 | { "unix2003!legacy", {false, false, false}}, | |
176 | { "unix2003&error", {false, false, true}}, | |
177 | { "error", {false, false, true}}, | |
178 | { "error|unix2003", {true, false, true}}, | |
179 | { "error|legacy", {false, true, true}}, | |
180 | { "error&legacy", {false, false, false}}, | |
181 | }; | |
182 | ||
183 | int ncases = sizeof(cases)/sizeof(testcase); | |
184 | ||
185 | int | |
186 | main(int argc, char *argv[]) { | |
187 | int i, j; | |
188 | int failures = 0; | |
189 | char *settings[] = { "unix2003", "legacy", "Invalid Mode"}; | |
190 | ||
191 | assert(sizeof(settings) / sizeof(char *) == NEXPECTED); | |
192 | ||
193 | for(i = 0; i < NEXPECTED; ++i) { | |
194 | setenv("COMMAND_MODE", settings[i], 1); | |
195 | char *compat_env = getenv("COMMAND_MODE"); | |
196 | printf("$COMMAND_MODE = %s\n", compat_env); | |
197 | if (i != 0) { | |
198 | /* here we force COMMAND_MODE to be checked again, which | |
199 | is normally impossiable because check_env_var() is a static | |
200 | function, but really nothing except the test cases wants to | |
201 | try it anyway... */ | |
202 | check_env_var(); | |
203 | } | |
204 | for(j = 0; j < ncases; ++j) { | |
205 | bool ret = compat_mode("bin/compat_mode", cases[j].mode); | |
206 | bool expect = cases[j].expected[i]; | |
207 | ||
208 | if (expect != ret) { | |
209 | failures++; | |
210 | } | |
211 | ||
212 | printf("Case %s got %d expected %d%s\n", | |
213 | cases[j].mode, ret, expect, (ret == expect) ? "" : " FAILED"); | |
214 | } | |
215 | } | |
216 | ||
217 | /* We have ncases entries in cases[], but each is run multiple | |
218 | times (once per entry in the settings array) thus the multiply */ | |
219 | printf("Passed %d of %d cases\n", | |
220 | NEXPECTED*ncases - failures, NEXPECTED*ncases); | |
221 | ||
222 | return failures ? 1 : 0; | |
223 | } | |
224 | ||
225 | #endif |