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