Category : C Source Code
Archive   : SHERLOCK.ZIP
Filename : DIR.C

 
Output of file : DIR.C contained in archive : SHERLOCK.ZIP
/*
CPP V5 -- directives

source: dir.c
started: October 7, 1985
version: May 31, 1988

External routines defined in this file: do_pp

Written by Edward K. Ream.
This software is in the public domain.

See the read.me file for disclaimer and other information.
*/

#include "cpp.h"

#define TRACE_LINE(name) TRACEP(name, printf("line %d\n", t_line))

static long eval (void);
static long eval1 (void);
static bool gt_prec (en_tokens, en_tokens);
static bool isfnch (int, en_tokens);

static void pp_elif (void);
static void pp_else (void);
static void pp_endif (void);
static void pp_enum (void);
static void pp_error (void);
static void pp_if (void);
static void pp_ifdef (bool);
static void pp_incl (void);
static void pp_line (void);
static void pp_undef (void);
static int prec (int);

static void push_op (en_tokens);
static en_tokens pop_op (void);
static void push_val (long);
static long pop_val (void);
static void skip_lines (void);

/*
Do one preprocessor directive.
*/

#define EQL(string) str_eq(t_symbol+1,string+1)

void
do_pp()
{
TICK("do_pp");

skip_ws(FALSE);

/* Get the directive into t_symbol[]. */
if (!isalpha(ch)) {
goto not_alpha;
}
t_id(t_symbol, MAX_SYMBOL);

/* 3/3/89: bug fix: full white space allowed here. */
skip_ws(FALSE);

/* Skip simple white space after the directive. */
/* -----
while (ch == ' ' || ch == '\t') {
sysnext();
}
----- */

switch(t_symbol [0]) {

case 'd':
if (t_length == 6 && EQL("define")) {
pp_def();
return;
}
goto not_pp;

case 'e':
if (t_length == 4) {
if (EQL("else")) {
pp_else();
return;
}
else if (EQL("elif")) {
pp_elif();
return;
}
}
#ifdef HAS_PP_ENUM
else if (t_length == 4 && EQL("enum")) {
pp_enum();
return;
}
#endif
else if (t_length == 5 && EQL("endif")) {
pp_endif();
return;
}
else if (t_length == 5 && EQL("error")) {
pp_error();
return;
}
goto not_pp;

case 'i':
switch(t_length) {
case 2: if (EQL("if")) {
pp_if();
return;
}
goto not_pp;
case 5: if (EQL("ifdef")) {
pp_ifdef(TRUE);
return;
}
goto not_pp;
case 6: if (EQL("ifndef")) {
pp_ifdef(FALSE);
return;
}
goto not_pp;
case 7: if (EQL("include")) {
pp_incl();
return;
}
goto not_pp;
}
goto not_pp;

case 'l':
if (t_length == 4 && EQL("line")) {
pp_line();
return;
}
goto not_pp;

case 'p':
if (t_length == 6 && EQL("pragma")) {
/* Do NOTHING!! */
skip_pp();
return;
}
goto not_pp;

case 'u':
if (t_length == 5 && EQL("undef")) {
pp_undef();
return;
}
goto not_pp;

default:
goto not_pp;
}

not_alpha:
/*
Be more permissive than the new C standard.
Just skip the rest of the line.
*/
skip_pp();
return;

not_pp:
err2(t_symbol, " is not a valid preprocessor directive.");
skip_pp();
}

#undef EQL

/*
Handle the #error directive.
Produce a diagnostic message.
*/
static char err_msg[] = "#error: ";

static void
pp_error()
{
char message[MAX_SYMBOL];
int i;

strcpy(message, err_msg);
for (i = strlen(message); i < MAX_SYMBOL; i++) {
if (ch == '\n' || ch == END_FILE) {
break;
}
else {
message[i] = ch;
sysnext();
}
}
message[i] = '\0';
fatal(message);
}


/*
Evaluate a constant expression to either true or false.
A constant expression consists of:

1. integer constants or character constants
2. the unary - + and ~ operators
3. the binary + - * / & | ^ << >> == != < > <= >= oprators
4. the ternary ? : operator
5. the ( and ) groupers.

Identifiers are expanded if they are defined, otherwise they
are taken to have a value of zero. All arithmetic is integer and
ints are expanded to long.
*/

#define MAX_EVAL_VAL 100
#define MAX_EVAL_OP 50

static long val_stack[MAX_EVAL_VAL];
static int val_ptr = 0;
static en_tokens op_stack[MAX_EVAL_OP];
static int op_ptr = 0;
static long result;
static bool paren_seen;
static bool error_seen;

