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/* Catastrophic errors only, including "out of memory" */ 35static bool parse_error = false; 36static bool bootstrapping = false; 37static bool parsed = false; 38#define UNIX2003_DEFAULT_MODE true 39static bool unix2003_mode = UNIX2003_DEFAULT_MODE; 40 41static 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 46static 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, 64or 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 78bool 79compat_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 size_t 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 141syn_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 154typedef struct { 155 char *mode; 156 /* [0] is unix2003 mode, [1] is legacy, [2] is Invalid_Mode */ 157 bool expected[NEXPECTED]; 158} testcase; 159 160testcase 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 183int ncases = sizeof(cases)/sizeof(testcase); 184 185int 186main(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 226