tclRegexp.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:30k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclRegexp.c --
  3.  *
  4.  * This file contains the public interfaces to the Tcl regular
  5.  * expression mechanism.
  6.  *
  7.  * Copyright (c) 1998 by Sun Microsystems, Inc.
  8.  * Copyright (c) 1998-1999 by Scriptics Corporation.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * RCS: @(#) $Id: tclRegexp.c,v 1.14.4.2 2006/04/07 01:14:28 hobbs Exp $
  14.  */
  15. #include "tclInt.h"
  16. #include "tclPort.h"
  17. #include "tclRegexp.h"
  18. /*
  19.  *----------------------------------------------------------------------
  20.  * The routines in this file use Henry Spencer's regular expression
  21.  * package contained in the following additional source files:
  22.  *
  23.  * regc_color.c regc_cvec.c regc_lex.c
  24.  * regc_nfa.c regcomp.c regcustom.h
  25.  * rege_dfa.c regerror.c regerrs.h
  26.  * regex.h regexec.c regfree.c
  27.  * regfronts.c regguts.h
  28.  *
  29.  * Copyright (c) 1998 Henry Spencer.  All rights reserved.
  30.  * 
  31.  * Development of this software was funded, in part, by Cray Research Inc.,
  32.  * UUNET Communications Services Inc., Sun Microsystems Inc., and Scriptics
  33.  * Corporation, none of whom are responsible for the results.  The author
  34.  * thanks all of them. 
  35.  * 
  36.  * Redistribution and use in source and binary forms -- with or without
  37.  * modification -- are permitted for any purpose, provided that
  38.  * redistributions in source form retain this entire copyright notice and
  39.  * indicate the origin and nature of any modifications.
  40.  * 
  41.  * I'd appreciate being given credit for this package in the documentation
  42.  * of software which uses it, but that is not a requirement.
  43.  * 
  44.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
  45.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  46.  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
  47.  * HENRY SPENCER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  48.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  49.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  50.  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  51.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  52.  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  53.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  54.  *
  55.  * *** NOTE: this code has been altered slightly for use in Tcl: ***
  56.  * *** 1. Names have been changed, e.g. from re_comp to  ***
  57.  * ***    TclRegComp, to avoid clashes with other   ***
  58.  * ***    regexp implementations used by applications.   ***
  59.  */
  60. /*
  61.  * Thread local storage used to maintain a per-thread cache of compiled
  62.  * regular expressions.
  63.  */
  64. #define NUM_REGEXPS 30
  65. typedef struct ThreadSpecificData {
  66.     int initialized; /* Set to 1 when the module is initialized. */
  67.     char *patterns[NUM_REGEXPS];/* Strings corresponding to compiled
  68.  * regular expression patterns.  NULL
  69.  * means that this slot isn't used.
  70.  * Malloc-ed. */
  71.     int patLengths[NUM_REGEXPS];/* Number of non-null characters in
  72.  * corresponding entry in patterns.
  73.  * -1 means entry isn't used. */
  74.     struct TclRegexp *regexps[NUM_REGEXPS];
  75. /* Compiled forms of above strings.  Also
  76.  * malloc-ed, or NULL if not in use yet. */
  77. } ThreadSpecificData;
  78. static Tcl_ThreadDataKey dataKey;
  79. /*
  80.  * Declarations for functions used only in this file.
  81.  */
  82. static TclRegexp * CompileRegexp _ANSI_ARGS_((Tcl_Interp *interp,
  83.     CONST char *pattern, int length, int flags));
  84. static void DupRegexpInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
  85.     Tcl_Obj *copyPtr));
  86. static void FinalizeRegexp _ANSI_ARGS_((ClientData clientData));
  87. static void FreeRegexp _ANSI_ARGS_((TclRegexp *regexpPtr));
  88. static void FreeRegexpInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr));
  89. static int RegExpExecUniChar _ANSI_ARGS_((Tcl_Interp *interp,
  90.     Tcl_RegExp re, CONST Tcl_UniChar *uniString,
  91.     int numChars, int nmatches, int flags));
  92. static int SetRegexpFromAny _ANSI_ARGS_((Tcl_Interp *interp,
  93.     Tcl_Obj *objPtr));
  94. /*
  95.  * The regular expression Tcl object type.  This serves as a cache
  96.  * of the compiled form of the regular expression.
  97.  */
  98. static Tcl_ObjType tclRegexpType = {
  99.     "regexp", /* name */
  100.     FreeRegexpInternalRep, /* freeIntRepProc */
  101.     DupRegexpInternalRep, /* dupIntRepProc */
  102.     NULL, /* updateStringProc */
  103.     SetRegexpFromAny /* setFromAnyProc */
  104. };
  105. /*
  106.  *----------------------------------------------------------------------
  107.  *
  108.  * Tcl_RegExpCompile --
  109.  *
  110.  * Compile a regular expression into a form suitable for fast
  111.  * matching.  This procedure is DEPRECATED in favor of the
  112.  * object version of the command.
  113.  *
  114.  * Results:
  115.  * The return value is a pointer to the compiled form of string,
  116.  * suitable for passing to Tcl_RegExpExec.  This compiled form
  117.  * is only valid up until the next call to this procedure, so
  118.  * don't keep these around for a long time!  If an error occurred
  119.  * while compiling the pattern, then NULL is returned and an error
  120.  * message is left in the interp's result.
  121.  *
  122.  * Side effects:
  123.  * Updates the cache of compiled regexps.
  124.  *
  125.  *----------------------------------------------------------------------
  126.  */
  127. Tcl_RegExp
  128. Tcl_RegExpCompile(interp, string)
  129.     Tcl_Interp *interp; /* For use in error reporting and
  130.  * to access the interp regexp cache. */
  131.     CONST char *string; /* String for which to produce
  132.  * compiled regular expression. */
  133. {
  134.     return (Tcl_RegExp) CompileRegexp(interp, string, (int) strlen(string),
  135.     REG_ADVANCED);
  136. }
  137. /*
  138.  *----------------------------------------------------------------------
  139.  *
  140.  * Tcl_RegExpExec --
  141.  *
  142.  * Execute the regular expression matcher using a compiled form
  143.  * of a regular expression and save information about any match
  144.  * that is found.
  145.  *
  146.  * Results:
  147.  * If an error occurs during the matching operation then -1
  148.  * is returned and the interp's result contains an error message.
  149.  * Otherwise the return value is 1 if a matching range is
  150.  * found and 0 if there is no matching range.
  151.  *
  152.  * Side effects:
  153.  * None.
  154.  *
  155.  *----------------------------------------------------------------------
  156.  */
  157. int
  158. Tcl_RegExpExec(interp, re, string, start)
  159.     Tcl_Interp *interp; /* Interpreter to use for error reporting. */
  160.     Tcl_RegExp re; /* Compiled regular expression;  must have
  161.  * been returned by previous call to
  162.  * Tcl_GetRegExpFromObj. */
  163.     CONST char *string; /* String against which to match re. */
  164.     CONST char *start; /* If string is part of a larger string,
  165.  * this identifies beginning of larger
  166.  * string, so that "^" won't match. */
  167. {
  168.     int flags, result, numChars;
  169.     TclRegexp *regexp = (TclRegexp *)re;
  170.     Tcl_DString ds;
  171.     CONST Tcl_UniChar *ustr;
  172.     /*
  173.      * If the starting point is offset from the beginning of the buffer,
  174.      * then we need to tell the regexp engine not to match "^".
  175.      */
  176.     if (string > start) {
  177. flags = REG_NOTBOL;
  178.     } else {
  179. flags = 0;
  180.     }
  181.     /*
  182.      * Remember the string for use by Tcl_RegExpRange().
  183.      */
  184.     regexp->string = string;
  185.     regexp->objPtr = NULL;
  186.     /*
  187.      * Convert the string to Unicode and perform the match.
  188.      */
  189.     Tcl_DStringInit(&ds);
  190.     ustr = Tcl_UtfToUniCharDString(string, -1, &ds);
  191.     numChars = Tcl_DStringLength(&ds) / sizeof(Tcl_UniChar);
  192.     result = RegExpExecUniChar(interp, re, ustr, numChars,
  193.     -1 /* nmatches */, flags);
  194.     Tcl_DStringFree(&ds);
  195.     return result;
  196. }
  197. /*
  198.  *---------------------------------------------------------------------------
  199.  *
  200.  * Tcl_RegExpRange --
  201.  *
  202.  * Returns pointers describing the range of a regular expression match,
  203.  * or one of the subranges within the match.
  204.  *
  205.  * Results:
  206.  * The variables at *startPtr and *endPtr are modified to hold the
  207.  * addresses of the endpoints of the range given by index.  If the
  208.  * specified range doesn't exist then NULLs are returned.
  209.  *
  210.  * Side effects:
  211.  * None.
  212.  *
  213.  *---------------------------------------------------------------------------
  214.  */
  215. void
  216. Tcl_RegExpRange(re, index, startPtr, endPtr)
  217.     Tcl_RegExp re; /* Compiled regular expression that has
  218.  * been passed to Tcl_RegExpExec. */
  219.     int index; /* 0 means give the range of the entire
  220.  * match, > 0 means give the range of
  221.  * a matching subrange. */
  222.     CONST char **startPtr; /* Store address of first character in
  223.  * (sub-) range here. */
  224.     CONST char **endPtr; /* Store address of character just after last
  225.  * in (sub-) range here. */
  226. {
  227.     TclRegexp *regexpPtr = (TclRegexp *) re;
  228.     CONST char *string;
  229.     if ((size_t) index > regexpPtr->re.re_nsub) {
  230. *startPtr = *endPtr = NULL;
  231.     } else if (regexpPtr->matches[index].rm_so < 0) {
  232. *startPtr = *endPtr = NULL;
  233.     } else {
  234. if (regexpPtr->objPtr) {
  235.     string = Tcl_GetString(regexpPtr->objPtr);
  236. } else {
  237.     string = regexpPtr->string;
  238. }
  239. *startPtr = Tcl_UtfAtIndex(string, regexpPtr->matches[index].rm_so);
  240. *endPtr = Tcl_UtfAtIndex(string, regexpPtr->matches[index].rm_eo);
  241.     }
  242. }
  243. /*
  244.  *---------------------------------------------------------------------------
  245.  *
  246.  * RegExpExecUniChar --
  247.  *
  248.  * Execute the regular expression matcher using a compiled form of a
  249.  * regular expression and save information about any match that is
  250.  * found.
  251.  *
  252.  * Results:
  253.  * If an error occurs during the matching operation then -1 is
  254.  * returned and an error message is left in interp's result.
  255.  * Otherwise the return value is 1 if a matching range was found or
  256.  * 0 if there was no matching range.
  257.  *
  258.  * Side effects:
  259.  * None.
  260.  *
  261.  *----------------------------------------------------------------------
  262.  */
  263. static int
  264. RegExpExecUniChar(interp, re, wString, numChars, nmatches, flags)
  265.     Tcl_Interp *interp; /* Interpreter to use for error reporting. */
  266.     Tcl_RegExp re; /* Compiled regular expression; returned by
  267.  * a previous call to Tcl_GetRegExpFromObj */
  268.     CONST Tcl_UniChar *wString; /* String against which to match re. */
  269.     int numChars; /* Length of Tcl_UniChar string (must
  270.  * be >= 0). */
  271.     int nmatches; /* How many subexpression matches (counting
  272.  * the whole match as subexpression 0) are
  273.  * of interest.  -1 means "don't know". */
  274.     int flags; /* Regular expression flags. */
  275. {
  276.     int status;
  277.     TclRegexp *regexpPtr = (TclRegexp *) re;
  278.     size_t last = regexpPtr->re.re_nsub + 1;
  279.     size_t nm = last;
  280.     if (nmatches >= 0 && (size_t) nmatches < nm) {
  281. nm = (size_t) nmatches;
  282.     }
  283.     status = TclReExec(&regexpPtr->re, wString, (size_t) numChars,
  284.     &regexpPtr->details, nm, regexpPtr->matches, flags);
  285.     /*
  286.      * Check for errors.
  287.      */
  288.     if (status != REG_OKAY) {
  289. if (status == REG_NOMATCH) {
  290.     return 0;
  291. }
  292. if (interp != NULL) {
  293.     TclRegError(interp, "error while matching regular expression: ",
  294.     status);
  295. }
  296. return -1;
  297.     }
  298.     return 1;
  299. }
  300. /*
  301.  *---------------------------------------------------------------------------
  302.  *
  303.  * TclRegExpRangeUniChar --
  304.  *
  305.  * Returns pointers describing the range of a regular expression match,
  306.  * or one of the subranges within the match, or the hypothetical range
  307.  * represented by the rm_extend field of the rm_detail_t.
  308.  *
  309.  * Results:
  310.  * The variables at *startPtr and *endPtr are modified to hold the
  311.  * offsets of the endpoints of the range given by index.  If the
  312.  * specified range doesn't exist then -1s are supplied.
  313.  *
  314.  * Side effects:
  315.  * None.
  316.  *
  317.  *---------------------------------------------------------------------------
  318.  */
  319. void
  320. TclRegExpRangeUniChar(re, index, startPtr, endPtr)
  321.     Tcl_RegExp re; /* Compiled regular expression that has
  322.  * been passed to Tcl_RegExpExec. */
  323.     int index; /* 0 means give the range of the entire
  324.  * match, > 0 means give the range of
  325.  * a matching subrange, -1 means the
  326.  * range of the rm_extend field. */
  327.     int *startPtr; /* Store address of first character in
  328.  * (sub-) range here. */
  329.     int *endPtr; /* Store address of character just after last
  330.  * in (sub-) range here. */
  331. {
  332.     TclRegexp *regexpPtr = (TclRegexp *) re;
  333.     if ((regexpPtr->flags&REG_EXPECT) && index == -1) {
  334. *startPtr = regexpPtr->details.rm_extend.rm_so;
  335. *endPtr = regexpPtr->details.rm_extend.rm_eo;
  336.     } else if ((size_t) index > regexpPtr->re.re_nsub) {
  337. *startPtr = -1;
  338. *endPtr = -1;
  339.     } else {
  340. *startPtr = regexpPtr->matches[index].rm_so;
  341. *endPtr = regexpPtr->matches[index].rm_eo;
  342.     }
  343. }
  344. /*
  345.  *----------------------------------------------------------------------
  346.  *
  347.  * Tcl_RegExpMatch --
  348.  *
  349.  * See if a string matches a regular expression.
  350.  *
  351.  * Results:
  352.  * If an error occurs during the matching operation then -1
  353.  * is returned and the interp's result contains an error message.
  354.  * Otherwise the return value is 1 if "string" matches "pattern"
  355.  * and 0 otherwise.
  356.  *
  357.  * Side effects:
  358.  * None.
  359.  *
  360.  *----------------------------------------------------------------------
  361.  */
  362. int
  363. Tcl_RegExpMatch(interp, string, pattern)
  364.     Tcl_Interp *interp; /* Used for error reporting. May be NULL. */
  365.     CONST char *string; /* String. */
  366.     CONST char *pattern; /* Regular expression to match against
  367.  * string. */
  368. {
  369.     Tcl_RegExp re;
  370.     re = Tcl_RegExpCompile(interp, pattern);
  371.     if (re == NULL) {
  372. return -1;
  373.     }
  374.     return Tcl_RegExpExec(interp, re, string, string);
  375. }
  376. /*
  377.  *----------------------------------------------------------------------
  378.  *
  379.  * Tcl_RegExpExecObj --
  380.  *
  381.  * Execute a precompiled regexp against the given object.
  382.  *
  383.  * Results:
  384.  * If an error occurs during the matching operation then -1
  385.  * is returned and the interp's result contains an error message.
  386.  * Otherwise the return value is 1 if "string" matches "pattern"
  387.  * and 0 otherwise.
  388.  *
  389.  * Side effects:
  390.  * Converts the object to a Unicode object.
  391.  *
  392.  *----------------------------------------------------------------------
  393.  */
  394. int
  395. Tcl_RegExpExecObj(interp, re, objPtr, offset, nmatches, flags)
  396.     Tcl_Interp *interp; /* Interpreter to use for error reporting. */
  397.     Tcl_RegExp re; /* Compiled regular expression;  must have
  398.  * been returned by previous call to
  399.  * Tcl_GetRegExpFromObj. */
  400.     Tcl_Obj *objPtr; /* String against which to match re. */
  401.     int offset; /* Character index that marks where matching
  402.  * should begin. */
  403.     int nmatches; /* How many subexpression matches (counting
  404.  * the whole match as subexpression 0) are
  405.  * of interest.  -1 means all of them. */
  406.     int flags; /* Regular expression execution flags. */
  407. {
  408.     TclRegexp *regexpPtr = (TclRegexp *) re;
  409.     Tcl_UniChar *udata;
  410.     int length;
  411.     /*
  412.      * Save the target object so we can extract strings from it later.
  413.      */
  414.     regexpPtr->string = NULL;
  415.     regexpPtr->objPtr = objPtr;
  416.     udata = Tcl_GetUnicodeFromObj(objPtr, &length);
  417.     if (offset > length) {
  418. offset = length;
  419.     }
  420.     udata += offset;
  421.     length -= offset;
  422.     
  423.     return RegExpExecUniChar(interp, re, udata, length, nmatches, flags);
  424. }
  425. /*
  426.  *----------------------------------------------------------------------
  427.  *
  428.  * Tcl_RegExpMatchObj --
  429.  *
  430.  * See if an object matches a regular expression.
  431.  *
  432.  * Results:
  433.  * If an error occurs during the matching operation then -1
  434.  * is returned and the interp's result contains an error message.
  435.  * Otherwise the return value is 1 if "string" matches "pattern"
  436.  * and 0 otherwise.
  437.  *
  438.  * Side effects:
  439.  * Changes the internal rep of the pattern and string objects.
  440.  *
  441.  *----------------------------------------------------------------------
  442.  */
  443. int
  444. Tcl_RegExpMatchObj(interp, stringObj, patternObj)
  445.     Tcl_Interp *interp; /* Used for error reporting. May be NULL. */
  446.     Tcl_Obj *stringObj; /* Object containing the String to search. */
  447.     Tcl_Obj *patternObj; /* Regular expression to match against
  448.  * string. */
  449. {
  450.     Tcl_RegExp re;
  451.     re = Tcl_GetRegExpFromObj(interp, patternObj,
  452.     TCL_REG_ADVANCED | TCL_REG_NOSUB);
  453.     if (re == NULL) {
  454. return -1;
  455.     }
  456.     return Tcl_RegExpExecObj(interp, re, stringObj, 0 /* offset */,
  457.     0 /* nmatches */, 0 /* flags */);
  458. }
  459. /*
  460.  *----------------------------------------------------------------------
  461.  *
  462.  * Tcl_RegExpGetInfo --
  463.  *
  464.  * Retrieve information about the current match.
  465.  *
  466.  * Results:
  467.  * None.
  468.  *
  469.  * Side effects:
  470.  * None.
  471.  *
  472.  *----------------------------------------------------------------------
  473.  */
  474. void
  475. Tcl_RegExpGetInfo(regexp, infoPtr)
  476.     Tcl_RegExp regexp; /* Pattern from which to get subexpressions. */
  477.     Tcl_RegExpInfo *infoPtr; /* Match information is stored here.  */
  478. {
  479.     TclRegexp *regexpPtr = (TclRegexp *) regexp;
  480.     infoPtr->nsubs = regexpPtr->re.re_nsub;
  481.     infoPtr->matches = (Tcl_RegExpIndices *) regexpPtr->matches;
  482.     infoPtr->extendStart = regexpPtr->details.rm_extend.rm_so;
  483. }
  484. /*
  485.  *----------------------------------------------------------------------
  486.  *
  487.  * Tcl_GetRegExpFromObj --
  488.  *
  489.  * Compile a regular expression into a form suitable for fast
  490.  * matching.  This procedure caches the result in a Tcl_Obj.
  491.  *
  492.  * Results:
  493.  * The return value is a pointer to the compiled form of string,
  494.  * suitable for passing to Tcl_RegExpExec.  If an error occurred
  495.  * while compiling the pattern, then NULL is returned and an error
  496.  * message is left in the interp's result.
  497.  *
  498.  * Side effects:
  499.  * Updates the native rep of the Tcl_Obj.
  500.  *
  501.  *----------------------------------------------------------------------
  502.  */
  503. Tcl_RegExp
  504. Tcl_GetRegExpFromObj(interp, objPtr, flags)
  505.     Tcl_Interp *interp; /* For use in error reporting, and to access
  506.  * the interp regexp cache. */
  507.     Tcl_Obj *objPtr; /* Object whose string rep contains regular
  508.  * expression pattern.  Internal rep will be
  509.  * changed to compiled form of this regular
  510.  * expression. */
  511.     int flags; /* Regular expression compilation flags. */
  512. {
  513.     int length;
  514.     Tcl_ObjType *typePtr;
  515.     TclRegexp *regexpPtr;
  516.     char *pattern;
  517.     typePtr = objPtr->typePtr;
  518.     regexpPtr = (TclRegexp *) objPtr->internalRep.otherValuePtr;
  519.     if ((typePtr != &tclRegexpType) || (regexpPtr->flags != flags)) {
  520. pattern = Tcl_GetStringFromObj(objPtr, &length);
  521. regexpPtr = CompileRegexp(interp, pattern, length, flags);
  522. if (regexpPtr == NULL) {
  523.     return NULL;
  524. }
  525. /*
  526.  * Add a reference to the regexp so it will persist even if it is
  527.  * pushed out of the current thread's regexp cache.  This reference
  528.  * will be removed when the object's internal rep is freed.
  529.  */
  530. regexpPtr->refCount++;
  531. /*
  532.  * Free the old representation and set our type.
  533.  */
  534. if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) {
  535.     (*typePtr->freeIntRepProc)(objPtr);
  536. }
  537. objPtr->internalRep.otherValuePtr = (VOID *) regexpPtr;
  538. objPtr->typePtr = &tclRegexpType;
  539.     }
  540.     return (Tcl_RegExp) regexpPtr;
  541. }
  542. /*
  543.  *----------------------------------------------------------------------
  544.  *
  545.  * TclRegAbout --
  546.  *
  547.  * Return information about a compiled regular expression.
  548.  *
  549.  * Results:
  550.  * The return value is -1 for failure, 0 for success, although at
  551.  * the moment there's nothing that could fail.  On success, a list
  552.  * is left in the interp's result:  first element is the subexpression
  553.  * count, second is a list of re_info bit names.
  554.  *
  555.  * Side effects:
  556.  * None.
  557.  *
  558.  *----------------------------------------------------------------------
  559.  */
  560. int
  561. TclRegAbout(interp, re)
  562.     Tcl_Interp *interp; /* For use in variable assignment. */
  563.     Tcl_RegExp re; /* The compiled regular expression. */
  564. {
  565.     TclRegexp *regexpPtr = (TclRegexp *)re;
  566.     char buf[TCL_INTEGER_SPACE];
  567.     static struct infoname {
  568. int bit;
  569. char *text;
  570.     } infonames[] = {
  571. {REG_UBACKREF, "REG_UBACKREF"},
  572. {REG_ULOOKAHEAD, "REG_ULOOKAHEAD"},
  573. {REG_UBOUNDS, "REG_UBOUNDS"},
  574. {REG_UBRACES, "REG_UBRACES"},
  575. {REG_UBSALNUM, "REG_UBSALNUM"},
  576. {REG_UPBOTCH, "REG_UPBOTCH"},
  577. {REG_UBBS, "REG_UBBS"},
  578. {REG_UNONPOSIX, "REG_UNONPOSIX"},
  579. {REG_UUNSPEC, "REG_UUNSPEC"},
  580. {REG_UUNPORT, "REG_UUNPORT"},
  581. {REG_ULOCALE, "REG_ULOCALE"},
  582. {REG_UEMPTYMATCH, "REG_UEMPTYMATCH"},
  583. {REG_UIMPOSSIBLE, "REG_UIMPOSSIBLE"},
  584. {REG_USHORTEST, "REG_USHORTEST"},
  585. {0, ""}
  586.     };
  587.     struct infoname *inf;
  588.     int n;
  589.     Tcl_ResetResult(interp);
  590.     sprintf(buf, "%u", (unsigned)(regexpPtr->re.re_nsub));
  591.     Tcl_AppendElement(interp, buf);
  592.     /*
  593.      * Must count bits before generating list, because we must know
  594.      * whether {} are needed before we start appending names.
  595.      */
  596.     n = 0;
  597.     for (inf = infonames; inf->bit != 0; inf++) {
  598. if (regexpPtr->re.re_info&inf->bit) {
  599.     n++;
  600. }
  601.     }
  602.     if (n != 1) {
  603. Tcl_AppendResult(interp, " {", NULL);
  604.     }
  605.     for (inf = infonames; inf->bit != 0; inf++) {
  606. if (regexpPtr->re.re_info&inf->bit) {
  607.     Tcl_AppendElement(interp, inf->text);
  608. }
  609.     }
  610.     if (n != 1) {
  611. Tcl_AppendResult(interp, "}", NULL);
  612.     }
  613.     return 0;
  614. }
  615. /*
  616.  *----------------------------------------------------------------------
  617.  *
  618.  * TclRegError --
  619.  *
  620.  * Generate an error message based on the regexp status code.
  621.  *
  622.  * Results:
  623.  * Places an error in the interpreter.
  624.  *
  625.  * Side effects:
  626.  * Sets errorCode as well.
  627.  *
  628.  *----------------------------------------------------------------------
  629.  */
  630. void
  631. TclRegError(interp, msg, status)
  632.     Tcl_Interp *interp; /* Interpreter for error reporting. */
  633.     CONST char *msg; /* Message to prepend to error. */
  634.     int status; /* Status code to report. */
  635. {
  636.     char buf[100]; /* ample in practice */
  637.     char cbuf[100]; /* lots in practice */
  638.     size_t n;
  639.     char *p;
  640.     Tcl_ResetResult(interp);
  641.     n = TclReError(status, (regex_t *)NULL, buf, sizeof(buf));
  642.     p = (n > sizeof(buf)) ? "..." : "";
  643.     Tcl_AppendResult(interp, msg, buf, p, NULL);
  644.     sprintf(cbuf, "%d", status);
  645.     (VOID) TclReError(REG_ITOA, (regex_t *)NULL, cbuf, sizeof(cbuf));
  646.     Tcl_SetErrorCode(interp, "REGEXP", cbuf, buf, NULL);
  647. }
  648. /*
  649.  *----------------------------------------------------------------------
  650.  *
  651.  * FreeRegexpInternalRep --
  652.  *
  653.  * Deallocate the storage associated with a regexp object's internal
  654.  * representation.
  655.  *
  656.  * Results:
  657.  * None.
  658.  *
  659.  * Side effects:
  660.  * Frees the compiled regular expression.
  661.  *
  662.  *----------------------------------------------------------------------
  663.  */
  664. static void
  665. FreeRegexpInternalRep(objPtr)
  666.     Tcl_Obj *objPtr; /* Regexp object with internal rep to free. */
  667. {
  668.     TclRegexp *regexpRepPtr = (TclRegexp *) objPtr->internalRep.otherValuePtr;
  669.     /*
  670.      * If this is the last reference to the regexp, free it.
  671.      */
  672.     if (--(regexpRepPtr->refCount) <= 0) {
  673. FreeRegexp(regexpRepPtr);
  674.     }
  675. }
  676. /*
  677.  *----------------------------------------------------------------------
  678.  *
  679.  * DupRegexpInternalRep --
  680.  *
  681.  * We copy the reference to the compiled regexp and bump its
  682.  * reference count.
  683.  *
  684.  * Results:
  685.  * None.
  686.  *
  687.  * Side effects:
  688.  * Increments the reference count of the regexp.
  689.  *
  690.  *----------------------------------------------------------------------
  691.  */
  692. static void
  693. DupRegexpInternalRep(srcPtr, copyPtr)
  694.     Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
  695.     Tcl_Obj *copyPtr; /* Object with internal rep to set. */
  696. {
  697.     TclRegexp *regexpPtr = (TclRegexp *) srcPtr->internalRep.otherValuePtr;
  698.     regexpPtr->refCount++;
  699.     copyPtr->internalRep.otherValuePtr = srcPtr->internalRep.otherValuePtr;
  700.     copyPtr->typePtr = &tclRegexpType;
  701. }
  702. /*
  703.  *----------------------------------------------------------------------
  704.  *
  705.  * SetRegexpFromAny --
  706.  *
  707.  * Attempt to generate a compiled regular expression for the Tcl object
  708.  * "objPtr".
  709.  *
  710.  * Results:
  711.  * The return value is TCL_OK or TCL_ERROR. If an error occurs during
  712.  * conversion, an error message is left in the interpreter's result
  713.  * unless "interp" is NULL.
  714.  *
  715.  * Side effects:
  716.  * If no error occurs, a regular expression is stored as "objPtr"s
  717.  * internal representation.
  718.  *
  719.  *----------------------------------------------------------------------
  720.  */
  721. static int
  722. SetRegexpFromAny(interp, objPtr)
  723.     Tcl_Interp *interp; /* Used for error reporting if not NULL. */
  724.     Tcl_Obj *objPtr; /* The object to convert. */
  725. {
  726.     if (Tcl_GetRegExpFromObj(interp, objPtr, REG_ADVANCED) == NULL) {
  727. return TCL_ERROR;
  728.     }
  729.     return TCL_OK;
  730. }
  731. /*
  732.  *---------------------------------------------------------------------------
  733.  *
  734.  * CompileRegexp --
  735.  *
  736.  * Attempt to compile the given regexp pattern.  If the compiled
  737.  * regular expression can be found in the per-thread cache, it
  738.  * will be used instead of compiling a new copy.
  739.  *
  740.  * Results:
  741.  * The return value is a pointer to a newly allocated TclRegexp
  742.  * that represents the compiled pattern, or NULL if the pattern
  743.  * could not be compiled.  If NULL is returned, an error message is
  744.  * left in the interp's result.
  745.  *
  746.  * Side effects:
  747.  * The thread-local regexp cache is updated and a new TclRegexp may
  748.  * be allocated.
  749.  *
  750.  *----------------------------------------------------------------------
  751.  */
  752. static TclRegexp *
  753. CompileRegexp(interp, string, length, flags)
  754.     Tcl_Interp *interp; /* Used for error reporting if not NULL. */
  755.     CONST char *string; /* The regexp to compile (UTF-8). */
  756.     int length; /* The length of the string in bytes. */
  757.     int flags; /* Compilation flags. */
  758. {
  759.     TclRegexp *regexpPtr;
  760.     CONST Tcl_UniChar *uniString;
  761.     int numChars;
  762.     Tcl_DString stringBuf;
  763.     int status, i;
  764.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  765.  
  766.     if (!tsdPtr->initialized) {
  767. tsdPtr->initialized = 1;
  768. Tcl_CreateThreadExitHandler(FinalizeRegexp, NULL);
  769.     }
  770.     /*
  771.      * This routine maintains a second-level regular expression cache in
  772.      * addition to the per-object regexp cache.  The per-thread cache is needed
  773.      * to handle the case where for various reasons the object is lost between
  774.      * invocations of the regexp command, but the literal pattern is the same.
  775.      */
  776.     /*
  777.      * Check the per-thread compiled regexp cache.  We can only reuse
  778.      * a regexp if it has the same pattern and the same flags.
  779.      */
  780.     for (i = 0; (i < NUM_REGEXPS) && (tsdPtr->patterns[i] != NULL); i++) {
  781. if ((length == tsdPtr->patLengths[i])
  782. && (tsdPtr->regexps[i]->flags == flags)
  783. && (strcmp(string, tsdPtr->patterns[i]) == 0)) {
  784.     /*
  785.      * Move the matched pattern to the first slot in the
  786.      * cache and shift the other patterns down one position.
  787.      */
  788.     if (i != 0) {
  789. int j;
  790. char *cachedString;
  791. cachedString = tsdPtr->patterns[i];
  792. regexpPtr = tsdPtr->regexps[i];
  793. for (j = i-1; j >= 0; j--) {
  794.     tsdPtr->patterns[j+1] = tsdPtr->patterns[j];
  795.     tsdPtr->patLengths[j+1] = tsdPtr->patLengths[j];
  796.     tsdPtr->regexps[j+1] = tsdPtr->regexps[j];
  797. }
  798. tsdPtr->patterns[0] = cachedString;
  799. tsdPtr->patLengths[0] = length;
  800. tsdPtr->regexps[0] = regexpPtr;
  801.     }
  802.     return tsdPtr->regexps[0];
  803. }
  804.     }
  805.     /*
  806.      * This is a new expression, so compile it and add it to the cache.
  807.      */
  808.     
  809.     regexpPtr = (TclRegexp *) ckalloc(sizeof(TclRegexp));
  810.     regexpPtr->objPtr = NULL;
  811.     regexpPtr->string = NULL;
  812.     regexpPtr->details.rm_extend.rm_so = -1;
  813.     regexpPtr->details.rm_extend.rm_eo = -1;
  814.     /*
  815.      * Get the up-to-date string representation and map to unicode.
  816.      */
  817.     Tcl_DStringInit(&stringBuf);
  818.     uniString = Tcl_UtfToUniCharDString(string, length, &stringBuf);
  819.     numChars = Tcl_DStringLength(&stringBuf) / sizeof(Tcl_UniChar);
  820.     /*
  821.      * Compile the string and check for errors.
  822.      */
  823.     regexpPtr->flags = flags;
  824.     status = TclReComp(&regexpPtr->re, uniString, (size_t) numChars, flags);
  825.     Tcl_DStringFree(&stringBuf);
  826.     if (status != REG_OKAY) {
  827. /*
  828.  * Clean up and report errors in the interpreter, if possible.
  829.  */
  830. ckfree((char *)regexpPtr);
  831. if (interp) {
  832.     TclRegError(interp,
  833.     "couldn't compile regular expression pattern: ",
  834.     status);
  835. }
  836. return NULL;
  837.     }
  838.     /*
  839.      * Allocate enough space for all of the subexpressions, plus one
  840.      * extra for the entire pattern.
  841.      */
  842.     regexpPtr->matches = (regmatch_t *) ckalloc(
  843.     sizeof(regmatch_t) * (regexpPtr->re.re_nsub + 1));
  844.     /*
  845.      * Initialize the refcount to one initially, since it is in the cache.
  846.      */
  847.     regexpPtr->refCount = 1;
  848.     /*
  849.      * Free the last regexp, if necessary, and make room at the head of the
  850.      * list for the new regexp.
  851.      */
  852.     if (tsdPtr->patterns[NUM_REGEXPS-1] != NULL) {
  853. TclRegexp *oldRegexpPtr = tsdPtr->regexps[NUM_REGEXPS-1];
  854. if (--(oldRegexpPtr->refCount) <= 0) {
  855.     FreeRegexp(oldRegexpPtr);
  856. }
  857. ckfree(tsdPtr->patterns[NUM_REGEXPS-1]);
  858.     }
  859.     for (i = NUM_REGEXPS - 2; i >= 0; i--) {
  860. tsdPtr->patterns[i+1] = tsdPtr->patterns[i];
  861. tsdPtr->patLengths[i+1] = tsdPtr->patLengths[i];
  862. tsdPtr->regexps[i+1] = tsdPtr->regexps[i];
  863.     }
  864.     tsdPtr->patterns[0] = (char *) ckalloc((unsigned) (length+1));
  865.     strcpy(tsdPtr->patterns[0], string);
  866.     tsdPtr->patLengths[0] = length;
  867.     tsdPtr->regexps[0] = regexpPtr;
  868.     return regexpPtr;
  869. }
  870. /*
  871.  *----------------------------------------------------------------------
  872.  *
  873.  * FreeRegexp --
  874.  *
  875.  * Release the storage associated with a TclRegexp.
  876.  *
  877.  * Results:
  878.  * None.
  879.  *
  880.  * Side effects:
  881.  * None.
  882.  *
  883.  *----------------------------------------------------------------------
  884.  */
  885. static void
  886. FreeRegexp(regexpPtr)
  887.     TclRegexp *regexpPtr; /* Compiled regular expression to free. */
  888. {
  889.     TclReFree(&regexpPtr->re);
  890.     if (regexpPtr->matches) {
  891. ckfree((char *) regexpPtr->matches);
  892.     }
  893.     ckfree((char *) regexpPtr);
  894. }
  895. /*
  896.  *----------------------------------------------------------------------
  897.  *
  898.  * FinalizeRegexp --
  899.  *
  900.  * Release the storage associated with the per-thread regexp
  901.  * cache.
  902.  *
  903.  * Results:
  904.  * None.
  905.  *
  906.  * Side effects:
  907.  * None.
  908.  *
  909.  *----------------------------------------------------------------------
  910.  */
  911. static void
  912. FinalizeRegexp(clientData)
  913.     ClientData clientData; /* Not used. */
  914. {
  915.     int i;
  916.     TclRegexp *regexpPtr;
  917.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  918.     for (i = 0; (i < NUM_REGEXPS) && (tsdPtr->patterns[i] != NULL); i++) {
  919. regexpPtr = tsdPtr->regexps[i];
  920. if (--(regexpPtr->refCount) <= 0) {
  921.     FreeRegexp(regexpPtr);
  922. }
  923. ckfree(tsdPtr->patterns[i]);
  924. tsdPtr->patterns[i] = NULL;
  925.     }
  926.     /*
  927.      * We may find ourselves reinitialized if another finalization routine
  928.      * invokes regexps.
  929.      */
  930.     tsdPtr->initialized = 0;
  931. }