static long
eval()
{
TRACETOK("eval");

error_seen = FALSE;
get_token(TRUE);
result = eval1();

RETURN_LONG("eval", result);
}

static char * junk;
static int junki;

static long
eval1()
{
register en_tokens op, op2;
register long val1, val2, val3;
int op_1ptr;

TRACETOK("eval1");

op_1ptr = op_ptr;

/* State S1: unary +, unary -, !, ~, constant or id is expected here. */

s1:
TRACEPN("v_eval1", printf("at state 1\n"));

while (is(PLUS_TOK) || is(MINUS_TOK) || is(TILDE_TOK) || is(NOT_TOK)) {
if (is(PLUS_TOK)) {
push_op(UPLUS_TOK);
}
else if (is(MINUS_TOK)) {
push_op(UMINUS_TOK);
}
else if (is(NOT_TOK)) {
push_op(NOT_TOK);
}
else {
push_op(TILDE_TOK);
}
get_token(TRUE);
}

/* We expect a constant or identifier here. */
if (is(INT_TOK) || is(LONG_TOK) || is(CHAR_TOK)) {
push_val((long) t_value);
get_token(TRUE);
}
else if (is(ID_TOK)) {
/* Special case defined id and defined(id). */
if (str_eq(t_symbol, "defined")) {

/* Do not macro expand an id here! */
get_token(FALSE);
if (!is(ID_TOK) && !is(LPAREN_TOK)) {
error("Id or '(' expected after 'defined'.");
goto bad_expr;
}
paren_seen = is(LPAREN_TOK);
if (paren_seen) {
get_token(FALSE);
if (!is(ID_TOK)) {
error("Id expected after '('.");
goto bad_expr;
}
}
if(mst_lookup(t_symbol, &junk, &junki)) {
push_val(1L);
}
else {
push_val(0L);
}
get_token(TRUE);
if (paren_seen) {
if (is(RPAREN_TOK)) {
get_token(TRUE);
}
else {
error("')' expected.");
goto bad_expr;
}
}
}
else {
/* The identifier must be undefined, so it gets 0. */
push_val(0L);
get_token(TRUE);
}
}
else if (is(LPAREN_TOK)) {
get_token(TRUE);

/* Evaluate the expression recursively. */
result = eval1();
if (is(RPAREN_TOK)) {
get_token(TRUE);
push_val(result);
}
else {
error("')' expected.");
goto bad_expr;
}
}
else {
error("Integer constant or parenthesized expression expected.");
goto bad_expr;
}

/* Perform all unary ops and enter state S2. */
TRACEPN("v_eval1", printf("at state 1A\n"));
while (op_ptr > op_1ptr) {
switch (op = pop_op()) {
case UPLUS_TOK: break;
case UMINUS_TOK: push_val(-pop_val()); break;
case NOT_TOK: push_val((long)(!pop_val())); break;
case TILDE_TOK: push_val(~pop_val()); break;
default: push_op(op); goto s2;
}
}

/* State S2: binary op or end_of_expression expected here. */

s2:
TRACEPN("v_eval1", printf("at state 2\n"));

/*
Perform binary operators until the operator stack is
empty or until token operator has a higher precedence
than the operator on the top of the operator stack.
*/

while (op_ptr > op_1ptr && gt_prec(op_stack[op_ptr - 1], token)) {

val2 = pop_val();
val1 = pop_val();
op = pop_op();

switch (op) {
case PLUS_TOK: push_val(val1 + val2); break;
case MINUS_TOK: push_val(val1 - val2); break;
case STAR_TOK: push_val(val1 * val2); break;
case DIV_TOK: push_val((long)(val2?(val1/val2):0));
break;
case MOD_TOK: push_val(val1 % val2); break;
case AND_TOK: push_val(val1 & val2); break;
case OR_TOK: push_val(val1 | val2); break;
case XOR_TOK: push_val(val1 ^ val2); break;
case LSHIFT_TOK: push_val(val1 << val2); break;
case RSHIFT_TOK: push_val(val1 >> val2); break;
case EQUAL_TOK: push_val((long)(val1 == val2)); break;
case NE_TOK: push_val((long)(val1 != val2)); break;
case LT_TOK: push_val((long)(val1 < val2)); break;
case GT_TOK: push_val((long)(val1 > val2)); break;
case LE_TOK: push_val((long)(val1 <= val2)); break;
case GE_TOK: push_val((long)(val1 >= val2)); break;
case LAND_TOK: push_val((long)(val1 && val2)); break;
case LOR_TOK: push_val((long)(val1 || val2)); break;

case COLON_TOK: op2 = pop_op();
if (op2 != QUESTION_TOK) {
goto bad_expr;
}
val3 = pop_val();
push_val(val3 ? val1 : val2);
break;

default: goto bad_expr;
}
}

/* Enter state S1 or return on end-of-expression. */
if (is(NULL_TOK) || is(RPAREN_TOK)) {
val1 = pop_val();
RETURN_LONG("eval1", val1);
}
else if (is(ERR_TOK)) {
goto bad_expr;
}
else {
push_op(token);
get_token(TRUE);
goto s1;
}

bad_expr:
if (!error_seen) {
error("Bad constant expression--zero assumed.");
}
error_seen = TRUE;
RETURN_LONG("eval1", 0L);
}


