2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
28 #include <sys/types.h>
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
;
41 static pthread_once_t threadsafe
= PTHREAD_ONCE_INIT
;
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 */
46 static void check_env_var(void) {
47 char *mode
= getenv("COMMAND_MODE");
50 if (!strcasecmp(mode
, "legacy")) {
51 unix2003_mode
= false;
53 if (!strcasecmp(mode
, "unix2003")) {
57 unix2003_mode
= UNIX2003_DEFAULT_MODE
;
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
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.
79 compat_mode(const char *function
, const char *mode
) {
80 if (!parsed
&& !bootstrapping
) {
81 pthread_once(&threadsafe
, check_env_var
);
85 bool want2003
= !strcasecmp("unix2003", mode
);
91 bool want_legacy
= !strcasecmp("legacy", mode
);
94 return !unix2003_mode
;
97 bool want_bootstrap
= !strcasecmp("bootstrap", mode
);
100 return bootstrapping
;
103 bool want_error
= !strcasecmp("error", mode
);
111 if (op
= strpbrk(mode
, "!^&|")) {
113 if (op
!= mode
) goto syn_error
;
114 return !compat_mode(function
, mode
+1);
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
);
125 bool right
= compat_mode(function
, op
+1);
127 /* XXX check leftOPright syntax errors */
133 return left
&& right
;
135 return left
|| right
;
142 fprintf(stderr
, "invalid mode %s (while checking for %s)\n",
148 #ifdef SELF_TEST_COMPAT_MODE
156 /* [0] is unix2003 mode, [1] is legacy, [2] is Invalid_Mode */
157 bool expected
[NEXPECTED
];
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}},
183 int ncases
= sizeof(cases
)/sizeof(testcase
);
186 main(int argc
, char *argv
[]) {
189 char *settings
[] = { "unix2003", "legacy", "Invalid Mode"};
191 assert(sizeof(settings
) / sizeof(char *) == NEXPECTED
);
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
);
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
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
];
212 printf("Case %s got %d expected %d%s\n",
213 cases
[j
].mode
, ret
, expect
, (ret
== expect
) ? "" : " FAILED");
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
);
222 return failures
? 1 : 0;