home *** CD-ROM | disk | FTP | other *** search
- /*
- * Somewhere along the way, someone deleted the GNU copyright from this code,
- * replacing it with "Imagine generic GNU copyright here". This is _not_
- * acceptable behavior in any society, polite or otherwise.
- *
- * However, since I understand the need to delete this large chunk of text, I've
- * followed the new GNU technic, and put it in a seperate file.
- */
-
- /*
- * Copyright (C) 1985 Richard M. Stallman
- *
- * This software may be redistributed under the terms described in the file
- * LICENSE in this directory.
- */
-
- /* modified by Robert Larson to make fewer assumptions about the compiler */
- /*
- * (Making it useful to mg) (#if not supported by osk, enum not supported by
- * many)
- */
-
- /*
- * To test, compile with -Dtest. This Dtestable feature turns this into a
- * self-contained program which reads a pattern, describes how it compiles,
- * then reads a string and searches for it.
- */
-
- #include "regexp.h"
-
- #ifdef REGEXP
- #include "def.h" /* defines VOID etc. for mg */
-
- static VOID init_syntax_once
- PROTO((VOID));
- static VOID store_jump
- PROTO((char *, int, char *));
- static VOID insert_jump
- PROTO((int, char *, char *, char *));
- static int bcmp_translate
- PROTO((char *, char *, int, char *));
-
- #ifdef ANSI
- #include <string.h>
- #include <stdlib.h>
- #endif
-
- #ifdef emacs
-
- /*
- * The `emacs' switch turns on certain special matching commands that make
- * sense only in emacs.
- */
-
- #include "config.h"
- #include "lisp.h"
- #include "buffer.h"
- #include "syntax.h"
-
- #else /* not emacs */
-
- /*
- * Define the syntax stuff, so we can do the \<...\> things.
- */
-
- #ifndef Sword /* must be non-zero in some of the tests
- * below... */
- #define Sword 1
- #endif
-
- #define SYNTAX(c) re_syntax_table[c]
-
- #ifdef SYNTAX_TABLE
-
- char *re_syntax_table;
-
- #else
-
- static char re_syntax_table[256];
-
- static VOID
- init_syntax_once()
- {
- register int c;
- static int done = 0;
-
- if (done)
- return;
-
- bzero(re_syntax_table, sizeof re_syntax_table);
-
- for (c = 'a'; c <= 'z'; c++)
- re_syntax_table[c] = Sword;
-
- for (c = 'A'; c <= 'Z'; c++)
- re_syntax_table[c] = Sword;
-
- for (c = '0'; c <= '9'; c++)
- re_syntax_table[c] = Sword;
-
- done = 1;
- }
-
- #endif /* SYNTAX_TABLE */
- #endif /* not emacs */
-
- #include "regex.h"
-
- /*
- * Number of failure points to allocate space for initially, when matching. f
- * this number is exceeded, more space is allocated, so it is not a hard
- * limit.
- */
-
- #ifndef NFAILURES
- #define NFAILURES 80
- #endif /* NFAILURES */
-
- /* width of a byte in bits */
-
- #define BYTEWIDTH 8
-
- /*
- * These are the command codes that appear in compiled regular expressions,
- * one per byte. Some command codes are followed by argument bytes. A command
- * code can specify any interpretation whatever for its arguments. Zero-bytes
- * may appear in the compiled regular expression.
- */
-
- typedef char regexpcode;
- #define unused 0
- #define exactn 1 /* followed by one byte giving n, and then by
- * n literal bytes */
- #define begline 2 /* fails unless at beginning of line */
- #define endline 3 /* fails unless at end of line */
- #define jump 4 /* followed by two bytes giving relative
- * address to jump to */
- #define on_failure_jump 5 /* followed by two bytes giving relative
- * address of place */
- /* to resume at in case of failure. */
- #define finalize_jump 6 /* Throw away latest failure point and then
- * jump to address. */
- #define maybe_finalize_jump 7 /* Like jump but finalize if safe to do so. */
- /*
- * This is used to jump back to the beginning of a repeat. If the command
- * that follows this jump is clearly incompatible with the one at the
- * beginning of the repeat, such that we can be sure that there is no use
- * backtracking out of repetitions already completed, then we finalize.
- */
- #define dummy_failure_jump 8 /* jump, and push a dummy failure point. */
- /*
- * This failure point will be thrown away if an attempt is made to use it for
- * a failure. A + construct makes this before the first repeat.
- */
- #define anychar 9 /* matches any one character */
- #define charset 10 /* matches any one char belonging to
- * specified set. */
- /*
- * First following byte is # bitmap bytes. Then come bytes for a bit-map
- * saying which chars are in. Bits in each byte are ordered low-bit-first. A
- * character is in the set if its bit is 1. A character too large to have a
- * bit in the map is automatically not in the set
- */
- #define charset_not 11 /* similar but match any character that is
- * NOT one of those specified */
- #define start_memory 12 /* starts remembering the text that is
- * matched */
- /*
- * and stores it in a memory register. followed by one byte containing the
- * register number. Register numbers must be in the range 0 through NREGS.
- */
- #define stop_memory 13 /* stops remembering the text that is matched */
- /*
- * and stores it in a memory register. followed by one byte containing the
- * register number. Register numbers must be in the range 0 through NREGS.
- */
- #define duplicate 14 /* match a duplicate of something remembered. */
- /* Followed by one byte containing the index of the memory register. */
- #define before_dot 15 /* Succeeds if before dot */
- #define at_dot 16 /* Succeeds if at dot */
- #define after_dot 17 /* Succeeds if after dot */
- #define begbuf 18 /* Succeeds if at beginning of buffer */
- #define endbuf 19 /* Succeeds if at end of buffer */
- #define wordchar 20 /* Matches any word-constituent character */
- #define notwordchar 21 /* Matches any char that is not a
- * word-constituent */
- #define wordbeg 22 /* Succeeds if at word beginning */
- #define wordend 23 /* Succeeds if at word end */
- #define wordbound 24 /* Succeeds if at a word boundary */
- #define notwordbound 25 /* Succeeds if not at a word boundary */
- #define syntaxspec 26 /* Matches any character whose syntax is
- * specified. */
- /* followed by a byte which contains a syntax code, Sword or such like */
- #define notsyntaxspec 27 /* Matches any character whose syntax differs
- * from the specified. */
-
-
- #ifndef SIGN_EXTEND_CHAR
- #define SIGN_EXTEND_CHAR(x) (x)
- #endif
-
-
- /*
- * compile_pattern takes a regular-expression descriptor string in the user's
- * format and converts it into a buffer full of byte commands for matching.
- *
- * pattern is the address of the pattern string size is the length of
- * it. bufp is a struct re_pattern_buffer * which points to the
- * info on where to store the byte commands. This structure contains a char *
- * which points to the actual space, which should have been obtained with
- * malloc. compile_pattern may use realloc to grow the buffer space.
- *
- * The number of bytes of commands can be found out by looking in the struct
- * re_pattern_buffer that bufp pointed to, after compile_pattern returns.
- */
-
- #define PATPUSH(ch) (*b++ = (char) (ch))
-
- #define PATFETCH(c) \
- {if (p == pend) goto end_of_pattern; \
- c = * (unsigned char *) p++; \
- if (translate) c = translate[c]; }
-
- #define PATFETCH_RAW(c) \
- {if (p == pend) goto end_of_pattern; \
- c = * (unsigned char *) p++; }
-
- #define PATUNFETCH p--
-
- #define EXTEND_BUFFER \
- { char *old_buffer = bufp->buffer; \
- if (bufp->allocated == (1<<16)) goto too_big; \
- bufp->allocated *= 2; \
- if (bufp->allocated > (1<<16)) bufp->allocated = (1<<16); \
- if (!(bufp->buffer = (char *) realloc (bufp->buffer, bufp->allocated))) \
- goto memory_exhausted; \
- c = bufp->buffer - old_buffer; \
- b += c; \
- if (fixup_jump) \
- fixup_jump += c; \
- if (laststart) \
- laststart += c; \
- begalt += c; \
- if (pending_exact) \
- pending_exact += c; \
- }
-
- char *
- re_compile_pattern(pattern, size, bufp)
- char *pattern;
- int size;
- struct re_pattern_buffer *bufp;
- {
- register char *b = bufp->buffer;
- register char *p = pattern;
- char *pend = pattern + size;
- register unsigned c, c1;
- char *p1;
- unsigned char *translate = (unsigned char *) bufp->translate;
-
- /*
- * address of the count-byte of the most recently inserted "exactn"
- * command. This makes it possible to tell whether a new exact-match
- * character can be added to that command or requires a new "exactn"
- * command.
- */
-
- char *pending_exact = 0;
-
- /*
- * address of the place where a forward-jump should go to the end of
- * the containing expression. Each alternative of an "or", except the
- * last, ends with a forward-jump of this sort.
- */
-
- char *fixup_jump = 0;
-
- /*
- * address of start of the most recently finished expression. This
- * tells postfix * where to find the start of its operand.
- */
-
- char *laststart = 0;
-
- /* In processing a repeat, 1 means zero matches is allowed */
-
- char zero_times_ok;
-
- /* In processing a repeat, 1 means many matches is allowed */
-
- char many_times_ok;
-
- /* address of beginning of regexp, or inside of last \( */
-
- char *begalt = b;
-
- /*
- * Stack of information saved by \( and restored by \). Four stack
- * elements are pushed by each \(: First, the value of b. Second, the
- * value of fixup_jump. Third, the value of regnum. Fourth, the value
- * of begalt.
- */
-
- int stackb[40];
- int *stackp = stackb;
- int *stacke = stackb + 40;
- int *stackt;
-
- /*
- * Counts \('s as they are encountered. Remembered for the matching
- * \), where it becomes the "register number" to put in the
- * stop_memory command
- */
-
- int regnum = 1;
-
- bufp->fastmap_accurate = 0;
-
- #ifndef SYNTAX_TABLE
- #ifndef emacs
- /*
- * Initialize the syntax table.
- */
- init_syntax_once();
- #endif
- #endif
-
- if (bufp->allocated == 0) {
- bufp->allocated = 28;
- if (bufp->buffer)
- /* EXTEND_BUFFER loses when bufp->allocated is 0 */
- bufp->buffer = (char *) realloc((char *) bufp->buffer, (unsigned) 28);
- else
- /* Caller did not allocate a buffer. Do it for him */
- bufp->buffer = (char *) malloc((unsigned) 28);
- if (!bufp->buffer)
- goto memory_exhausted;
- begalt = b = bufp->buffer;
- }
- while (p != pend) {
- if (b - bufp->buffer > bufp->allocated - 10)
- /* Note that EXTEND_BUFFER clobbers c */
- EXTEND_BUFFER;
-
- PATFETCH(c);
-
- switch (c) {
- case '$':
- /*
- * $ means succeed if at end of line, but only in
- * special contexts. If randonly in the middle of a
- * pattern, it is a normal character.
- */
- if (p == pend || (*p == '\\' && (p[1] == ')' || p[1] == '|'))) {
- PATPUSH(endline);
- break;
- }
- goto normal_char;
-
- case '^':
- /*
- * ^ means succeed if at beg of line, but only if no
- * preceding pattern.
- */
- if (laststart)
- goto normal_char;
- PATPUSH(begline);
- break;
-
- case '*':
- case '+':
- case '?':
- /* If there is no previous pattern, char not special. */
- if (!laststart)
- goto normal_char;
- /*
- * If there is a sequence of repetition chars,
- * collapse it down to equivalent to just one.
- */
- zero_times_ok = 0;
- many_times_ok = 0;
- while (1) {
- zero_times_ok |= c != '+';
- many_times_ok |= c != '?';
- if (p == pend)
- break;
- PATFETCH(c);
- if (!(c == '*' || c == '+' || c == '?')) {
- PATUNFETCH;
- break;
- }
- }
-
- /*
- * Now we know whether 0 matches is allowed, and
- * whether 2 or more matches is allowed.
- */
- if (many_times_ok) {
- /*
- * If more than one repetition is allowed,
- * put in a backward jump at the end.
- */
- store_jump(b, maybe_finalize_jump, laststart - 3);
- b += 3;
- }
- insert_jump(on_failure_jump, laststart, b + 3, b);
- pending_exact = 0;
- b += 3;
- if (!zero_times_ok) {
- /*
- * At least one repetition required: insert
- * before the loop a skip over the initial
- * on-failure-jump instruction
- */
- insert_jump(dummy_failure_jump, laststart, laststart + 6, b);
- b += 3;
- }
- break;
-
- case '.':
- laststart = b;
- PATPUSH(anychar);
- break;
-
- case '[':
- if (b - bufp->buffer
- > bufp->allocated - 3 - (1 << BYTEWIDTH) / BYTEWIDTH)
- /* Note that EXTEND_BUFFER clobbers c */
- EXTEND_BUFFER;
-
- laststart = b;
- if (*p == '^')
- PATPUSH(charset_not), p++;
- else
- PATPUSH(charset);
- p1 = p;
-
- PATPUSH((1 << BYTEWIDTH) / BYTEWIDTH);
- /* Clear the whole map */
- bzero(b, (1 << BYTEWIDTH) / BYTEWIDTH);
- /* Read in characters and ranges, setting map bits */
- while (1) {
- PATFETCH(c);
- if (c == ']' && p != p1 + 1)
- break;
- if (*p == '-') {
- PATFETCH(c1);
- PATFETCH(c1);
- while (c <= c1)
- b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH), c++;
- } else {
- b[c / BYTEWIDTH] |= 1 << (c % BYTEWIDTH);
- }
- }
- /*
- * Discard any bitmap bytes that are all 0 at the end
- * of the map. Decrement the map-length byte too.
- */
- while (b[-1] > 0 && b[b[-1] - 1] == 0)
- b[-1]--;
- b += b[-1];
- break;
-
- case '\\':
- if (p == pend)
- goto invalid_pattern;
- PATFETCH_RAW(c);
- switch (c) {
- case '(':
- if (stackp == stacke)
- goto nesting_too_deep;
- if (regnum < RE_NREGS) {
- PATPUSH(start_memory);
- PATPUSH(regnum);
- }
- *stackp++ = b - bufp->buffer;
- *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0;
- *stackp++ = regnum++;
- *stackp++ = begalt - bufp->buffer;
- fixup_jump = 0;
- laststart = 0;
- begalt = b;
- break;
-
- case ')':
- if (stackp == stackb)
- goto unmatched_close;
- begalt = *--stackp + bufp->buffer;
- if (fixup_jump)
- store_jump(fixup_jump, jump, b);
- if (stackp[-1] < RE_NREGS) {
- PATPUSH(stop_memory);
- PATPUSH(stackp[-1]);
- }
- stackp -= 2;
- fixup_jump = 0;
- if (*stackp)
- fixup_jump = *stackp + bufp->buffer - 1;
- laststart = *--stackp + bufp->buffer;
- break;
-
- case '|':
- insert_jump(on_failure_jump, begalt, b + 6, b);
- pending_exact = 0;
- b += 3;
- if (fixup_jump)
- store_jump(fixup_jump, jump, b);
- fixup_jump = b;
- b += 3;
- laststart = 0;
- begalt = b;
- break;
-
- #ifdef emacs
- case '=':
- PATPUSH(at_dot);
- break;
-
- case 's':
- laststart = b;
- PATPUSH(syntaxspec);
- PATFETCH(c);
- PATPUSH(syntax_spec_code[c]);
- break;
-
- case 'S':
- laststart = b;
- PATPUSH(notsyntaxspec);
- PATFETCH(c);
- PATPUSH(syntax_spec_code[c]);
- break;
- #endif /* emacs */
-
- case 'w':
- laststart = b;
- PATPUSH(wordchar);
- break;
-
- case 'W':
- laststart = b;
- PATPUSH(notwordchar);
- break;
-
- case '<':
- PATPUSH(wordbeg);
- break;
-
- case '>':
- PATPUSH(wordend);
- break;
-
- case 'b':
- PATPUSH(wordbound);
- break;
-
- case 'B':
- PATPUSH(notwordbound);
- break;
-
- case '`':
- PATPUSH(begbuf);
- break;
-
- case '\'':
- PATPUSH(endbuf);
- break;
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- c1 = c - '0';
- if (c1 >= regnum)
- goto normal_char;
- for (stackt = stackp - 2; stackt > stackb; stackt -= 4)
- if (*stackt == c1)
- goto normal_char;
- laststart = b;
- PATPUSH(duplicate);
- PATPUSH(c1);
- break;
- default:
- goto normal_char;
- }
- break;
-
- default:
- normal_char:
- if (!pending_exact || pending_exact + *pending_exact + 1 != b
- || *pending_exact == 0177 || *p == '*' || *p == '^'
- || *p == '+' || *p == '?') {
- laststart = b;
- PATPUSH(exactn);
- pending_exact = b;
- PATPUSH(0);
- }
- PATPUSH(c);
- (*pending_exact)++;
- }
- }
-
- if (fixup_jump)
- store_jump(fixup_jump, jump, b);
-
- if (stackp != stackb)
- goto unmatched_open;
-
- bufp->used = b - bufp->buffer;
- return 0;
-
- invalid_pattern:
- return "Invalid regular expression";
-
- unmatched_open:
- return "Unmatched \\(";
-
- unmatched_close:
- return "Unmatched \\)";
-
- end_of_pattern:
- return "Premature end of regular expression";
-
- nesting_too_deep:
- return "Nesting too deep";
-
- too_big:
- return "Regular expression too big";
-
- memory_exhausted:
- return "Memory exhausted";
- }
-
- /*
- * Store where `from' points a jump operation to jump to where `to' points.
- * `opcode' is the opcode to store.
- */
-
- static VOID
- store_jump(from, opcode, to)
- char *from, *to;
- int opcode;
- {
- from[0] = (char) opcode;
- from[1] = (to - (from + 3)) & 0377;
- from[2] = (to - (from + 3)) >> 8;
- }
-
- /*
- * Open up space at char FROM, and insert there a jump to TO. CURRENT_END
- * gives te end of the storage no in use, so we know how much data to copy
- * up. OP is the opcode of the jump to insert.
- *
- * If you call this function, you must zero out pending_exact.
- */
-
- static VOID
- insert_jump(op, from, to, current_end)
- int op;
- char *from, *to, *current_end;
- {
- register char *pto = current_end + 3;
- register char *pfrom = current_end;
- while (pfrom != from)
- *--pto = *--pfrom;
- store_jump(from, op, to);
- }
-
-
- /*
- * Given a pattern, compute a fastmap from it. The fastmap records which of
- * the (1 << BYTEWIDTH) possible characters can start a string that matches
- * the pattern. This fastmap is used by re_search to skip quickly over
- * totally implausible text.
- *
- * The caller must supply the address of a (1 << BYTEWIDTH)-byte data area as
- * bufp->fastmap. The other components of bufp describe the pattern to be
- * used.
- */
-
- VOID
- re_compile_fastmap(bufp)
- struct re_pattern_buffer *bufp;
- {
- unsigned char *pattern = (unsigned char *) bufp->buffer;
- int size = bufp->used;
- register char *fastmap = bufp->fastmap;
- register unsigned char *p = pattern;
- register unsigned char *pend = pattern + size;
- register int j;
- #ifdef emacs
- register int k;
- #endif
- unsigned char *translate = (unsigned char *) bufp->translate;
-
- unsigned char *stackb[NFAILURES];
- unsigned char **stackp = stackb;
-
- bzero(fastmap, (1 << BYTEWIDTH));
- bufp->fastmap_accurate = 1;
- bufp->can_be_null = 0;
-
- while (p) {
- if (p == pend) {
- bufp->can_be_null = 1;
- break;
- }
- switch ((regexpcode) * p++) {
- case exactn:
- if (translate)
- fastmap[translate[p[1]]] = 1;
- else
- fastmap[p[1]] = 1;
- break;
-
- case begline:
- case before_dot:
- case at_dot:
- case after_dot:
- case begbuf:
- case endbuf:
- case wordbound:
- case notwordbound:
- case wordbeg:
- case wordend:
- continue;
-
- case endline:
- if (translate)
- fastmap[translate['\n']] = 1;
- else
- fastmap['\n'] = 1;
- bufp->can_be_null = 1;
- break;
-
- case finalize_jump:
- case maybe_finalize_jump:
- case jump:
- case dummy_failure_jump:
- bufp->can_be_null = 1;
- j = *p++ & 0377;
- j += SIGN_EXTEND_CHAR(*(char *) p++) << 8;
- p += j;
- if (j > 0)
- continue;
- /*
- * Jump backward reached implies we just went through
- * the body of a loop and matched nothing. Opcode
- * jumped to should be an on_failure_jump. Just treat
- * it like an ordinary jump. For a * loop, it has
- * pushed its failure point already; if so, discard
- * that as redundant.
- */
- if ((regexpcode) * p != on_failure_jump)
- continue;
- p++;
- j = *p++ & 0377;
- j += SIGN_EXTEND_CHAR(*(char *) p++) << 8;
- p += j;
- if (stackp != stackb && *stackp == p)
- stackp--;
- continue;
-
- case on_failure_jump:
- j = *p++ & 0377;
- j += SIGN_EXTEND_CHAR(*(char *) p++) << 8;
- *++stackp = p + j;
- continue;
-
- case start_memory:
- case stop_memory:
- p++;
- continue;
-
- case duplicate:
- bufp->can_be_null = 1;
- fastmap['\n'] = 1;
- case anychar:
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (j != '\n')
- fastmap[j] = 1;
- if (bufp->can_be_null)
- return;
- /*
- * Don't return; check the alternative paths so we
- * can set can_be_null if appropriate.
- */
- break;
-
- case wordchar:
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (SYNTAX(j) == Sword)
- fastmap[j] = 1;
- break;
-
- case notwordchar:
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (SYNTAX(j) != Sword)
- fastmap[j] = 1;
- break;
-
- #ifdef emacs
- case syntaxspec:
- k = *p++;
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (SYNTAX(j) == (enum syntaxcode) k)
- fastmap[j] = 1;
- break;
-
- case notsyntaxspec:
- for (j = 0; j < (1 << BYTEWIDTH); j++)
- if (SYNTAX(j) != (enum syntaxcode) k)
- fastmap[j] = 1;
- break;
- #endif /* emacs */
-
- case charset:
- for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
- if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) {
- if (translate)
- fastmap[translate[j]] = 1;
- else
- fastmap[j] = 1;
- }
- break;
-
- case charset_not:
- /* Chars beyond end of map must be allowed */
- for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
- if (translate)
- fastmap[translate[j]] = 1;
- else
- fastmap[j] = 1;
-
- for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
- if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) {
- if (translate)
- fastmap[translate[j]] = 1;
- else
- fastmap[j] = 1;
- }
- break;
- }
-
- /*
- * Get here means we have successfully found the possible
- * starting characters of one path of the pattern. We need
- * not follow this path any farther. Instead, look at the
- * next alternative remembered in the stack.
- */
- if (stackp != stackb)
- p = *stackp--;
- else
- break;
- }
- }
-
-
- /* Like re_search_2, below, but only one string is specified. */
-
- int
- re_search(pbufp, string, size, startpos, range, regs)
- struct re_pattern_buffer *pbufp;
- char *string;
- int size, startpos, range;
- struct re_registers *regs;
- {
- return re_search_2(pbufp, (char *) 0, 0, string, size, startpos, range, regs, size);
- }
-
- /*
- * Like re_match_2 but tries first a match starting at index `startpos', then
- * at startpos + 1, and so on. `range' is the number of places to try before
- * giving up. If `range' is negative, the starting positions tried are
- * startpos, startpos - 1, etc. It is up to the caller to make sure that
- * range is not so large as to take the starting position outside of the
- * input strings.
- *
- * The value returned is the position at which the match was found, or -1 if no
- * match was found.
- */
-
- int
- re_search_2(pbufp, string1, size1, string2, size2, startpos, range, regs, mstop)
- struct re_pattern_buffer *pbufp;
- char *string1, *string2;
- int size1, size2;
- int startpos;
- register int range;
- struct re_registers *regs;
- int mstop;
- {
- register char *fastmap = pbufp->fastmap;
- register char *translate = pbufp->translate;
- int total = size1 + size2;
-
- /* Update the fastmap now if not correct already */
- if (fastmap && !pbufp->fastmap_accurate)
- re_compile_fastmap(pbufp);
-
- while (1) {
- /*
- * If a fastmap is supplied, skip quickly over characters
- * that cannot possibly be the start of a match. Note,
- * however, that if the pattern can possibly match the null
- * string, we must test it at each starting point so that we
- * take the first null string we get.
- */
-
- if (fastmap && startpos < total && !pbufp->can_be_null) {
- if (range > 0) {
- register int lim = 0;
- register char *p;
- int irange = range;
- if (startpos < size1 && startpos + range >= size1)
- lim = range - (size1 - startpos);
-
- p = &(startpos >= size1 ? string2 - size1 : string1)[startpos];
-
- if (translate) {
- while (range > lim && !fastmap[translate[*p++]])
- range--;
- } else {
- while (range > lim && !fastmap[*p++])
- range--;
- }
- startpos += irange - range;
- } else {
- register char c;
- if (startpos >= size1)
- c = string2[startpos - size1];
- else
- c = string1[startpos];
- if (translate ? !fastmap[translate[c]] : !fastmap[c])
- goto advance;
- }
- }
- if (range >= 0 && startpos == total
- && fastmap && !pbufp->can_be_null)
- return -1;
-
- if (0 <= re_match_2(pbufp, string1, size1, string2, size2, startpos, regs, mstop))
- return startpos;
-
- #ifdef C_ALLOCA
- alloca(0);
- #endif /* C_ALLOCA */
-
- advance:
- if (!range)
- break;
- if (range > 0)
- range--, startpos++;
- else
- range++, startpos--;
- }
- return -1;
- }
-
-
- #ifndef emacs /* emacs never uses this */
- int
- re_match(pbufp, string, size, pos, regs)
- struct re_pattern_buffer *pbufp;
- char *string;
- int size, pos;
- struct re_registers *regs;
- {
- return re_match_2(pbufp, 0, 0, string, size, pos, regs, size);
- }
- #endif /* emacs */
-
- /*
- * Match the pattern described by `pbufp' against data which is the virtual
- * concatenation of `string1' and `string2'. `size1' and `size2' are the
- * sizes of the two data strings. Start the match at position `pos'. Do not
- * consider matching past the position `mstop'.
- *
- * If pbufp->fastmap is nonzero, then it had better be up to date.
- *
- * The reason that the data to match is specified as two components which are to
- * be regarded as concatenated is so that this function can be used directly
- * on the contents of an Emacs buffer.
- *
- * -1 is returned if there is no match. Otherwise the value is the length of
- * the substring which was matched.
- */
-
- int
- re_match_2(pbufp, string1, size1, string2, size2, pos, regs, mstop)
- struct re_pattern_buffer *pbufp;
- char *string1, *string2;
- int size1, size2;
- int pos;
- struct re_registers *regs;
- int mstop;
- {
- register char *p = pbufp->buffer;
- register char *pend = p + pbufp->used;
- /* End of first string */
- char *end1;
- /* End of second string */
- char *end2;
- /* Pointer just past last char to consider matching */
- char *end_match_1, *end_match_2;
- register char *d, *dend;
- register int mcnt;
- char *translate = pbufp->translate;
-
- /*
- * Failure point stack. Each place that can handle a failure further
- * down the line pushes a failure point on this stack. It consists
- * of two char *'s. The first one pushed is where to resume scanning
- * the pattern; the second pushed is where to resume scanning the
- * strings. If the latter is zero, the failure point is a "dummy". If
- * a failure happens and the innermost failure point is dormant, it
- * discards that failure point and tries the next one.
- */
-
- char **stackb = (char **) alloca(2 * NFAILURES * sizeof(char *));
- char **stackp = stackb, **stacke = &stackb[2 * NFAILURES];
-
- /*
- * Information on the "contents" of registers. These are pointers
- * into the input strings; they record just what was matched (on this
- * attempt) by some part of the pattern. The start_memory command
- * stores the start of a register's contents and the stop_memory
- * command stores the end.
- *
- * At that point, regstart[regnum] points to the first character in the
- * register, regend[regnum] points to the first character beyond the
- * end of the register, and regstart_segend[regnum] is either the
- * same as regend[regnum] or else points to the end of the input
- * string into which regstart[regnum] points. The latter case happens
- * when regstart[regnum] is in string1 and regend[regnum] is in
- * string2.
- */
-
- char *regstart[RE_NREGS];
- char *regstart_segend[RE_NREGS];
- char *regend[RE_NREGS];
-
- /*
- * Set up pointers to ends of strings. Don't allow the second string
- * to be empty unless both are empty.
- */
- if (!size2) {
- string2 = string1;
- size2 = size1;
- string1 = 0;
- size1 = 0;
- }
- end1 = string1 + size1;
- end2 = string2 + size2;
-
- /* Compute where to stop matching, within the two strings */
- if (mstop <= size1) {
- end_match_1 = string1 + mstop;
- end_match_2 = string2;
- } else {
- end_match_1 = end1;
- end_match_2 = string2 + mstop - size1;
- }
-
- /*
- * Initialize \( and \) text positions to -1 to mark ones that no \(
- * or \) has been seen for.
- */
-
- for (mcnt = 0; mcnt < sizeof(regstart) / sizeof(*regstart); mcnt++)
- regstart[mcnt] = (char *) -1;
-
- /*
- * `p' scans through the pattern as `d' scans through the data.
- * `dend' is the end of the input string that `d' points within. `d'
- * is advanced into the following input string whenever necessary,
- * but this happens before fetching; therefore, at the beginning of
- * the loop, `d' can be pointing at the end of a string, but it
- * cannot equal string2.
- */
-
- if (pos <= size1)
- d = string1 + pos, dend = end_match_1;
- else
- d = string2 + pos - size1, dend = end_match_2;
-
- /* Write PREFETCH; just before fetching a character with *d. */
- #define PREFETCH \
- while (d == dend) \
- { if (dend == end_match_2) goto fail; /* end of string2 => failure */ \
- d = string2; /* end of string1 => advance to string2. */ \
- dend = end_match_2; }
-
- /*
- * This loop loops over pattern commands. It exits by returning from
- * the function if match is complete, or it drops through if match
- * fails at this starting point in the input data.
- */
-
- while (1) {
- if (p == pend)
- /* End of pattern means we have succeeded! */
- {
- /*
- * If caller wants register contents data back,
- * convert it to indices
- */
- if (regs) {
- regend[0] = d;
- regstart[0] = string1;
- for (mcnt = 0; mcnt < RE_NREGS; mcnt++) {
- if ((mcnt != 0) && regstart[mcnt] == (char *) -1) {
- regs->start[mcnt] = -1;
- regs->end[mcnt] = -1;
- continue;
- }
- if (regstart[mcnt] - string1 < 0 ||
- regstart[mcnt] - string1 > size1)
- regs->start[mcnt] = regstart[mcnt] - string2 + size1;
- else
- regs->start[mcnt] = regstart[mcnt] - string1;
- if (regend[mcnt] - string1 < 0 ||
- regend[mcnt] - string1 > size1)
- regs->end[mcnt] = regend[mcnt] - string2 + size1;
- else
- regs->end[mcnt] = regend[mcnt] - string1;
- }
- regs->start[0] = pos;
- }
- if (d - string1 >= 0 && d - string1 <= size1)
- return d - string1 - pos;
- else
- return d - string2 + size1 - pos;
- }
- /* Otherwise match next pattern command */
- switch ((regexpcode) * p++) {
-
- /*
- * \( is represented by a start_memory, \) by a
- * stop_memory. Both of those commands contain a
- * "register number" argument. The text matched
- * within the \( and \) is recorded under that
- * number. Then, \<digit> turns into a `duplicate'
- * command which is followed by the numeric value of
- * <digit> as the register number.
- */
-
- case start_memory:
- regstart[*p] = d;
- regstart_segend[*p++] = dend;
- break;
-
- case stop_memory:
- regend[*p] = d;
- if (regstart_segend[*p] == dend)
- regstart_segend[*p] = d;
- p++;
- break;
-
- case duplicate:
- {
- int regno = *p++; /* Get which register to
- * match against */
- register char *d2, *dend2;
-
- d2 = regstart[regno];
- dend2 = regstart_segend[regno];
- while (1) {
- /*
- * Advance to next segment in
- * register contents, if necessary
- */
- while (d2 == dend2) {
- if (dend2 == end_match_2)
- break;
- if (dend2 == regend[regno])
- break;
- d2 = string2, dend2 = regend[regno]; /* end of string1 =>
- * advance to string2. */
- }
- /*
- * At end of register contents =>
- * success
- */
- if (d2 == dend2)
- break;
-
- /*
- * Advance to next segment in data
- * being matched, if necessary
- */
- PREFETCH;
-
- /*
- * mcnt gets # consecutive chars to
- * compare
- */
- mcnt = dend - d;
- if (mcnt > dend2 - d2)
- mcnt = dend2 - d2;
- /*
- * Compare that many; failure if
- * mismatch, else skip them.
- */
- if (translate ? bcmp_translate(d, d2, mcnt, translate) : bcmp(d, d2, mcnt))
- goto fail;
- d += mcnt, d2 += mcnt;
- }
- }
- break;
-
- case anychar:
- /* fetch a data character */
- PREFETCH;
- /* Match anything but a newline. */
- if ((translate ? translate[*d++] : *d++) == '\n')
- goto fail;
- break;
-
- case charset:
- case charset_not:
- {
- /* Nonzero for charset_not */
- int not = 0;
- register int c;
- if (*(p - 1) == (char) charset_not)
- not = 1;
-
- /* fetch a data character */
- PREFETCH;
-
- if (translate)
- c = translate[*(unsigned char *) d];
- else
- c = *(unsigned char *) d;
-
- if (c < *p * BYTEWIDTH
- && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
- not = !not;
-
- p += 1 + *p;
-
- if (!not)
- goto fail;
- d++;
- break;
- }
-
- case begline:
- if (d == string1 || d[-1] == '\n')
- break;
- goto fail;
-
- case endline:
- if (d == end2
- || (d == end1 ? (size2 == 0 || *string2 == '\n') : *d == '\n'))
- break;
- goto fail;
-
- /*
- * "or" constructs ("|") are handled by starting each
- * alternative with an on_failure_jump that points to
- * the start of the next alternative. Each
- * alternative except the last ends with a jump to
- * the joining point. (Actually, each jump except for
- * the last one really jumps to the following jump,
- * because tensioning the jumps is a hassle.)
- */
-
- /*
- * The start of a stupid repeat has an
- * on_failure_jump that points past the end of the
- * repeat text. This makes a failure point so that,
- * on failure to match a repetition, matching
- * restarts past as many repetitions have been found
- * with no way to fail and look for another one.
- */
-
- /*
- * A smart repeat is similar but loops back to the
- * on_failure_jump so that each repetition makes
- * another failure point.
- */
-
- case on_failure_jump:
- if (stackp == stacke) {
- char **stackx = (char **) alloca(2 * (stacke - stackb) * sizeof(char *));
- bcopy((char *) stackb, (char *) stackx, (stacke - stackb) * sizeof(char *));
- stackp += stackx - stackb;
- stacke = stackx + 2 * (stacke - stackb);
- stackb = stackx;
- }
- mcnt = *p++ & 0377;
- mcnt += SIGN_EXTEND_CHAR(*p++) << 8;
- *stackp++ = mcnt + p;
- *stackp++ = d;
- break;
-
- /*
- * The end of a smart repeat has an
- * maybe_finalize_jump back. Change it either to a
- * finalize_jump or an ordinary jump.
- */
-
- case maybe_finalize_jump:
- mcnt = *p++ & 0377;
- mcnt += SIGN_EXTEND_CHAR(*p++) << 8;
- /*
- * Compare what follows with the begining of the
- * repeat. If we can establish that there is nothing
- * that they would both match, we can change to
- * finalize_jump
- */
- if (p == pend)
- p[-3] = (char) finalize_jump;
- else if (*p == (char) exactn || *p == (char) endline) {
- register int c = *p == (char) endline ? '\n' : p[2];
- register char *p1 = p + mcnt;
- /*
- * p1[0] ... p1[2] are an on_failure_jump.
- * Examine what follows that
- */
- if (p1[3] == (char) exactn && p1[5] != c)
- p[-3] = (char) finalize_jump;
- else if (p1[3] == (char) charset || p1[3] == (char) charset_not) {
- int not = p1[3] == (char) charset_not;
- if (c < p1[4] * BYTEWIDTH
- && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
- not = !not;
- /* not is 1 if c would match */
- /*
- * That means it is not safe to
- * finalize
- */
- if (!not)
- p[-3] = (char) finalize_jump;
- }
- }
- p -= 2;
- if (p[-1] != (char) finalize_jump) {
- p[-1] = (char) jump;
- goto nofinalize;
- }
- /*
- * The end of a stupid repeat has a finalize-jump
- * back to the start, where another failure point
- * will be made which will point after all the
- * repetitions found so far.
- */
-
- case finalize_jump:
- stackp -= 2;
-
- case jump:
- nofinalize:
- mcnt = *p++ & 0377;
- mcnt += SIGN_EXTEND_CHAR(*p++) << 8;
- p += mcnt;
- break;
-
- case dummy_failure_jump:
- if (stackp == stacke) {
- char **stackx = (char **) alloca(2 * (stacke - stackb) * sizeof(char *));
- bcopy((char *) stackb, (char *) stackx, (stacke - stackb) * sizeof(char *));
- stackp += stackx - stackb;
- stacke = stackx + 2 * (stacke - stackb);
- stackb = stackx;
- }
- *stackp++ = 0;
- *stackp++ = 0;
- goto nofinalize;
-
- case wordbound:
- if (d == string1 /* Points to first char */
- || d == end2 /* Points to end */
- || (d == end1 && size2 == 0)) /* Points to end */
- break;
- if ((SYNTAX(((unsigned char *) d)[-1]) == Sword)
- != (SYNTAX(d == end1 ? *(unsigned char *) string2 : *(unsigned char *) d) == Sword))
- break;
- goto fail;
-
- case notwordbound:
- if (d == string1 /* Points to first char */
- || d == end2 /* Points to end */
- || (d == end1 && size2 == 0)) /* Points to end */
- goto fail;
- if ((SYNTAX(((unsigned char *) d)[-1]) == Sword)
- != (SYNTAX(d == end1 ? *(unsigned char *) string2 : *(unsigned char *) d) == Sword))
- goto fail;
- break;
-
- case wordbeg:
- if (d == end2 /* Points to end */
- || (d == end1 && size2 == 0) /* Points to end */
- ||SYNTAX(*(unsigned char *) (d == end1 ? string2 : d)) != Sword) /* Next char not a
- * letter */
- goto fail;
- if (d == string1 /* Points to first char */
- || SYNTAX(((unsigned char *) d)[-1]) != Sword) /* prev char not letter */
- break;
- goto fail;
-
- case wordend:
- if (d == string1 /* Points to first char */
- || SYNTAX(((unsigned char *) d)[-1]) != Sword) /* prev char not letter */
- goto fail;
- if (d == end2 /* Points to end */
- || (d == end1 && size2 == 0) /* Points to end */
- ||SYNTAX(d == end1 ? *(unsigned char *) string2 : *(unsigned char *) d) != Sword) /* Next char not a
- * letter */
- break;
- goto fail;
-
- #ifdef emacs
- case before_dot:
- if (((d - string2 <= (unsigned) size2)
- ? d - (char *) bf_p2 : d - (char *) bf_p1)
- <= point)
- goto fail;
- break;
-
- case at_dot:
- if (((d - string2 <= (unsigned) size2)
- ? d - (char *) bf_p2 : d - (char *) bf_p1)
- == point)
- goto fail;
- break;
-
- case after_dot:
- if (((d - string2 <= (unsigned) size2)
- ? d - (char *) bf_p2 : d - (char *) bf_p1)
- >= point)
- goto fail;
- break;
-
- case wordchar:
- mcnt = (int) Sword;
- goto matchsyntax;
-
- case syntaxspec:
- mcnt = *p++;
- matchsyntax:
- PREFETCH;
- if (SYNTAX(*(unsigned char *) d++) != (enum syntaxcode) mcnt)
- goto fail;
- break;
-
- case notwordchar:
- mcnt = (int) Sword;
- goto matchnotsyntax;
-
- case notsyntaxspec:
- mcnt = *p++;
- matchnotsyntax:
- PREFETCH;
- if (SYNTAX(*(unsigned char *) d++) == (enum syntaxcode) mcnt)
- goto fail;
- break;
- #else
- case wordchar:
- PREFETCH;
- if (SYNTAX(*(unsigned char *) d++) == 0)
- goto fail;
- break;
-
- case notwordchar:
- PREFETCH;
- if (SYNTAX(*(unsigned char *) d++) != 0)
- goto fail;
- break;
- #endif /* not emacs */
-
- case begbuf:
- if (d == string1) /* Note, d cannot equal
- * string2 */
- break; /* unless string1 == string2. */
- goto fail;
-
- case endbuf:
- if (d == end2 || (d == end1 && size2 == 0))
- break;
- goto fail;
-
- case exactn:
- /*
- * Match the next few pattern characters exactly.
- * mcnt is how many characters to match.
- */
- mcnt = *p++;
- if (translate) {
- do {
- PREFETCH;
- if (translate[*(unsigned char *) d++] != *p++)
- goto fail;
- }
- while (--mcnt);
- } else {
- do {
- PREFETCH;
- if (*d++ != *p++)
- goto fail;
- }
- while (--mcnt);
- }
- break;
- }
- continue; /* Successfully matched one pattern command;
- * keep matching */
-
- /* Jump here if any matching operation fails. */
- fail:
- if (stackp != stackb)
- /*
- * A restart point is known. Restart there and pop
- * it.
- */
- {
- if (!stackp[-2]) { /* If innermost failure point
- * is dormant, flush it and
- * keep looking */
- stackp -= 2;
- goto fail;
- }
- d = *--stackp;
- p = *--stackp;
- if (d >= string1 && d <= end1)
- dend = end_match_1;
- } else
- break; /* Matching at this starting point really
- * fails! */
- }
- return -1; /* Failure to match */
- }
-
- static int
- bcmp_translate(s1, s2, len, translate)
- char *s1, *s2;
- register int len;
- char *translate;
- {
- register char *p1 = s1, *p2 = s2;
- while (len) {
- if (translate[*p1++] != translate[*p2++])
- return 1;
- len--;
- }
- return 0;
- }
-
-
- /* Entry points compatible with bsd4.2 regex library */
-
- #ifndef emacs
-
- static struct re_pattern_buffer re_comp_buf;
-
- char *
- re_comp(s)
- char *s;
- {
- if (!s) {
- if (!re_comp_buf.buffer)
- return "No previous regular expression";
- return 0;
- }
- if (!re_comp_buf.buffer) {
- if (!(re_comp_buf.buffer = (char *) malloc(200)))
- return "Memory exhausted";
- re_comp_buf.allocated = 200;
- if (!(re_comp_buf.fastmap = (char *) malloc(1 << BYTEWIDTH)))
- return "Memory exhausted";
- }
- return re_compile_pattern(s, strlen(s), &re_comp_buf);
- }
-
- int
- re_exec(s)
- char *s;
- {
- int len = strlen(s);
- return 0 <= re_search(&re_comp_buf, s, len, 0, len, 0);
- }
-
- #endif /* emacs */
-
-
- #ifdef test
-
- #include <stdio.h>
-
- /* Indexed by a character, gives the upper case equivalent of the character */
-
- static char upcase[0400] =
- {000, 001, 002, 003, 004, 005, 006, 007,
- 010, 011, 012, 013, 014, 015, 016, 017,
- 020, 021, 022, 023, 024, 025, 026, 027,
- 030, 031, 032, 033, 034, 035, 036, 037,
- 040, 041, 042, 043, 044, 045, 046, 047,
- 050, 051, 052, 053, 054, 055, 056, 057,
- 060, 061, 062, 063, 064, 065, 066, 067,
- 070, 071, 072, 073, 074, 075, 076, 077,
- 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
- 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
- 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
- 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
- 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
- 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
- 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
- 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
- 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
- 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
- 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
- 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
- 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
- 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
- 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
- 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
- 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
- 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
- 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
- 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
- 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
- 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
- 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
- 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
- };
-
- main()
- {
- char pat[80];
- struct re_pattern_buffer buf;
- struct re_registers regs;
- int i;
- char c;
- char fastmap[(1 << BYTEWIDTH)];
-
- buf.allocated = 40;
- buf.buffer = (char *) malloc(buf.allocated);
- buf.fastmap = fastmap;
- buf.translate = upcase;
-
- while (1) {
- printf("Enter pattern\n");
- gets(pat);
-
- if (*pat) {
- re_compile_pattern(pat, strlen(pat), &buf);
-
- for (i = 0; i < buf.used; i++)
- printchar(buf.buffer[i]);
-
- putchar('\n');
-
- printf("%d allocated, %d used.\n", buf.allocated, buf.used);
-
- re_compile_fastmap(&buf);
- printf("Allowed by fastmap: ");
- for (i = 0; i < (1 << BYTEWIDTH); i++)
- if (fastmap[i])
- printchar(i);
- putchar('\n');
- }
- printf("enter string to search\n");
- gets(pat); /* Now read the string to match against */
-
- /* i = re_match (&buf, pat, strlen (pat), 0, 0); */
- i = re_search(&buf, pat, strlen(pat), 0, strlen(pat), ®s);
- printf("Match value %d.\n", i);
- for (i = 0; i < RE_NREGS; i++)
- printf("%2d start %2d end %2d\n", i, regs.start[i], regs.end[i]);
- }
- }
-
- #ifdef NOTDEF
- print_buf(bufp)
- struct re_pattern_buffer *bufp;
- {
- int i;
-
- printf("buf is :\n----------------\n");
- for (i = 0; i < bufp->used; i++)
- printchar(bufp->buffer[i]);
-
- printf("\n%d allocated, %d used.\n", bufp->allocated, bufp->used);
-
- printf("Allowed by fastmap: ");
- for (i = 0; i < (1 << BYTEWIDTH); i++)
- if (bufp->fastmap[i])
- printchar(i);
- printf("\nAllowed by translate: ");
- if (bufp->translate)
- for (i = 0; i < (1 << BYTEWIDTH); i++)
- if (bufp->translate[i])
- printchar(i);
- printf("\nfastmap is%s accurate\n", bufp->fastmap_accurate ? "" : "n't");
- printf("can %s be null\n----------", bufp->can_be_null ? "" : "not");
- }
- #endif
-
- printchar(c)
- char c;
- {
- if (c < 041 || c >= 0177) {
- putchar('\\');
- putchar(((c >> 6) & 3) + '0');
- putchar(((c >> 3) & 7) + '0');
- putchar((c & 7) + '0');
- } else
- putchar(c);
- }
-
- error(string)
- char *string;
- {
- puts(string);
- exit(1);
- }
-
- #endif /* test */
- #else
- #include "nullfile.h"
- #endif
-