diff options
Diffstat (limited to 'libslang/src/slprepr.c')
-rw-r--r-- | libslang/src/slprepr.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/libslang/src/slprepr.c b/libslang/src/slprepr.c new file mode 100644 index 0000000..8cb1f5f --- /dev/null +++ b/libslang/src/slprepr.c @@ -0,0 +1,569 @@ +/* Copyright (c) 1996, 1999, 2001, 2002, 2003 John E. Davis + * This file is part of the S-Lang library. + * + * You may distribute under the terms of either the GNU General Public + * License or the Perl Artistic License. + */ + +/*--------------------------------*-C-*---------------------------------* + * File: slprepr.c + * + * preprocessing routines + */ +/*{{{ notes: */ +/* + * various preprocessing tokens supported + * + * #ifdef TOKEN1 TOKEN2 ... + * - True if any of TOKEN1 TOKEN2 ... are defined + * + * #ifndef TOKEN1 TOKEN2 ... + * - True if none of TOKEN1 TOKEN2 ... are defined + * + * #iftrue + * #ifnfalse + * #if true + * #if !false + * - always True + * + * #iffalse + * #ifntrue + * #if false + * #if !true + * - always False + * + * #if$ENV + * - True if the enviroment variable ENV is set + * + * #ifn$ENV + * #if !$ENV + * - True if the enviroment variable ENV is not set + * + * #if$ENV TOKEN1 TOKEN2 ... + * - True if the contents of enviroment variable ENV match + * any of TOKEN1 TOKEN2 ... + * + * #ifn$ENV TOKEN1 TOKEN2 ... + * #if !$ENV TOKEN1 TOKEN2 ... + * - True if the contents of enviroment variable ENV do not match + * any of TOKEN1 TOKEN2 ... + * + * NB: For $ENV, the tokens may contain wildcard characters: + * '?' - match any single character + * '*' - match any number of characters + * + * #ifexists TOKEN + * #ifnexists TOKEN + * #if !exists TOKEN + * - check if a variable/function exists + * + * #ifeval EXPRESSION + * #ifneval EXPRESSION + * #if !eval TOKEN + * - evaluates the EXPRESSION as an SLang expression + * + * #if (EXPRESSION) + * #if !(EXPRESSION) + * - as per '#ifeval' / '#ifneval', + * evaluates the EXPRESSION as a SLang expression + * - using '#ifn (EXPRESSION)' is possible, but deprecated + * + * #elif... + * #else + * #endif + * + * #stop + * - stop reading the entire file here, provided that the line + * would have been executed + * eg: + * #iffalse + * # stop + * #endif + * would NEVER stop + * + * #<TAG> + * - start embedded text region + * #</TAG> + * - end embedded text region + * + * All text, include other preprocessing directives, that occurs between + * the '#<TAG>' and '#</TAG>' directives will be ignored. + * This is useful for embedding other code or documentation. + * eg: + * #<latex> + * \chapter{My Documentation Effort} + * #</latex> + * NB: * although the current implementation only looks for sequences + * '#<' and '#</', it is advisable to use the full '<TAG>' form + * for documentation purposes and to avoid future surprises. + * * do NOT attempt to nest these constructions + * + * GENERAL NOTES: + * Apart from the '#ifdef' and '#ifndef' constructions, we are quite + * generous with accepting whitespace and the alternative '!' form. + * eg., + * #ifTEST + * #ifnTEST + * #if TEST + * #if !TEST + * #if ! TEST + * + * mj olesen + *----------------------------------------------------------------------*/ +/*}}}*/ +/*{{{ includes: */ +#include "slinclud.h" + +#include "slang.h" +#include "_slang.h" +/*}}}*/ + +int (*SLprep_exists_hook) (char *, char); /* in "slang.h" */ +int (*_SLprep_eval_hook) (char *); /* in "_slang.h" */ + +/*{{{ SLprep_open_prep (), SLprep_close_prep () */ +int SLprep_open_prep (SLPreprocess_Type *pt) +{ + pt->this_level = 0; + pt->exec_level = 0; + pt->prev_exec_level = 0; + pt->comment_char = '%'; + pt->preprocess_char = '#'; + pt->flags = 0; + return 0; +} + +void SLprep_close_prep (SLPreprocess_Type *pt) +{ + (void) pt; +} +/*}}}*/ + +/*{{{ SLwildcard () */ +/*----------------------------------------------------------------------* + * Does `string' match `pattern' ? + * + * '*' in pattern matches any sub-string (including the null string) + * '?' matches any single char. + * + * Code taken from that donated by Paul Hudson <paulh@harlequin.co.uk> + * to the fvwm project. + * It is public domain, no strings attached. No guarantees either. + *----------------------------------------------------------------------*/ +static int SLwildcard (char *pattern, char *string) +{ + if (pattern == NULL || *pattern == '\0' || !strcmp (pattern, "*")) + return 1; + else if (string == NULL) + return 0; + + while (*pattern && *string) switch (*pattern) + { + case '?': + /* match any single character */ + pattern++; + string++; + break; + + case '*': + /* see if rest of pattern matches any trailing */ + /* substring of the string. */ + if (*++pattern == '\0') + return 1; /* trailing * must match rest */ + + while (*string) + { + if (SLwildcard (pattern, string)) return 1; + string++; + } + return 0; + + /* break; */ + + default: + if (*pattern == '\\') + { + if (*++pattern == '\0') + pattern--; /* don't skip trailing backslash */ + } + if (*pattern++ != *string++) return 0; + break; + } + + return ((*string == '\0') + && ((*pattern == '\0') || !strcmp (pattern, "*"))); +} +/*}}}*/ + +#if defined(__16_BIT_SYSTEM__) +# define MAX_DEFINES 10 +#else +# define MAX_DEFINES 128 +#endif + +/* The extra one is for NULL termination */ +char *_SLdefines [MAX_DEFINES + 1]; + +int SLdefine_for_ifdef (char *s) /*{{{*/ +{ + unsigned int i; + + for (i = 0; i < MAX_DEFINES; i++) + { + char *s1 = _SLdefines [i]; + + if (s1 == s) + return 0; /* already defined (hashed string) */ + + if (s1 != NULL) + continue; + + s = SLang_create_slstring (s); + if (s == NULL) + return -1; + + _SLdefines[i] = s; + return 0; + } + return -1; +} +/*}}}*/ + +/*{{{ static functions */ +static int is_any_defined(char *buf, char comment) /*{{{*/ +{ + char *sys; + unsigned int i; + + while (1) + { + register char ch; + + /* Skip whitespace */ + while (((ch = *buf) == ' ') || (ch == '\t')) + buf++; + + if ((ch == '\n') || (ch == 0) || (ch == comment)) + return 0; + + i = 0; + while (NULL != (sys = _SLdefines [i++])) + { + unsigned int n; + + if (*sys != ch) + continue; + + n = strlen (sys); + if (0 == strncmp (buf, sys, n)) + { + char ch1 = *(buf + n); + + if ((ch1 == '\n') || (ch1 == 0) || + (ch1 == ' ') || (ch1 == '\t') || (ch1 == comment)) + return 1; + } + } + + /* Skip past word */ + while (((ch = *buf) != ' ') + && (ch != '\n') + && (ch != 0) + && (ch != '\t') + && (ch != comment)) + buf++; + } +} +/*}}}*/ + +static unsigned char *tokenize (unsigned char *buf, char *token, unsigned int len) +{ + register char *token_end; + + token_end = token + (len - 1); /* allow room for \0 */ + + while ((token < token_end) && (*buf > ' ')) + *token++ = *buf++; + + if (*buf > ' ') return NULL; /* token too long */ + + *token = '\0'; + + while ((*buf == ' ') || (*buf == '\t')) buf++; + + return buf; +} + +static int is_env_defined (char *buf, char comment) /*{{{*/ +{ + char * env, token [32]; + + if ((*buf <= ' ') || (*buf == comment)) return 0; /* no token */ + + if (NULL == (buf = (char *) tokenize ((unsigned char *) buf, + token, sizeof (token)))) + return 0; + + if (NULL == (env = getenv (token))) + return 0; /* ENV not defined */ + + if ((*buf == '\0') || (*buf == '\n') || (*buf == comment)) + return 1; /* no tokens, but getenv() worked */ + + do + { + buf = (char *) tokenize ((unsigned char *) buf, token, sizeof (token)); + if (buf == NULL) return 0; + + if (SLwildcard (token, env)) + return 1; + } + while (*buf && (*buf != '\n') && (*buf != comment)); + + return 0; +} +/*}}}*/ +/*}}}*/ + +int SLprep_line_ok (char *buf, SLPreprocess_Type *pt) /*{{{*/ +{ + int level, prev_exec_level, exec_level; + unsigned int flags; + + if ((buf == NULL) || (pt == NULL)) return 1; + + /* the '#stop' marker was already reached */ + if (pt->flags & SLPREP_STOP_READING) + return 0; + + /* local bookkeeping */ + level = pt->this_level; + exec_level = pt->exec_level; + prev_exec_level = pt->prev_exec_level; + flags = pt->flags; + + if (*buf != pt->preprocess_char) + { + /* ignore out-of-context or embedded text */ + if ((level != exec_level) || (flags & SLPREP_EMBEDDED_TEXT)) + return 0; + + if (*buf == '\n') return flags & SLPREP_BLANK_LINES_OK; + if (*buf == pt->comment_char) return flags & SLPREP_COMMENT_LINES_OK; + + return 1; + } + + buf++; + + /* + * Always allow '#!' to pass. This could be a shell script + * with something like '#! /usr/local/bin/slang' + */ + if ((*buf == '!') && (pt->preprocess_char == '#')) + return 0; + + /* Allow whitespace as in '# ifdef' */ + while ((*buf == ' ') || (*buf == '\t')) buf++; + + /* + * quick and dirty coding for '#<TAG>' and '#</TAG>' + * only bothers to differentiate between '#<' and '#</' + * never attempt to nest these constructions!! + */ + if (*buf == '<') + { + buf++; + if (*buf == '/') /* likely a '#</TAG>' */ + pt->flags &= ~SLPREP_EMBEDDED_TEXT; + else /* likely a '#<TAG>' */ + pt->flags |= SLPREP_EMBEDDED_TEXT; + + return 0; + } + + if (pt->flags & SLPREP_EMBEDDED_TEXT) + return 0; /* embedded text - ignore everything */ + + + if ((*buf < 'a') || (*buf > 'z')) /* something weird */ + return (level == exec_level); + + if ( !strncmp(buf, "stop", 4) ) + { + if (level == exec_level) /* signal stop if we're in scope */ + pt->flags |= SLPREP_STOP_READING; + return 0; /* swallow this tag */ + } + + if (!strncmp(buf, "endif", 5)) + { + if (level == exec_level) + { + exec_level--; + prev_exec_level = exec_level; + } + level--; + if (level < prev_exec_level) prev_exec_level = level; + goto done; + } + + if ((buf[0] == 'e') && (buf[1] == 'l')) /* else, elifdef, ... */ + { + if ((level == exec_level + 1) + && (prev_exec_level != level)) + { + /* We are in position to execute */ + buf += 2; + if ((buf[0] == 's') && (buf[1] == 'e')) + { + /* 'else' */ + exec_level = level; + goto done; + } + + /* + * drop through to 'if' testing. + * First set variable to value appropriate for 'if' testing. + */ + level--; /* now == to exec level */ + } + else + { + if (level == exec_level) + { + exec_level--; + } + goto done; + } + } + + if ((buf[0] == 'i') && (buf[1] == 'f')) + { + int test = 0; /* fallback value */ + int truth = 1; + buf += 2; + + if (level != exec_level) + { + level++; + goto done; /* Not interested */ + } + level++; + + if (buf[0] == 'n') + { + truth = !truth; + buf++; + } + + /* for 'ifdef' and 'ifndef' we are done */ + if (!strncmp (buf, "def", 3)) + { + test = is_any_defined(buf + 3, pt->comment_char); + } + else + { + /* + * the '#ifn' construction cannot have whitespace or '!' + * for the other forms, we can also accept + * 'if!..' instead of 'ifn...', or even 'if ...' or 'if ! ...' + */ + if (truth) + { + /* Allow some whitespace */ + while ((*buf == ' ') || (*buf == '\t')) buf++; + + if (*buf == '!') + { + /* the 'if !' form */ + truth = !truth; + buf++; + while ((*buf == ' ') || (*buf == '\t')) buf++; + } + } + + if (*buf == '$') + test = is_env_defined (buf + 1, pt->comment_char); + else if (*buf == '(' + && (_SLprep_eval_hook != NULL)) + test = (*_SLprep_eval_hook) (buf); + else if (!strncmp (buf, "eval", 4) + && (_SLprep_eval_hook != NULL)) + test = (*_SLprep_eval_hook) (buf + 4); + else if (!strncmp (buf, "exists", 6) + && (SLprep_exists_hook != NULL)) + test = (*SLprep_exists_hook) (buf + 6, pt->comment_char); + else if (!strncmp (buf, "true", 4)) + test = 1; + else if (strncmp (buf, "false", 5)) + return 1; /* unknown - let it bomb */ + } + + if (truth == test) prev_exec_level = exec_level = level; + } + else + { + return 1; /* let it bomb */ + } + + done: + + if (exec_level < 0) return 1; /* bad level - let it bomb */ + + pt->this_level = level; + pt->exec_level = exec_level; + pt->prev_exec_level = prev_exec_level; + return 0; +} +/*}}}*/ + +/*{{{ main() - for testing only */ +#if 0 +int main (int argc, char *argv[]) +{ + int i; + char buf[1024]; + SLPreprocess_Type pt; + + SLprep_open_prep (&pt); + SLdefine_for_ifdef ("UNIX"); + + /* super cheap getopts */ + for (i = 1; i < argc; i++) + { + char *p = argv[i]; + if (*p++ != '-') break; + if (*p == '-') break; + + if (*p == 'D') + { + SLdefine_for_ifdef (p + 1); + continue; + } + + while (*p) { + switch (*p) { + case 'B': pt.flags |= (SLPREP_BLANK_LINES_OK); break; + case 'C': pt.flags |= (SLPREP_COMMENT_LINES_OK); break; + case 'c': if (p[1]) pt.comment_char = *++p; break; + case 'p': if (p[1]) pt.preprocess_char = *++p; break; + default: + fprintf (stderr, "unknown flag '%c'\n", *p); + break; + } + p++; + } + } + + while (NULL != fgets (buf, sizeof (buf) - 1, stdin)) + { + if (SLprep_line_ok (buf, &pt)) + fputs (buf, stdout); + } + + SLprep_close_prep (&pt); + return 0; +} +#endif +/*}}}*/ |