/* reparse.c - parse a regular expression * * cl /c /Zep /AM /NT RE /Gs /G2 /Oa /D LINT_ARGS /Fc reparse.c * * Modifications: * * 22-Jul-1986 mz Hookable allocator (allow Z to create enough free space) * 19-Nov-1986 mz Add RETranslateLength for Z to determine overflows * 18-Aug-1987 mz Add field width and justification in translations * 01-Mar-1988 mz Add in UNIX-like syntax * 14-Jun-1988 mz Fix file parts allowing backslashes * 04-Dec-1989 bp Let :p accept uppercase drive names * 20-Dec-1989 ln capture trailing periods in :p * 23-Jan-1990 ln Handle escaped characters & invalid trailing \ in * RETranslate. * * 28-Jul-1990 davegi Changed Fill to memset (OS/2 2.0) * Changed Move to memmove (OS/2 2.0) * 19-Oct-1990 w-barry changed cArg to unsigned int from int. */ #include #include #include #include #include #include #include #include #include "re.h" #if DEBUG #define DEBOUT(x) printf x; fflush (stdout) #else #define DEBOUT(x) #endif /* regular expression compiler. A regular expression is compiled into pseudo- * machine code. The principle is portable to other machines and is outlined * below. We parse by recursive descent. * * The pseudo-code is fairly close to normal assembler and can be easily * converted to be real machine code and has been done for the 80*86 * processor family. * * The basic regular expressions handled are: * * letter matches a single letter * [class] matches a single character in the class * [~class] matches a single character not in the class * ^ matches the beginning of the line * $ matches the end of the line * ? matches any character (except previous two) * \x literal x * \n matches the previously tagged/matched expression (n digit) * * Regular expressions are now build from the above via: * * x* matches 0 or more x, matching minimal number * x+ matches 1 or more x, matching minimal number * x@ matches 0 or more x, matching maximal number * x# matches 1 or more x, matching maximal number * (x1!x2!...) matches x1 or x2 or ... * ~x matches 0 characters but prevents x from occuring * {x} identifies an argument * * The final expression that is matched by the compiler is: * * xy matches x then y * * * The actual grammar used is: Parsing action: * * TOP -> re PROLOG .re. EPILOG * * * re -> { re } re | LEFTARG .re. RIGHTARG * e re | * empty * * e -> se * | SMSTAR .se. SMSTAR1 * se + | * se @ | STAR .se. STAR1 * se # | * se * * se -> ( alt ) | * [ ccl ] | * ? | ANY * ^ | BOL * $ | EOL * ~ se | NOTSIGN .se. NOTSIGN1 * :x | * \n | PREV * letter LETTER x * * alt -> re ! alt | LEFTOR .re. ORSIGN * re LEFTOR .re. ORSIGN RIGHTOR * * ccl -> ~ cset | CCLBEG NOTSIGN .cset. CCLEND * cset CCLBEG NULL .cset. CCLEND * * cset -> item cset | * item * * item -> letter - letter | RANGE x y * letter RANGE x x * * Abbreviations are introduced by :. * * :a [a-zA-Z0-9] alphanumeric * :b ([]#) whitespace * :c [a-zA-Z] alphabetic * :d [0-9] digit * :f ([~/\\ "\[\]\:<|>+=;,.]#) file part * :h ([0-9a-fA-F]#) hex number * :i ([a-zA-Z_$][a-zA-Z0-9_$]@) identifier * :n ([0-9]#.[0-9]@![0-9]@.[0-9]#![0-9]#) number * :p (([A-Za-z]\:!)(\\!)(:f(.:f!)(\\!/))@:f(.:f!.!)) path * :q ("[~"]@"!'[~']@') quoted string * :w ([a-zA-Z]#) word * :z ([0-9]#) integer * */ extern char XLTab[256]; /* lower-casing table */ /* There are several classes of characters: * * Closure characters are suffixes that indicate repetition of the previous * RE. * * Simple RE chars are characters that indicate a particular type of match * */ /* Closure character equates */ #define CCH_SMPLUS 0 /* plus closure */ #define CCH_SMCLOSURE 1 /* star closure */ #define CCH_POWER 2 /* n repetitions of previous pattern */ #define CCH_CLOSURE 3 /* greedy closure */ #define CCH_PLUS 4 /* greedy plus */ #define CCH_NONE 5 #define CCH_ERROR -1 /* Simple RE character equates */ #define SR_BOL 0 #define SR_EOL 1 #define SR_ANY 2 #define SR_CCLBEG 3 #define SR_LEFTOR 4 #define SR_CCLEND 5 #define SR_ABBREV 6 #define SR_RIGHTOR 7 #define SR_ORSIGN 8 #define SR_NOTSIGN 9 #define SR_LEFTARG 10 #define SR_RIGHTARG 11 #define SR_LETTER 12 #define SR_PREV 13 int EndAltRE[] = { SR_ORSIGN, SR_RIGHTOR, -1}; int EndArg[] = { SR_RIGHTARG, -1}; char *pAbbrev[] = { "a[a-zA-Z0-9]", "b([ \t]#)", "c[a-zA-Z]", "d[0-9]", "f([~/\\\\ \\\"\\[\\]\\:<|>+=;,.]#!..!.)", "h([0-9a-fA-F]#)", "i([a-zA-Z_$][a-zA-Z0-9_$]@)", "n([0-9]#.[0-9]@![0-9]@.[0-9]#![0-9]#)", "p(([A-Za-z]\\:!)(\\\\!/!)(:f(.:f!)(\\\\!/))@:f(.:f!.!))", "q(\"[~\"]@\"!'[~']@')", "w([a-zA-Z]#)", "z([0-9]#)", NULL }; static char *digits = "0123456789"; static flagType fZSyntax = TRUE; /* TRUE => use Z syntax for things */ static unsigned int cArg; /* RECharType - classify a character type * * p character pointer * * returns type of character (SR_xx) */ int RECharType ( char *p ) { if (fZSyntax) /* Zibo syntax */ switch (*p) { case '^': return SR_BOL; case '$': if (isdigit (p[1])) return SR_PREV; else return SR_EOL; case '?': return SR_ANY; case '[': return SR_CCLBEG; case '(': return SR_LEFTOR; case ']': return SR_CCLEND; case ':': return SR_ABBREV; case ')': return SR_RIGHTOR; case '!': return SR_ORSIGN; case '~': return SR_NOTSIGN; case '{': return SR_LEFTARG; case '}': return SR_RIGHTARG; default: return SR_LETTER; } else /* Crappy UNIX syntax */ switch (*p) { case '^': return SR_BOL; case '$': return SR_EOL; case '.': return SR_ANY; case '[': return SR_CCLBEG; case ']': return SR_CCLEND; case '\\': switch (p[1]) { case ':': /* \:C */ return SR_ABBREV; case '(': /* \( */ return SR_LEFTARG; case ')': /* \) */ return SR_RIGHTARG; case '~': /* \~ */ return SR_NOTSIGN; case '{': /* \{ */ return SR_LEFTOR; case '}': /* \} */ return SR_RIGHTOR; case '!': /* \! */ return SR_ORSIGN; } if (isdigit (p[1])) /* \N */ return SR_PREV; default: return SR_LETTER; } } /* RECharLen - length of character type * * p character pointer to type * * returns length in chars of type */ int RECharLen ( char *p ) { if (fZSyntax) if (RECharType (p) == SR_PREV) /* $N */ return 2; else if (RECharType (p) == SR_ABBREV) /* :N */ return 2; else return 1; else { if (*p == '\\') switch (p[1]) { case '{': case '}': case '~': case '(': case ')': case '!': return 2; /* \C */ case ':': /* \:C */ return 3; default: if (isdigit (p[1])) return 2; /* \N */ else return 1; } return 1; } } /* REClosureLen - length of character type * * p character pointer to type * * returns length in chars of type */ int REClosureLen ( char *p ) { p; return 1; } /* REParseRE - parse a general RE up to but not including the pEnd set * of chars. Apply a particular action to each node in the parse tree. * * pAction Parse action routine to call at particluar points in the * parse tree. This routine returns an unsigned quantity that * is expected to be passed on to other action calls within the * same node. * p character pointer to string being parsed * pEnd pointer to set of char types that end the current RE. * External callers will typically use NULL for this value. * Internally, however, we need to break on the ALT-terminating * types or on arg-terminating types. * * Returns: pointer to delimited character if successful parse * NULL if unsuccessful parse (syntax error). * */ char * REParseRE ( PACT pAction, register char *p, int *pEnd ) { int *pe; UINT_PTR u; DEBOUT (("REParseRE (%04x, %s)\n", pAction, p)); while (TRUE) { /* If we're at end of input */ if (*p == '\0') /* If we're not in the midst of an open expression */ if (pEnd == NULL) /* return the current parse position */ return p; else { /* End of input, but expecting more, ERROR */ DEBOUT (("REParse expecting more, ERROR\n")); return NULL; } /* If there is an open expression */ if (pEnd != NULL) /* Find a matching character */ for (pe = pEnd; *pe != -1; pe++) if (RECharType (p) == *pe) return p; /* If we are looking at a left argument */ if (RECharType (p) == SR_LEFTARG) { /* Parse LEFTARG .re. RIGHTARG */ u = (*pAction) (LEFTARG, 0, '\0', '\0'); if ((p = REParseRE (pAction, p + RECharLen (p), EndArg)) == NULL) return NULL; (*pAction) (RIGHTARG, u, '\0', '\0'); cArg++; p += RECharLen (p); } else /* Parse .e. */ if ((p = REParseE (pAction, p)) == NULL) return NULL; } } /* REParseE - parse a simple regular expression with potential closures. * * pAction Action to apply at special parse nodes * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseE ( PACT pAction, register char *p ) { DEBOUT (("REParseE (%04x, %s)\n", pAction, p)); switch (REClosureChar (p)) { case CCH_SMPLUS: if (REParseSE (pAction, p) == NULL) return NULL; case CCH_SMCLOSURE: return REParseClosure (pAction, p); case CCH_PLUS: if (REParseSE (pAction, p) == NULL) return NULL; case CCH_CLOSURE: return REParseGreedy (pAction, p); case CCH_POWER: return REParsePower (pAction, p); case CCH_NONE: return REParseSE (pAction, p); default: return NULL; } } /* REParseSE - parse a simple regular expression * * pAction Action to apply at special parse nodes * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseSE ( register PACT pAction, register char *p ) { DEBOUT (("REParseSE (%04x, %s)\n", pAction, p)); switch (RECharType (p)) { case SR_CCLBEG: return REParseClass (pAction, p); case SR_ANY: return REParseAny (pAction, p); case SR_BOL: return REParseBOL (pAction, p); case SR_EOL: return REParseEOL (pAction, p); case SR_PREV: return REParsePrev (pAction, p); case SR_LEFTOR: return REParseAlt (pAction, p); case SR_NOTSIGN: return REParseNot (pAction, p); case SR_ABBREV: return REParseAbbrev (pAction, p); default: return REParseChar (pAction, p); } } /* REParseClass - parse a class membership match * * pAction Action to apply at beginning of parse and at each range * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseClass ( PACT pAction, register char *p ) { char c; UINT_PTR u; DEBOUT (("REParseClass (%04x, %s)\n", pAction, p)); p += RECharLen (p); if ((fZSyntax && *p == '~') || (!fZSyntax && *p == '^')) { u = (*pAction) (CCLNOT, 0, '\0', '\0'); p += RECharLen (p); } else u = (*pAction) (CCLBEG, 0, '\0', '\0'); while (RECharType (p) != SR_CCLEND) { if (*p == '\\') p++; if (*p == '\0') { DEBOUT (("REParseClass expecting more, ERROR\n")); return NULL; } c = *p++; if (*p == '-') { p++; if (*p == '\\') p++; if (*p == '\0') { DEBOUT (("REParseClass expecting more, ERROR\n")); return NULL; } (*pAction) (RANGE, u, c, *p); p++; } else (*pAction) (RANGE, u, c, c); } return p + RECharLen (p); } /* REParseAny - parse a match-any-character expression * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseAny ( PACT pAction, char *p ) { DEBOUT (("REParseAny (%04x, %s)\n", pAction, p)); (*pAction) (ANY, 0, '\0', '\0'); return p + RECharLen (p); } /* REParseBOL - parse a beginning-of-line match * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseBOL ( PACT pAction, char *p ) { DEBOUT (("REParseBOL (%04x, %s)\n", pAction, p)); (*pAction) (BOL, 0, '\0', '\0'); return p + RECharLen (p); } /* REParsePrev - parse a previous-match item * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParsePrev ( PACT pAction, char *p ) { unsigned int i = *(p + 1) - '0'; DEBOUT (("REParsePrev (%04x, %s)\n", pAction, p)); if (i < 1 || i > cArg) { DEBOUT (("REParsePrev invalid previous number, ERROR\n")); return NULL; } (*pAction) (PREV, i, '\0', '\0'); return p + RECharLen (p); } /* REParseEOL - parse an end-of-line match * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseEOL ( PACT pAction, char *p ) { DEBOUT (("REParseEOL (%04x, %s)\n", pAction, p)); (*pAction) (EOL, 0, '\0', '\0'); return p + RECharLen (p); } /* REParseAlt - parse a series of alternatives * * pAction Action to apply before and after each alternative * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseAlt ( PACT pAction, register char *p ) { UINT_PTR u = 0; DEBOUT (("REParseAlt (%04x, %s)\n", pAction, p)); while (RECharType (p) != SR_RIGHTOR) { p += RECharLen (p); u = (*pAction) (LEFTOR, u, '\0', '\0'); if ((p = REParseRE (pAction, p, EndAltRE)) == NULL) return NULL; u = (*pAction) (ORSIGN, u, '\0', '\0'); } (*pAction) (RIGHTOR, u, '\0', '\0'); return p + RECharLen (p); } /* REParseNot - parse a guard-against match * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseNot ( PACT pAction, register char *p ) { UINT_PTR u; DEBOUT (("REParseNot (%04x, %s)\n", pAction, p)); p += RECharLen (p); if (*p == '\0') { DEBOUT (("REParseNot expecting more, ERROR\n")); return NULL; } u = (*pAction) (NOTSIGN, 0, '\0', '\0'); p = REParseSE (pAction, p); (*pAction) (NOTSIGN1, u, '\0', '\0'); return p; } /* REParseAbbrev - parse and expand an abbreviation * * Note that since the abbreviations are in Z syntax, we must change syntax * temporarily to Z. We are careful to do this so that we do not mess up * advancign the pointers. * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseAbbrev ( PACT pAction, register char *p ) { int i; flagType fZSTmp; DEBOUT (("REParseAbbrev (%04x, %s)\n", pAction, p)); p += RECharLen (p); fZSTmp = fZSyntax; fZSyntax = TRUE; if (p[-1] == '\0') { DEBOUT (("REParseAbbrev expecting abbrev char, ERROR\n")); fZSyntax = fZSTmp; return NULL; } for (i = 0; pAbbrev[i]; i++) if (p[-1] == *pAbbrev[i]) if (REParseSE (pAction, pAbbrev[i] + 1) == NULL) { fZSyntax = fZSTmp; return NULL; } else { fZSyntax = fZSTmp; return p; } DEBOUT (("REParseAbbrev found invalid abbrev char %s, ERROR\n", p - 1)); fZSyntax = fZSTmp; return NULL; } /* REParseChar - parse a single character match * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseChar ( PACT pAction, register char *p ) { DEBOUT (("REParseChar (%04x, %s)\n", pAction, p)); if (*p == '\\') p++; if (*p == '\0') { DEBOUT (("REParseChar expected more, ERROR\n")); return NULL; } (*pAction) (LETTER, 0, *p, '\0'); return p+1; } /* REParseClosure - parse a minimal match closure. The match occurs by * matching none, then one, ... * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseClosure ( PACT pAction, register char *p ) { UINT_PTR u; DEBOUT (("REParseaClosure (%04x, %s)\n", pAction, p)); u = (*pAction) (SMSTAR, 0, '\0', '\0'); if ((p = REParseSE (pAction, p)) == NULL) return NULL; (*pAction) (SMSTAR1, u, '\0', '\0'); return p + REClosureLen (p); } /* REParseGreedy - parse a maximal-match closure. The match occurs by * matching the maximal number and then backing off as failures occur. * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParseGreedy ( PACT pAction, register char *p ) { UINT_PTR u; DEBOUT (("REParseGreedy (%04x, %s)\n", pAction, p)); u = (*pAction) (STAR, 0, '\0', '\0'); if ((p = REParseSE (pAction, p)) == NULL) return NULL; (*pAction) (STAR1, u, '\0', '\0'); return p + REClosureLen (p); } /* REParsePower - parse a power-closure. This is merely the simple pattern * repeated the number of times specified by the exponent. * * pAction Action to apply * p character pointer to spot where parsing occurs * * Returns pointer past parsed text if successful * NULL otherwise (syntax error) */ char * REParsePower ( PACT pAction, char *p ) { register char *p1; int exp; DEBOUT (("REParsePower (%04x, %s)\n", pAction, p)); /* We have .se. POWER something. Skip over the .se. and POWER * to make sure that what follows is a valid number */ p1 = REParseSE (NullAction, p); if (p1 == NULL) /* Parse of .se. failed */ return NULL; /* skip POWER */ p1 += REClosureLen (p1); if (*p1 == '\0') { DEBOUT (("REParsePower expecting more, ERROR\n")); return NULL; } /* try to parse off number */ if (sscanf (p1, "%d", &exp) != 1) { DEBOUT (("REParsePower expecting number, ERROR\n")); return NULL; } p1 = strbskip (p1, digits); /* iterate the pattern the exponent number of times */ while (exp--) if (REParseSE (pAction, p) == NULL) return NULL; return p1; } /* NullAction - a do-nothing action. Used for stubbing out the action * during a parse. */ UINT_PTR NullAction( unsigned int type, UINT_PTR u, unsigned char x, unsigned char y ) { type; u; x; y; return 0; } /* REClosureChar - return the character that corresponds to the next * closure to be parsed. We call REParseSE with a null action to merely * advance the character pointer to point just beyond the current simple * regular expression. * * p character pointer to spot where parsing occurs * * Returns closure character if appropriate * CCH_NONE if no closure character found. */ char REClosureChar ( char *p ) { p = REParseSE (NullAction, p); if (p == NULL) return CCH_ERROR; if (fZSyntax) /* Zibo syntax */ switch (*p) { case '^': return CCH_POWER; case '+': return CCH_SMPLUS; case '#': return CCH_PLUS; case '*': return CCH_SMCLOSURE; case '@': return CCH_CLOSURE; default: return CCH_NONE; } else /* Crappy UNIX syntax */ switch (*p) { case '+': return CCH_PLUS; case '*': return CCH_CLOSURE; default: return CCH_NONE; } } /* RECompile - compile a pattern into the machine. Return a * pointer to the match machine. * * p character pointer to pattern being compiled * * Returns: pointer to the machine if compilation was successful * NULL if syntax error or not enough memory for malloc */ struct patType * RECompile( char *p, flagType fCase, flagType fZS ) { fZSyntax = fZS; REEstimate (p); DEBOUT (("Length is %04x\n", RESize)); if (RESize == -1) return NULL; if ((REPat = (struct patType *) (*tools_alloc) (RESize)) == NULL) return NULL; memset ((char far *) REPat, -1, RESize); memset ((char far *) REPat->pArgBeg, 0, sizeof (REPat->pArgBeg)); memset ((char far *) REPat->pArgEnd, 0, sizeof (REPat->pArgEnd)); REip = REPat->code; REArg = 1; REPat->fCase = fCase; REPat->fUnix = (flagType) !fZS; cArg = 0; CompileAction (PROLOG, 0, '\0', '\0'); if (REParseRE (CompileAction, p, NULL) == NULL) return NULL; CompileAction (EPILOG, 0, '\0', '\0'); #if DEBUG REDump (REPat); #endif return REPat; } /* Escaped - translate an escaped character ala UNIX C conventions. * * \t => tab \e => ESC char \h => backspace \g => bell * \n => lf \r => cr \\ => \ * * c character to be translated * * Returns: character as per above */ char Escaped( char c ) { switch (c) { case 't': return '\t'; case 'e': return 0x1B; case 'h': return 0x08; case 'g': return 0x07; case 'n': return '\n'; case 'r': return '\r'; case '\\': return '\\'; default: return c; } } /* REGetArg - copy argument string out from match. * * pat matched pattern * i index of argument to fetch, 0 is entire pattern * p destination of argument * * Returns: TRUE if successful, FALSE if i is out of range. */ flagType REGetArg ( struct patType *pat, int i, char *p ) { int l = 0; if (i > MAXPATARG) return FALSE; else if (pat->pArgBeg[i] != (char *)-1) memmove ((char far *)p, (char far *)pat->pArgBeg[i], l = RELength (pat, i)); p[l] = '\0'; return TRUE; } /* RETranslate - translate a pattern string and match structure into an * output string. During pattern search-and-replace, RETranslate is used * to generate an output string based on an input match pattern and a template * that directs the output. * * The input match is any patType returned from RECompile that has been passed * to fREMatch and that causes fREMatch to return TRUE. The template string * is any set of ascii chars. The $ character leads in arguments: * * $$ is replaced with $ * $0 is replaced with the entire match string * $1-$9 is replaced with the corresponding tagged (by {}) item from * the match. * * An alternative method is to specify the argument as: * * $([w,]a) where a is the argument number (0-9) and w is an optional field * width that will be used in a printf %ws format. * * buf pattern matched * src template for the match * dst destination of the translation * * Returns: TRUE if translation was successful, FALSE otherwise */ flagType RETranslate ( struct patType *buf, register char *src, register char *dst ) { int i, w; char *work; char chArg = (char) (buf->fUnix ? '\\' : '$'); work = (*tools_alloc) (MAXLINELEN); if (work == NULL) return FALSE; *dst = '\0'; while (*src != '\0') { /* Process tagged substitutions first */ if (*src == chArg && (isdigit (src[1]) || src[1] == '(')) { /* presume 0-width field */ w = 0; /* skip $ and char */ src += 2; /* if we saw $n */ if (isdigit (src[-1])) i = src[-1] - '0'; /* else we saw $( */ else { /* get tagged expr number */ i = atoi (src); /* skip over number */ if (*src == '-') src++; src = strbskip (src, digits); /* was there a comma? */ if (*src == ',') { /* We saw field width, parse off expr number */ w = i; i = atoi (++src); src = strbskip (src, digits); } /* We MUST end with a close paren */ if (*src++ != ')') { free (work); return FALSE; } } /* w is field width * i is selected argument */ if (!REGetArg (buf, i, work)) { free (work); return FALSE; } sprintf (dst, "%*s", w, work); dst += strlen (dst); } else /* process escaped characters */ if (*src == '\\') { src++; if (!*src) { free (work); return FALSE; } *dst++ = Escaped (*src++); } else /* chArg quotes itself */ if (*src == chArg && src[1] == chArg) { *dst++ = chArg; src += 2; } else *dst++ = *src++; } *dst = '\0'; free (work); return TRUE; } /* RETranslateLength - given a matched pattern and a replacement string * return the length of the final replacement * * The inputs have the same syntax/semantics as in RETranslate. * * buf pattern matched * src template for the match * * Returns: number of bytes in total replacement, -1 if error */ int RETranslateLength ( struct patType *buf, register char *src ) { int i, w; int length = 0; char chArg = (char) (buf->fUnix ? '\\' : '$'); while (*src != '\0') { /* Process tagged substitutions first */ if (*src == chArg && (isdigit (src[1]) || src[1] == '(')) { w = 0; src += 2; if (isdigit (src[-1])) i = src[-1] - '0'; else { i = atoi (src); if (*src == '-') src++; src = strbskip (src, digits); if (*src == ',') { w = i; i = atoi (++src); src = strbskip (src, digits); } if (*src++ != ')') return -1; } /* w is field width * i is selected argument */ i = RELength (buf, i); length += max (i, abs(w)); } else /* process escaped characters */ if (*src == '\\') { src += 2; length++; } else /* chArg quotes itself */ if (*src == chArg && src[1] == chArg) { src += 2; length++; } else { length++; src++; } } return length; } /* RELength - return length of argument in match. * * pat matched pattern * i index of argument to examine, 0 is entire pattern * * Returns: length of ith argument, -1 if i is out-of-range. */ int RELength ( struct patType *pat, int i ) { if (i > MAXPATARG) return -1; else if (pat->pArgBeg[i] == (char *)-1) return 0; else return (int)(pat->pArgEnd[i] - pat->pArgBeg[i]); } /* REStart - return pointer to beginning of match. * * ppat matched pattern * * Returns: character pointer to beginning of match */ char * REStart ( struct patType *pat ) { return pat->pArgBeg[0] == (char *)-1 ? NULL : pat->pArgBeg[0]; }