/*
Return TRUE if a reduction is possible with op1 on the operator stack
and op2 as the new operator.

Always return TRUE if op2 delimits the expression, otherwise
return TRUE if op1 has higher precedence than op2, otherwise
return TRUE if they associate left to right.

This code reflects the table on page 49 of K & R.
*/

static bool
gt_prec(op1, op2)
register en_tokens op1;
register en_tokens op2;
{
register int prec1, prec2;

TRACEP("gt_prec", printf("(%s, %s)\n",
pr_op(op1), pr_op(op2)));

if (op2 == RPAREN_TOK || op2 == NULL_TOK) {
return TRUE;
}
else if (op2 == ERR_TOK) {
/* Abort immediately. */
return FALSE;
}

prec1 = prec((int)op1);
prec2 = prec((int)op2);

if (prec1 != prec2) {
/* Associativity doesn't matter. */
return prec1 > prec2;
}
else if (prec1 == 14 || prec1 == 3 || prec1 == 2) {
/* Associate right to left. */
return FALSE;
}
else {
/* Associate left to right. */
return TRUE;
}
}


/*
Return TRUE if c is legal in a file name.
Assume that the delim character is not legal.
*/
static bool
isfnch(c, delim)
int c;
en_tokens delim;
{
TICK("isfnch");

switch (c) {
case '*':
case '?':
case '\n':
case ' ':
case '\t':
case END_FILE:
return FALSE;

default: return (c != (int)delim);
}
}


/*
Return the precedence of an operator.

This code reflects the table on page 49 of K & R.
*/

static int
prec(operator)
int operator;
{
TRACEP("prec", printf("(%s)\n", pr_op(operator)));

switch (operator) {
case TILDE_TOK: return 14;
case STAR_TOK:
case DIV_TOK:
case MOD_TOK: return 13;
case PLUS_TOK:
case MINUS_TOK: return 12;
case LSHIFT_TOK:
case RSHIFT_TOK: return 11;
case LT_TOK:
case LE_TOK:
case GT_TOK:
case GE_TOK: return 10;
case EQUAL_TOK:
case NE_TOK: return 9;
case XOR_TOK: return 7;
case OR_TOK: return 6;
case COLON_TOK:
case QUESTION_TOK: return 3;
default: return -1;
}
}


/*
Routines to push and pop the operator and operand stacks.
*/
static void
push_op(op)
en_tokens op;
{
TRACEP("push_op", printf("(%s), op_ptr = %d\n",
pr_op(op), op_ptr));

if (op_ptr < MAX_EVAL_OP) {
op_stack[op_ptr++] = op;
}
else {
fatal("Operator stack overflow.");
}
}

static en_tokens
pop_op()
{
TRACEP("pop_op", printf("op_ptr = %d\n", op_ptr));

if (op_ptr > 0) {

TRACEP("pop_op", printf("returns: %s\n",
pr_op(op_stack[op_ptr - 1])));

return op_stack [--op_ptr];
}
else {
syserr("Operator stack underflow.");
return NULL_TOK;
}
}

static void
push_val(val)
long val;
{
TRACEP("push_val", printf("(%ld), val_ptr = %d\n", val, val_ptr));

if (val_ptr < MAX_EVAL_VAL) {
val_stack [val_ptr++] = val;
}
else {
fatal("Value stack overflow.");
}
}

static long
pop_val()
{
TRACEP("pop_val", printf("val_ptr = %d\n", val_ptr));

if (val_ptr > 0) {

TRACEP("pop_val", printf("returns %ld\n",
val_stack [val_ptr - 1]));

return val_stack [--val_ptr];
}
else {
syserr("Value stack underflow.");
return 0L;
}
}

