Goto sanos source index
//
// test.c
//
// Evaluate expression
//
// Copyright (C) 2012 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <shlib.h>
#include <unistd.h>
#include <sys/stat.h>
struct parser {
char **argv;
int pos;
char *current;
};
static int parse_expr(struct parser *p);
int filemode(char *filename) {
struct stat st;
if (stat(filename, &st) < 0) return 0;
return st.st_mode;
}
int filesize(char *filename) {
struct stat st;
if (stat(filename, &st) < 0) return -1;
return st.st_size;
}
static next(struct parser *p) {
if (p->current) p->current = p->argv[p->pos++];
}
static int match(struct parser *p, char *token) {
return p->current && strcmp(p->current, token) == 0;
}
static int parse_term(struct parser *p) {
int n;
char *op;
char *arg1;
char *arg2;
int cmp;
if (!p->current) {
// Empty expression evaluates to false
return 0;
} else if (match(p, "(")) {
// Parse expression in parentheses
next(p);
n = parse_expr(p);
if (!match(p, ")")) {
fprintf(stderr, "Unmatched parentheses\n");
return -1;
}
next(p);
return n;
} else if (match(p, "!")) {
// Parse negation
next(p);
n = parse_expr(p);
return n < 0 ? n : !n;
} else if (*p->current == '-') {
// Parse unary operator
op = p->current;
next(p);
arg1 = p->current;
if (!arg1) return -1;
next(p);
if (strlen(op) != 2) {
fprintf(stderr, "%s: unary operator expected\n", op);
return -1;
}
switch (op[1]) {
case 'b': // File exists and is block device
return S_ISBLK(filemode(arg1));
case 'c': // File exists and is character device
return S_ISCHR(filemode(arg1));
case 'd': // File exists and is a directory
return S_ISDIR(filemode(arg1));
case 'e': // File exists
return access(arg1, F_OK);
case 'f': // File exists and is a regular file
return S_ISREG(filemode(arg1));
case 'g': // File exists and is has set-group-id
return 0;
case 'h': // File exists and is a symbolic link
return 0;
case 'n': // Length of string is non-zero
return *arg1 != 0;
case 'p': // File exists and is a FIFO
return S_ISFIFO(filemode(arg1));
case 'r': // File exists and is readable
return access(arg1, R_OK) == 0;
case 's': // File exists and has non-zero size
return filesize(arg1) > 0;
case 't': // File exists and is a terminal
return 0;
case 'u': // File exists and is has set-user-id
return 0;
case 'w': // File exists and is writable
return access(arg1, W_OK) == 0;
case 'x': // File exists and is executable
return access(arg1, X_OK) == 0;
case 'z': // Length of string is zero
return *arg1 == 0;
case 'L': // File exists and is a symbolic link
return 0;
case 'S': // File exists and is a socket
return S_ISSOCK(filemode(arg1));
default:
fprintf(stderr, "%s: unary operator expected\n", op);
return -1;
}
} else {
// Parse binary operator
arg1 = p->current;
next(p);
op = p->current;
next(p);
arg2 = p->current;
next(p);
if (!arg1 || !op || !arg2) return -1;
cmp = strcmp(arg1, arg2);
if (strcmp(op, "=") == 0) {
return strcmp(arg1, arg2) == 0;
} else if (strcmp(op, "!=") == 0) {
return strcmp(arg1, arg2) != 0;
} else {
int n1 = atol(arg1);
int n2 = atol(arg2);
if (strcmp(op, "-eq") == 0) {
return n1 == n2;
} else if (strcmp(op, "-ne") == 0) {
return n1 != n2;
} else if (strcmp(op, "-gt") == 0) {
return n1 > n2;
} else if (strcmp(op, "-ge") == 0) {
return n1 >= n2;
} else if (strcmp(op, "-lt") == 0) {
return n1 < n2;
} else if (strcmp(op, "-le") == 0) {
return n1 <= n2;
} else {
fprintf(stderr, "%s: binary operator expected\n", op);
return -1;
}
}
}
return -1;
}
static int parse_or(struct parser *p) {
int a, b;
a = parse_term(p);
if (a < 0) return a;
while (match(p, "-o")) {
next(p);
b = parse_term(p);
if (b < 0) return b;
a = a || b;
}
return a;
}
static int parse_and(struct parser *p) {
int a, b;
a = parse_or(p);
if (a < 0) return a;
while (match(p, "-a")) {
next(p);
b = parse_or(p);
if (b < 0) return b;
a = a && b;
}
return a;
}
static int parse_expr(struct parser *p) {
return parse_and(p);
}
shellcmd(test) {
struct parser p;
int rc;
// Initialize parser
p.argv = argv;
p.pos = 1;
p.current = argv[0];
next(&p);
// Parse expression
rc = parse_expr(&p);
if (match(&p, "]")) next(&p);
if (p.current != NULL) {
fprintf(stderr, "syntax error at '%s'\n", p.current);
rc = -1;
}
// Return 0 if expression is true, 1 if false, and 2 on error
if (rc < 0) return 2;
if (rc > 0) return 0;
return 1;
}