#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
static bool parse_error = false;
static bool bootstrapping = false;
static bool parsed = false;
#define UNIX2003_DEFAULT_MODE true
static bool unix2003_mode = UNIX2003_DEFAULT_MODE;
static pthread_once_t threadsafe = PTHREAD_ONCE_INIT;
static void check_env_var(void) {
char *mode = getenv("COMMAND_MODE");
if (mode) {
if (!strcasecmp(mode, "legacy")) {
unix2003_mode = false;
} else {
if (!strcasecmp(mode, "unix2003")) {
unix2003_mode = true;
} else {
parse_error = true;
unix2003_mode = UNIX2003_DEFAULT_MODE;
}
}
}
}
bool
compat_mode(const char *function, const char *mode) {
if (!parsed && !bootstrapping) {
pthread_once(&threadsafe, check_env_var);
parsed = true;
}
bool want2003 = !strcasecmp("unix2003", mode);
if (want2003) {
return unix2003_mode;
}
bool want_legacy = !strcasecmp("legacy", mode);
if (want_legacy) {
return !unix2003_mode;
}
bool want_bootstrap = !strcasecmp("bootstrap", mode);
if (want_bootstrap) {
return bootstrapping;
}
bool want_error = !strcasecmp("error", mode);
if (want_error) {
return parse_error;
}
char *op = NULL;
if (op = strpbrk(mode, "!^&|")) {
if (*op == '!') {
if (op != mode) goto syn_error;
return !compat_mode(function, mode +1);
}
int left_sz = 1 + (op - mode);
char *left_arg = malloc(left_sz);
strlcpy(left_arg, mode, left_sz);
bool left = compat_mode(function, left_arg);
free(left_arg);
bool right = compat_mode(function, op +1);
switch(*op) {
case '^':
return left ^ right;
case '&':
return left && right;
case '|':
return left || right;
default:
goto syn_error;
}
}
syn_error:
fprintf(stderr, "invalid mode %s (while checking for %s)\n",
mode, function);
return false;
}
#ifdef SELF_TEST_COMPAT_MODE
#include <assert.h>
#define NEXPECTED 3
typedef struct {
char *mode;
bool expected[NEXPECTED];
} testcase;
testcase cases[] = {
{ "unix2003", {true, false, true}},
{ "legacy", {false, true, false}},
{ "bootstrap", {false, false, false}},
{ "unix2003|legacy", {true, true, true}},
{ "unix2003&legacy", {false, false, false}},
{ "unix2003|legacy|bootstrap", {true, true, true}},
{ "unix2003&legacy&bootstrap", {false, false, false}},
{ "!unix2003", {false, true, false}},
{ "!legacy", {true, false, true}},
{ "unix2003^bootstrap", {true, false, true}},
{ "unix2003^legacy", {true, true, true}},
{ "&unix2003", {false, false, false}},
{ "unix2003&", {false, false, false}},
{ "unix2003!legacy", {false, false, false}},
{ "unix2003&error", {false, false, true}},
{ "error", {false, false, true}},
{ "error|unix2003", {true, false, true}},
{ "error|legacy", {false, true, true}},
{ "error&legacy", {false, false, false}},
};
int ncases = sizeof(cases)/sizeof(testcase);
int
main(int argc, char *argv[]) {
int i, j;
int failures = 0;
char *settings[] = { "unix2003", "legacy", "Invalid Mode"};
assert(sizeof(settings) / sizeof(char *) == NEXPECTED);
for(i = 0; i < NEXPECTED; ++i) {
setenv("COMMAND_MODE", settings[i], 1);
char *compat_env = getenv("COMMAND_MODE");
printf("$COMMAND_MODE = %s\n", compat_env);
if (i != 0) {
check_env_var();
}
for(j = 0; j < ncases; ++j) {
bool ret = compat_mode("bin/compat_mode", cases[j].mode);
bool expect = cases[j].expected[i];
if (expect != ret) {
failures++;
}
printf("Case %s got %d expected %d%s\n",
cases[j].mode, ret, expect, (ret == expect) ? "" : " FAILED");
}
}
printf("Passed %d of %d cases\n",
NEXPECTED*ncases - failures, NEXPECTED*ncases);
return failures ? 1 : 0;
}
#endif