/*
Handle the #elif directive when lines are NOT being skipped.
The skip_lines() routines handles #elif when lines ARE being skipped.
*/
static void
pp_elif()
{
TRACE_LINE("pp_elif");

if (t_iflevel == 0) {
error("#elif ignored--no matching #if.");
skip_pp();
return;
}

/* Skip lines if the constant expression evaluates to zero. */
if (eval() == 0L) {
/* Skip until #else or #endif. */
skip_lines();
}
}


/*
Handle the #else directive when lines are NOT being skipped.
The skip_lines() routine handles #else when lines ARE being skipped.
*/
static void
pp_else()
{
TRACE_LINE("pp_else");

if (t_iflevel == 0) {
error("#else ignored--no matching #if.");
skip_pp();
}
else if (t_ifstack [t_iflevel - 1] == TRUE) {
error("Duplicate #else ignored.");
skip_pp();
}
else {
t_ifstack [t_iflevel - 1] = TRUE;
skip_lines();
}
}


/*
Handle the #endif directive when lines are NOT being skipped.
The skip_lines() routine handles #endif when lines ARE being skipped.
*/
static void
pp_endif()
{
TRACE_LINE("pp_endif");

if (t_iflevel == 0) {
error("#endif ignored--no matching #if.");
skip_pp();
}
else {
t_iflevel--;
skip_pp();
}
}

/*
Handle the #if directive when lines are NOT being skipped.
The skip_lines() routine handles #if when lines ARE being skipped.
*/
static void
pp_if()
{
TRACE_LINE("pp_if");

/* Indicate that no #else has been seen. */
if (t_iflevel >= MAX_IF) {
error("#if ignored--nested too deeply.");
skip_pp();
return;
}
t_ifstack [t_iflevel++] = FALSE;

/* Skip lines if the constant expression evaluates to zero. */
if (eval() == 0L) {
/* Skip until #else or #endif. */
skip_lines();
}
}

/*
Handle the #ifdef and #ifndef directives when lines are NOT being
skipped.
The skip_lines() routine handles #if when lines ARE being skipped.

flag == TRUE: #ifdef
flag == FALSE: #ifndef
*/
static void
pp_ifdef(flag)
bool flag;
{
bool result;
char *text;
int nargs;

TRACE_LINE("pp_ifdef");

if(t_iflevel > MAX_IF) {
error("#ifdef or #ifndef ignored--nested too deeply.");
skip_pp();
return;
}

if (!isid1(ch)) {
error("#ifdef or #ifndef ignored--identifier expected.");
skip_pp();
return;
}

/* Get id into t_symbol[]. */
t_id(t_symbol, MAX_SYMBOL);
skip_pp();

/* Indicate that no #else has been seen. */
t_ifstack [t_iflevel++] = FALSE;

/* Skip lines if required. */
result = mst_lookup(t_symbol, &text, &nargs);
if ((flag && result == FALSE) || (!flag && result == TRUE)) {
/* Skip until #else or #endif. */
skip_lines();
}
}


/*
Handle the #include directive.
*/
static void
pp_incl()
{
bool old_mflag;
bool err_flag;
int i;
char f_name [MAX_FILE_NAME];
char path_name [200];
char delim;

TRACE_LINE("pp_incl");

/* File name is OK. */
err_flag = FALSE;

/*
Check for opening delimiter.
Allow simple macro expansions here.
*/
if (isid1(ch)) {
old_mflag = m_flag;
t_id(&t_symbol[0], MAX_SYMBOL);

/* We expect a proper macro here. */
outer_expand(&t_symbol[0], old_mflag);
}

if (ch == '"') {
delim = '"';
sysnext();
}
else if (ch == '<') {
delim = '>';
sysnext();
}
else {
err_flag = TRUE;
delim = '\r';
}

/* Get file name into f_name[]. */
for (i = 0; i < MAX_FILE_NAME-1 && isfnch(ch,(en_tokens)delim); i++) {
f_name[i] = ch;
sysnext();
}
f_name[i] = '\0';

if (err_flag || ch != delim) {
err3("#include ", f_name, " ignored--bad delimiter.");
skip_pp();
return;
}

/* Skip over the delimiter. */
sysnext();

/*
We must skip the line here, because after
sysopen() is called we would not be able to skip
characters in the old level.
*/

skip_pp();
if (ch == '\n') {
sysnlput();
sysnext();
}
bump_line();

if (t_inlevel >= MAX_INCLUDE) {
t_line--;
err3("#include ", f_name, " ignored--nested too deeply.");
t_line++;
return;
}

/* Open the file. */
if(sysopen(f_name) == FALSE) {
for (i = 0; i < n_paths; i++) {
strcpy(path_name, paths [i]);
strcat(path_name, f_name);

TRACEP("pp_incl", printf("attempting to open %s\n",
path_name));

if (sysopen(path_name) == TRUE) {
return;
}
}
t_line--;
err3("#include ", f_name, " ignored--file not found.");
t_line++;
}
}


/*
Set the line number and file name.
*/
static void
pp_line()
{
bool old_mflag;
int i;

TRACE_LINE("pp_line");

/* Allow macro expansion. */
if (isid1(ch)) {
old_mflag = m_flag;
t_id(&t_symbol[0], MAX_SYMBOL);

/* We expect a macro id here. */
outer_expand(&t_symbol[0], old_mflag);
}

(void) t_number(FALSE);
t_line = (int) t_value;

/* See if an optional file name is present. */
skip_ws(FALSE);

/* Allow macro expansion. */
if (isid1(ch)) {
old_mflag = m_flag;
t_id(&t_symbol[0], MAX_SYMBOL);

/* We expect a macro id here. */
outer_expand(&t_symbol[0], old_mflag);
}
if (ch == '\n' || ch == END_FILE) {
return;
}

/* Copy line to t_file[]. */
for(i = 0; i < MAX_FILE_NAME - 1; i++) {
if (ch == END_FILE || ch == '\n') {
break;
}
t_file[i] = ch;
sysnext();
}
t_file [i] = '\0';
skip_pp();
}


/*
Undefine a previously #defined variable.
*/
static void
pp_undef()
{
TRACE_LINE("pp_undef");

/* Get the identifier into t_symbol[]. */
if (!isid1(ch)) {
error("#undef ignored--identifier expected.");
skip_pp();
return;
}
t_id(t_symbol, MAX_SYMBOL);

/* Delete the identifier. Give no warning if it doesn't exist. */
mst_delete(t_symbol);
skip_pp();
}


/*
Skip lines until a matching #else or #endif directive is found.
Thus, interior #ifdef, #ifndef, #if, #elif and #else directives
must be recognized and dealt with.

A fine point: This code skips lines without doing any parsing of the
line. The ONLY things this code looks for are lines which start with
#ifdef, #ifndef, #if or #else. One should not detect "unknown"
preprocessor directives or other possible "errors," for the very good
reason that one wants to use conditional compilation to exclude lines
which might not even be written in the C language.

Examples can be given where this action might be suspect. Consider a
string which is continued from one line to the next using the backslash
newline convention. If the continuation line starts with one of the
four directives that this routine is looking for then either the lex or
the person using the lex will become confused. This is a minor hole in
the C language, and it seems better to ignore the fine points and do
what is sensible in most cases, namely skip entire lines as a unit.

The routine skip_past() is used to do the skipping of exactly 1 line.
*/

#define EQL(string) str_eq(t_symbol,string)
static void
skip_lines()
{
register int level = 0; /* Inner nesting level */

TRACE_LINE("skip_lines");

/* Just in case. */
if (t_iflevel <= 0) {
syserr("skip_lines: Can't happen.");
}

/* Start a new line. */
loop:

if (ch == END_FILE) {
error("File ends inside range of #ifdef.");
return;
}

/* Skip the line if it doesn't start with '#'. */
if (ch != '#') {
skip_past();
goto loop;
}

/* Skip the line if '#' isn't followed by an id. */
sysnext();
skip_ws(FALSE);

if (!isid1(ch)) {
skip_past();
goto loop;
}

/* Get the directive into t_symbol[]. */
t_id(t_symbol, MAX_SYMBOL);

if (EQL("ifdef") || EQL("ifndef") || EQL("if")) {
level++;
}
else if (EQL("elif")) {
if (level > 0) {
/* This directive will itself be skipped. */
skip_pp();
}
else if (eval() == 0L) {
/* Call to eval will bump line number. */
TRACEPN("skip_lines",
printf("stopped by #elif at line %d\n",
t_line - 1));
return;
}
}
else if (EQL("else")) {
if (level > 0) {
/* Skip this directive. */
skip_pp();
}
else if (t_ifstack [t_iflevel - 1] == FALSE) {
t_ifstack [t_iflevel - 1] = TRUE;
skip_pp();
TRACEPN("skip_lines",
printf("stopped by #else at line %d\n",
t_line));
return;
}
else {
error("Extra #else ignored.");
}
}
else if (EQL("endif")) {
if (level > 0) {
level--;
}
else {
t_iflevel--;
skip_pp();
TRACEPN("skip_lines",
printf("stopped by #endif at line %d\n",
t_line));
return;
}
}
else {
skip_past();
}
goto loop;
}

#undef EQL


  3 Responses to “Category : C Source Code
Archive   : SHERLOCK.ZIP
Filename : DIR.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/