home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Controls / Visual Basic Controls.iso / vbcontrol / sregexpf / clsregex.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-05-29  |  44.6 KB  |  1,777 lines

  1. // Win32 porting notes.
  2.  
  3. #if defined( _MBCS )
  4. #pragma message( __FILEINFO__ "This code is broken under _MBCS, " \
  5.                  "see the comments at the top of this file." )
  6. #endif //_MBCS
  7.  
  8. //
  9. //
  10. // In case this isn't obvious from the later comments this is an ALTERED
  11. // version of the software. If you like my changes then cool, but nearly
  12. // all of the functionality here is derived from Henry Spencer's original
  13. // work.
  14. //
  15. // This code should work correctly under both _SBCS and _UNICODE, I did
  16. // start working on making it work with _MBCS but gave up after a while
  17. // since I don't need this particular port and it's not going to be as
  18. // straight forward as the other two.
  19. //
  20. // The problem stems from the compiled program being stored as TCHARS,
  21. // the individual items need to be wide enough to hold whatever character
  22. // is thrown at them, but currently they are accessed as an array of
  23. // whatever size integral type is appropriate.  _MBCS would cause this
  24. // to be char, but at times it would need to be larger.  This would
  25. // require making the program be an array of short with the appropriate
  26. // conversions used everywhere.  Certainly it's doable, but it's a pain.
  27. // What's worse is that the current code will compile and run under _MBCS,
  28. // only breaking when it gets wide characters thrown against it.
  29. //
  30. // I've marked at least one bit of code with #pragma messages, I may not
  31. // get all of them, but they should be a start
  32. //
  33. // Guy Gascoigne - Piggford (ggp@bigfoot.com) Friday, February 27, 1998
  34.  
  35.  
  36. // regcomp and regexec -- regsub and regerror are elsewhere
  37. // @(#)regexp.c    1.3 of 18 April 87
  38. //
  39. //    Copyright (c) 1986 by University of Toronto.
  40. //    Written by Henry Spencer.  Not derived from licensed software.
  41. //
  42. //    Permission is granted to anyone to use this software for any
  43. //    purpose on any computer system, and to redistribute it freely,
  44. //    subject to the following restrictions:
  45. //
  46. //    1. The author is not responsible for the consequences of use of
  47. //        this software, no matter how awful, even if they arise
  48. //        from defects in it.
  49. //
  50. //    2. The origin of this software must not be misrepresented, either
  51. //        by explicit claim or by omission.
  52. //
  53. //    3. Altered versions must be plainly marked as such, and must not
  54. //        be misrepresented as being the original software.
  55. // *** THIS IS AN ALTERED VERSION.  It was altered by John Gilmore,
  56. // *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching
  57. // *** as in BSD grep and ex.
  58. // *** THIS IS AN ALTERED VERSION.  It was altered by John Gilmore,
  59. // *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \.
  60. // *** THIS IS AN ALTERED VERSION.  It was altered by James A. Woods,
  61. // *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy.
  62. // *** THIS IS AN ALTERED VERSION.  It was altered by Geoffrey Noer,
  63. // *** THIS IS AN ALTERED VERSION.  It was altered by Guy Gascoigne - Piggford
  64. // *** guy@wyrdrune.com, on 15 March 1998, porting it to C++ and converting
  65. // *** it to be the engine for the Regexp class
  66. //
  67. // Beware that some of this code is subtly aware of the way operator
  68. // precedence is structured in regular expressions.  Serious changes in
  69. // regular-expression syntax might require a total rethink.
  70.  
  71. #include "stdafx.h"
  72. #include "clsRegExp.h"
  73.  
  74. // The first byte of the regexp internal "program" is actually this magic
  75. // number; the start node begins in the second byte.
  76.  
  77. const TCHAR    MAGIC = ((TCHAR)'\234');
  78.  
  79. //#define new DEBUG_NEW
  80.  
  81. #pragma warning( disable : 4711 )    // automatic inline selected
  82.  
  83. // The "internal use only" fields in regexp.h are present to pass info from
  84. // compile to execute that permits the execute phase to run lots faster on
  85. // simple cases.  They are:
  86. //
  87. // regstart    char that must begin a match; '\0' if none obvious
  88. // reganch    is the match anchored (at beginning-of-line only)?
  89. // regmust    string (pointer into program) that match must include, or NULL
  90. // regmlen    length of regmust string
  91. //
  92. // Regstart and reganch permit very fast decisions on suitable starting
  93. // points for a match, cutting down the work a lot.  Regmust permits fast
  94. // rejection of lines that cannot possibly match.  The regmust tests are
  95. // costly enough that regcomp() supplies a regmust only if the
  96. // r.e. contains something potentially expensive (at present, the only
  97. // such thing detected is * or + at the start of the r.e., which can
  98. // involve a lot of backup).  Regmlen is supplied because the test in
  99. // regexec() needs it and regcomp() is computing it anyway.
  100.  
  101. // Structure for regexp "program".  This is essentially a linear encoding
  102. // of a nondeterministic finite-state machine (aka syntax charts or
  103. // "railroad normal form" in parsing technology).  Each node is an opcode
  104. // plus a "next" pointer, possibly plus an operand.  "Next" pointers of
  105. // all nodes except BRANCH implement concatenation; a "next" pointer with
  106. // a BRANCH on both ends of it is connecting two alternatives.  (Here we
  107. // have one of the subtle syntax dependencies: an individual BRANCH (as
  108. // opposed to a collection of them) is never concatenated with anything
  109. // because of operator precedence.)  The operand of some types of node is
  110. // a literal string; for others, it is a node leading into a sub-FSM.  In
  111. // particular, the operand of a BRANCH node is the first node of the
  112. // branch.  (NB this is *not* a tree structure: the tail of the branch
  113. // connects to the thing following the set of BRANCHes.)  The opcodes
  114. // are:
  115.  
  116. enum {
  117. //  definition    number        opnd?    meaning
  118.     END        =    0,        //    no        End of program.
  119.     BOL        =    1,        //    no        Match beginning of line.
  120.     EOL        =    2,        //    no        Match end of line.
  121.     ANY        =    3,        //    no        Match any character.
  122.     ANYOF    =    4,        //    str        Match any of these.
  123.     ANYBUT    =    5,        //    str        Match any but one of these.
  124.     BRANCH    =    6,        //    node    Match this, or the next..\&.
  125.     BACK    =    7,        //    no        "next" ptr points backward.
  126.     EXACTLY    =    8,        //    str        Match this string.
  127.     NOTHING    =    9,        //    no        Match empty string.
  128.     STAR    =    10,        //    node    Match this 0 or more times.
  129.     PLUS    =    11,        //    node    Match this 1 or more times.
  130.     WORDA    =    12,        //    no        Match "" at wordchar, where prev is nonword
  131.     WORDZ    =    13,        //    no        Match "" at nonwordchar, where prev is word
  132.     OPEN    =    20,        //    no        Sub-RE starts here.
  133.                         //            OPEN+1 is number 1, etc.
  134.     CLOSE    =    30        //    no        Analogous to OPEN.
  135. };
  136.  
  137. // Opcode notes:
  138. //
  139. // BRANCH    The set of branches constituting a single choice are hooked
  140. //        together with their "next" pointers, since precedence prevents
  141. //        anything being concatenated to any individual branch.  The
  142. //        "next" pointer of the last BRANCH in a choice points to the
  143. //        thing following the whole choice.  This is also where the
  144. //        final "next" pointer of each individual branch points; each
  145. //        branch starts with the operand node of a BRANCH node.
  146. //
  147. // BACK        Normal "next" pointers all implicitly point forward; BACK
  148. //        exists to make loop structures possible.
  149. //
  150. // STAR,PLUS    '?', and complex '*' and '+', are implemented as circular
  151. //        BRANCH structures using BACK.  Simple cases (one character
  152. //        per match) are implemented with STAR and PLUS for speed
  153. //        and to minimize recursive plunges.
  154. //
  155. // OPEN,CLOSE    ...are numbered at compile time.
  156.  
  157. // A node is one char of opcode followed by two chars of "next" pointer.
  158. // "Next" pointers are stored as two 8-bit pieces, high order first.  The
  159. // value is a positive offset from the opcode of the node containing it.
  160. // An operand, if any, simply follows the node.  (Note that much of the
  161. // code generation knows about this implicit relationship.)
  162. //
  163. // Using two bytes for the "next" pointer is vast overkill for most things,
  164. // but allows patterns to get big without disasters.
  165.  
  166.  
  167. enum
  168. {
  169.     REGERR_SENTINEL_VALUE = 0,
  170.     REGERR_NULLARG = 1, REGERR_CORRUPTED, REGERR_CORRUPTION, REGERR_CORRUPTED_POINTERS,
  171.     REGERR_BAD_REGREPEAT, REGERR_CORRUPTED_OPCODE, REGERR_NULL_TO_REGSUB,
  172.     REGERR_DAMAGED_REGEXP_REGSUB, REGERR_DAMAGED_MATCH_STRING, REGERR_NULL_TO_REGCOMP,
  173.     REGERR_TO_BIG, REGERR_TO_MANY_PAREN, REGERR_UNTERMINATED_PAREN, REGERR_UNMATCHED_PAREN,
  174.     REGERR_INTERNAL_ERROR_JUNK, REGERR_OP_COULD_BE_EMPTY, REGERR_NESTED_OP, REGERR_INVALID_RANGE,
  175.     REGERR_UNMATCHED_BRACE, REGERR_INTERNAL_UNEXPECTED_CHAR, REGERR_OP_FOLLOWS_NOTHING,
  176.     REGERR_TRAILING_ESC, REGERR_INTERNAL_STRSCSPN, REGERR_NO_REGEXP
  177. };
  178.  
  179. struct regErr
  180. {
  181.     int m_id;
  182.     LPCTSTR m_err;
  183. } errors[] = {
  184.     { REGERR_NULLARG,                    _T( "NULL argument to regexec" ) },
  185.     { REGERR_CORRUPTED,                    _T( "corrupted regexp" ) },
  186.     { REGERR_CORRUPTION,                _T( "regexp corruption" ) },
  187.     { REGERR_CORRUPTED_POINTERS,        _T( "corrupted pointers" ) },
  188.     { REGERR_BAD_REGREPEAT,                _T( "internal error: bad call of regrepeat" ) },
  189.     { REGERR_CORRUPTED_OPCODE,            _T( "corrupted opcode" ) },
  190.     { REGERR_NULL_TO_REGSUB,            _T( "NULL parm to regsub" ) },
  191.     { REGERR_DAMAGED_REGEXP_REGSUB,        _T( "damaged regexp fed to regsub" ) },
  192.     { REGERR_DAMAGED_MATCH_STRING,        _T( "damaged match string" ) },
  193.     { REGERR_NULL_TO_REGCOMP,            _T( "NULL argument to regcomp" ) },
  194.     { REGERR_TO_BIG,                    _T( "regexp too big" ) },
  195.     { REGERR_TO_MANY_PAREN,                _T( "too many ()" ) },
  196.     { REGERR_UNTERMINATED_PAREN,        _T( "unterminated ()" ) },
  197.     { REGERR_UNMATCHED_PAREN,            _T( "unmatched ()" ) },
  198.     { REGERR_INTERNAL_ERROR_JUNK,        _T( "internal error: junk on end" ) },
  199.     { REGERR_OP_COULD_BE_EMPTY,            _T( "*+ operand could be empty" ) },
  200.     { REGERR_NESTED_OP,                    _T( "nested *?+" ) },
  201.     { REGERR_INVALID_RANGE,                _T( "invalid [] range" ) },
  202.     { REGERR_UNMATCHED_BRACE,            _T( "unmatched []" ) },
  203.     { REGERR_INTERNAL_UNEXPECTED_CHAR,    _T( "internal error: \\0|) unexpected" ) },
  204.     { REGERR_OP_FOLLOWS_NOTHING,        _T( "?+* follows nothing" ) },
  205.     { REGERR_TRAILING_ESC,                _T( "trailing \\" ) },
  206.     { REGERR_INTERNAL_STRSCSPN,            _T( "internal error: strcspn 0" ) },
  207.     { REGERR_NO_REGEXP,                    _T( "Regular expression is not specified or invalid" ) },
  208.     { REGERR_SENTINEL_VALUE,            _T( "Unknown error") }                            // must be last value
  209. };
  210.  
  211. // Flags to be passed up and down.
  212.  
  213. enum {
  214.     HASWIDTH    =    01,    // Known never to match null string.
  215.     SIMPLE    =    02,    // Simple enough to be STAR/PLUS operand.
  216.     SPSTART    =    04,    // Starts with * or +.
  217.     WORST        =    0        // Worst case.
  218. };
  219.  
  220. ///////////////////////////////////////////////////////////////////////////////
  221.  
  222. class CRegErrorHandler
  223. {
  224.     friend Regexp;
  225.     mutable str_t m_sError;
  226.     static LPCTSTR FindErr( int id );
  227. protected:
  228.     void ClearErrorString() const;
  229.     void regerror( LPCTSTR s ) const;
  230.     void regerror( int id ) const;
  231. public:
  232.     static int m_errId;
  233.     CRegErrorHandler() { m_errId = 0; }
  234.     CRegErrorHandler( const CRegErrorHandler & reh ) : m_sError( reh.m_sError ) {}
  235.  
  236.     const str_t& GetErrorString() const;
  237.     short GetErrorNumber() const { return short(m_errId); }
  238.  
  239. };
  240.  
  241. int CRegErrorHandler::m_errId = 0;
  242.  
  243. void CRegErrorHandler::regerror( LPCTSTR s ) const
  244. {
  245.     m_sError = s;
  246. }
  247.  
  248. void CRegErrorHandler::regerror( int id ) const
  249. {
  250.     m_errId = id;
  251.     regerror( FindErr( id ) );
  252. }
  253.  
  254. const str_t& CRegErrorHandler::GetErrorString() const
  255. {
  256.     return m_sError;
  257. }
  258.  
  259. void CRegErrorHandler::ClearErrorString() const
  260. {
  261.     m_errId = 0;
  262.     m_sError.erase();
  263. }
  264.  
  265. LPCTSTR CRegErrorHandler::FindErr( int id )
  266. {
  267.     m_errId = id;
  268.     for ( struct regErr * perr = errors; perr->m_id != REGERR_SENTINEL_VALUE; perr++ )
  269.         if ( perr->m_id == id )
  270.             return perr->m_err;
  271.  
  272.     return perr->m_err;        // since we've fallen off the array, perr->m_id == 0
  273. }
  274.  
  275. ///////////////////////////////////////////////////////////////////////////////
  276.  
  277. // All of the functions required to directly access the 'program'
  278. class CRegProgramAccessor : public CRegErrorHandler
  279. {
  280. public:
  281.     static inline TCHAR OP( LPTSTR p )
  282.     {
  283.         return (*(p));
  284.     }
  285.     static inline LPTSTR OPERAND( LPTSTR p )
  286.     {
  287.         return p + 3;
  288.     }
  289.     static inline LPTSTR regnext( LPTSTR p )
  290.     {
  291.         const short &offset = *((short*)(p+1));
  292.  
  293.         if (offset == 0)
  294.             return(NULL);
  295.         
  296.         return((OP(p) == BACK) ? p-offset : p+offset);
  297.     }
  298. #ifdef _RE_DEBUG
  299.     LPTSTR CRegProgramAccessor::regprop( LPTSTR op );
  300. #endif
  301. };
  302.  
  303. ///////////////////////////////////////////////////////////////////////////////
  304.  
  305. // The internal interface to the regexp, wrapping the compilation as well as the
  306. // execution of the regexp (matching)
  307.  
  308. class regexp : public CRegProgramAccessor
  309. {
  310.     friend class CRegExecutor;
  311.     friend class Regexp;
  312.     
  313.     int m_programSize;
  314.     LPTSTR startp[Regexp::NSUBEXP];
  315.     LPTSTR endp[Regexp::NSUBEXP];
  316.     TCHAR regstart;        // Internal use only.
  317.     TCHAR reganch;        // Internal use only.
  318.     LPTSTR regmust;        // Internal use only.
  319.     int regmlen;        // Internal use only.
  320.     LPTSTR program;
  321.     
  322.     bool status;
  323.     int count;            // used by Regexp to manage the reference counting of regexps
  324.     int numSubs;
  325. public:
  326.     
  327.     regexp( LPCTSTR exp, BOOL iCase );
  328.     regexp( const regexp & r );
  329.     ~regexp();
  330.     
  331.     void ignoreCase( const TCHAR * in, TCHAR * out );
  332.     
  333.     bool regcomp( LPCTSTR exp );
  334.     bool regexec( LPCTSTR string );
  335.     bool Status() const { return status; }
  336.  
  337.     str_t GetReplaceString(LPCTSTR sReplaceExp) const;
  338.  
  339.     regexp * getCopy();
  340.  
  341. #ifdef _RE_DEBUG
  342.     void  regdump();
  343. #endif
  344.     
  345. #ifdef _DEBUG
  346.     str_t m_originalPattern;
  347.     str_t m_modifiedPattern;
  348. #endif
  349. };
  350.  
  351. ///////////////////////////////////////////////////////////////////////////////
  352. // Compile / Validate the regular expression - ADT
  353.  
  354. class CRegCompilerBase : public CRegProgramAccessor
  355. {
  356. public:
  357.     CRegCompilerBase( LPCTSTR parse );
  358.  
  359.     LPTSTR reg(int paren, int *flagp);
  360. protected:
  361.     LPTSTR regparse;    // Input-scan pointer. 
  362.     int regnpar;        // () count. 
  363.  
  364.     LPTSTR regbranch(int *flagp);
  365.     LPTSTR regpiece(int *flagp);
  366.     LPTSTR regatom(int *flagp);
  367.     inline bool ISREPN( TCHAR c )    { return ((c) == '*' || (c) == '+' || (c) == '?'); }
  368.  
  369.     virtual void regc(int c) = 0;
  370.     virtual LPTSTR regnode(int op) = 0;
  371.     virtual void reginsert(TCHAR op, LPTSTR opnd) = 0;
  372.     virtual void regtail(LPTSTR p, LPTSTR val) = 0;
  373.     virtual void regoptail(LPTSTR p, LPTSTR val) = 0;
  374. };
  375.  
  376. ///////////////////////////////////////////////////////////////////////////////
  377. // First pass over the expression, testing for validity and returning the 
  378. // program size
  379.  
  380. class CRegValidator : public CRegCompilerBase
  381. {
  382. public:
  383.     CRegValidator( LPCTSTR parse );
  384.  
  385.     inline long Size() const { return regsize; }
  386. private:
  387.     long regsize;        // Code size. 
  388.     TCHAR regdummy[3];    // NOTHING, 0 next ptr 
  389. protected:
  390.     LPTSTR regnode(int) { regsize += 3; return regdummy; }
  391.     void regc(int) { regsize++; }
  392.     void reginsert(TCHAR, LPTSTR) { regsize += 3; }
  393.     void regtail(LPTSTR, LPTSTR) { return; }
  394.     void regoptail(LPTSTR, LPTSTR) { return; }
  395. };
  396.  
  397. ///////////////////////////////////////////////////////////////////////////////
  398. // Second pass, actually generating the program
  399.  
  400. class CRegCompiler : public CRegCompilerBase
  401. {
  402. public:
  403.     CRegCompiler( LPCTSTR parse, LPTSTR prog );
  404. private:
  405.     LPTSTR regcode;
  406. protected:
  407.     // regc - emit (if appropriate) a byte of code
  408.     void regc(int b)
  409.     {
  410.         *regcode++ = (TCHAR)b;
  411.     }
  412.     LPTSTR regnode(int op);
  413.     void reginsert(TCHAR op, LPTSTR opnd);
  414.     void regtail(LPTSTR p, LPTSTR val);
  415.     void regoptail(LPTSTR p, LPTSTR val);
  416. };
  417.  
  418. // regnode - emit a node
  419. LPTSTR CRegCompiler::regnode(int op)
  420. {
  421.     LPTSTR const ret = regcode;
  422.  
  423.     LPTSTR ptr = ret;
  424.     *ptr++ = (TCHAR)op;
  425.     *ptr++ = '\0';        // Null next pointer. 
  426.     *ptr++ = '\0';
  427.     regcode = ptr;
  428.  
  429.     return(ret);
  430. }
  431.  
  432. // reginsert - insert an operator in front of already-emitted operand
  433. //
  434. // Means relocating the operand.
  435. void CRegCompiler::reginsert(TCHAR op, LPTSTR opnd)
  436. {
  437.     LPTSTR place;
  438.  
  439.     (void) memmove(opnd+3, opnd, (size_t)((regcode - opnd)*sizeof(TCHAR)));
  440.     regcode += 3;
  441.  
  442.     place = opnd;        // Op node, where operand used to be.
  443.     *place++ = op;
  444.     *place++ = '\0';
  445.     *place++ = '\0';
  446. }
  447.  
  448. // regtail - set the next-pointer at the end of a node chain
  449. void CRegCompiler::regtail(LPTSTR p, LPTSTR val)
  450. {
  451.     LPTSTR scan;
  452.     LPTSTR temp;
  453.  
  454.     // Find last node. 
  455.     for (scan = p; (temp = regnext(scan)) != NULL; scan = temp)
  456.         continue;
  457.  
  458.     *((short *)(scan+1)) = (short)((OP(scan) == BACK) ? scan - val : val - scan);
  459. }
  460.  
  461. // regoptail - regtail on operand of first argument; nop if operandless
  462. void CRegCompiler::regoptail(LPTSTR p, LPTSTR val)
  463. {
  464.     // "Operandless" and "op != BRANCH" are synonymous in practice. 
  465.     if (OP(p) == BRANCH)
  466.         regtail(OPERAND(p), val);
  467. }
  468.  
  469. ///////////////////////////////////////////////////////////////////////////////
  470.  
  471. CRegCompilerBase::CRegCompilerBase( LPCTSTR parse )
  472.     : regparse( (LPTSTR)parse ),
  473.     regnpar(1)
  474. {
  475. }
  476.  
  477. CRegValidator::CRegValidator( LPCTSTR parse )
  478.     : CRegCompilerBase( parse ),
  479.     regsize(0)
  480. {
  481.     regc(MAGIC);
  482.     regdummy[0] = NOTHING;
  483.     regdummy[1] = regdummy[2] = 0;
  484. }
  485.  
  486. CRegCompiler::CRegCompiler( LPCTSTR parse, LPTSTR prog )
  487.     : CRegCompilerBase( parse ),
  488.     regcode(prog)
  489. {
  490.     regc(MAGIC);
  491. }
  492.  
  493. ///////////////////////////////////////////////////////////////////////////////
  494.  
  495. regexp::regexp( LPCTSTR exp, BOOL iCase )
  496.     : regstart(0),
  497.     reganch(0),
  498.     regmust(0),
  499.     regmlen(0),
  500.     program(0),
  501.     m_programSize(0)
  502. {
  503. #if _DEBUG
  504.     m_originalPattern = exp;        // keep a version of the pattern for debugging
  505. #endif
  506.  
  507.     if ( iCase )
  508.     {
  509.         TCHAR * out = new TCHAR[(_tcslen( exp ) * 4) + 1];
  510.         ignoreCase( exp, out );
  511.  
  512. #if _DEBUG
  513.         m_modifiedPattern = out;    // and the modified version if there is one
  514. #endif
  515.         status = regcomp( out );
  516.         delete [] out;
  517.     }
  518.     else
  519.         status = regcomp( exp );
  520.  
  521.     count = numSubs = 0;
  522. }
  523.  
  524. regexp::regexp( const regexp & orig )
  525.     : regstart(orig.regstart),
  526.     reganch(orig.reganch),
  527.     regmlen(orig.regmlen),
  528.     m_programSize(orig.m_programSize),
  529.     numSubs(orig.numSubs),
  530.     regmust(0)
  531. {
  532. #if _DEBUG
  533.     m_originalPattern = orig.m_originalPattern;
  534.     m_modifiedPattern = orig.m_modifiedPattern;
  535. #endif
  536.     status = orig.status;
  537.     count = 0;
  538.     program = new TCHAR[m_programSize];
  539.     memcpy( program, orig.program, m_programSize * sizeof( TCHAR ) );
  540.     if ( orig.regmust )
  541.         regmust = program + ( orig.regmust - orig.program );
  542.  
  543.     for ( int i = Regexp::NSUBEXP; i > 0; i--)
  544.     {
  545.         startp[i] = orig.startp[i];
  546.         endp[i] = orig.endp[i];
  547.     }
  548. }
  549.  
  550. regexp::~regexp()
  551. {
  552.     delete [] program;
  553. }
  554.  
  555.  
  556. // regcomp - compile a regular expression into internal code
  557. //
  558. // We can't allocate space until we know how big the compiled form will
  559. // be, but we can't compile it (and thus know how big it is) until we've
  560. // got a place to put the code.  So we cheat: we compile it twice, once
  561. // with code generation turned off and size counting turned on, and once
  562. // "for real".  This also means that we don't allocate space until we are
  563. // sure that the thing really will compile successfully, and we never
  564. // have to move the code and thus invalidate pointers into it.  (Note
  565. // that it has to be in one piece because free() must be able to free it
  566. // all.)
  567. //
  568. // Beware that the optimization-preparation code in here knows about some
  569. // of the structure of the compiled regexp.
  570.  
  571. bool regexp::regcomp(LPCTSTR exp)
  572. {
  573.     LPTSTR scan;
  574.     int flags;
  575.  
  576.     if (exp == NULL)
  577.     {
  578.         regerror( REGERR_NULL_TO_REGCOMP );
  579.         return NULL;
  580.     }
  581.  
  582.     // First pass: determine size, legality.
  583.     CRegValidator tester( exp );
  584.  
  585.     if (tester.reg(0, &flags) == NULL)
  586.         return false;
  587.  
  588.     // Small enough for pointer-storage convention? 
  589.     if (tester.Size() >= 0x7fffL)    // Probably could be 0xffffL. 
  590.     {
  591.         regerror(REGERR_TO_BIG);
  592.         return NULL;
  593.     }
  594.  
  595.     m_programSize = tester.Size();
  596.     // Allocate space. 
  597.     program = new TCHAR[m_programSize];
  598.  
  599.     CRegCompiler comp( exp, program );
  600.     // Second pass: emit code. 
  601.     if (comp.reg(0, &flags) == NULL)
  602.         return false;
  603.  
  604.     scan = program + 1;            // First BRANCH. 
  605.     if (OP(regnext(scan)) == END)
  606.     {        // Only one top-level choice. 
  607.         scan = OPERAND(scan);
  608.  
  609.         // Starting-point info. 
  610.         if (OP(scan) == EXACTLY)
  611.             regstart = *OPERAND(scan);
  612.         else if (OP(scan) == BOL)
  613.             reganch = 1;
  614.  
  615.         // If there's something expensive in the r.e., find the
  616.         // longest literal string that must appear and make it the
  617.         // regmust.  Resolve ties in favor of later strings, since
  618.         // the regstart check works with the beginning of the r.e.
  619.         // and avoiding duplication strengthens checking.  Not a
  620.         // strong reason, but sufficient in the absence of others.
  621.          
  622.         if (flags&SPSTART) 
  623.         {
  624.             LPTSTR longest = NULL;
  625.             size_t len = 0;
  626.  
  627.             for (; scan != NULL; scan = regnext(scan))
  628.                 if (OP(scan) == EXACTLY && _tcslen(OPERAND(scan)) >= len)
  629.                 {
  630.                     longest = OPERAND(scan);
  631.                     len = _tcslen(OPERAND(scan));
  632.                 }
  633.             regmust = longest;
  634.             regmlen = (int)len;
  635.         }
  636.     }
  637.  
  638.     return true;
  639. }
  640.  
  641. regexp * regexp::getCopy()
  642. {
  643.     return new regexp( *this );
  644. }
  645.  
  646. // reg - regular expression, i.e. main body or parenthesized thing
  647. //
  648. // Caller must absorb opening parenthesis.
  649. //
  650. // Combining parenthesis handling with the base level of regular expression
  651. // is a trifle forced, but the need to tie the tails of the branches to what
  652. // follows makes it hard to avoid.
  653.  
  654. LPTSTR CRegCompilerBase::reg( int paren, int *flagp )
  655. {
  656.     LPTSTR ret;
  657.     LPTSTR br;
  658.     LPTSTR ender;
  659.     int parno = 0;
  660.     int flags;
  661.  
  662.     *flagp = HASWIDTH;    // Tentatively. 
  663.  
  664.     if (paren)
  665.     {
  666.         // Make an OPEN node. 
  667.         if (regnpar >= Regexp::NSUBEXP)
  668.         {
  669.             regerror(REGERR_TO_MANY_PAREN);
  670.             return NULL;
  671.         }
  672.         parno = regnpar;
  673.         regnpar++;
  674.         ret = regnode(OPEN+parno);
  675.     }
  676.  
  677.     // Pick up the branches, linking them together. 
  678.     br = regbranch(&flags);
  679.     if (br == NULL)
  680.         return(NULL);
  681.     if (paren)
  682.         regtail(ret, br);    // OPEN -> first. 
  683.     else
  684.         ret = br;
  685.     *flagp &= ~(~flags&HASWIDTH);    // Clear bit if bit 0. 
  686.     *flagp |= flags&SPSTART;
  687.     while (*regparse == '|')
  688.     {
  689.         regparse++;
  690.         br = regbranch(&flags);
  691.         if (br == NULL)
  692.             return(NULL);
  693.         regtail(ret, br);    // BRANCH -> BRANCH. 
  694.         *flagp &= ~(~flags&HASWIDTH);
  695.         *flagp |= flags&SPSTART;
  696.     }
  697.  
  698.     // Make a closing node, and hook it on the end. 
  699.     ender = regnode((paren) ? CLOSE+parno : END);
  700.     regtail(ret, ender);
  701.  
  702.     // Hook the tails of the branches to the closing node. 
  703.     for (br = ret; br != NULL; br = regnext(br))
  704.         regoptail(br, ender);
  705.  
  706.     // Check for proper termination. 
  707.     if (paren && *regparse++ != ')')
  708.     {
  709.         regerror( REGERR_UNTERMINATED_PAREN );
  710.         return NULL;
  711.     }
  712.     else if (!paren && *regparse != '\0')
  713.     {
  714.         if (*regparse == ')')
  715.         {
  716.             regerror( REGERR_UNMATCHED_PAREN );
  717.             return NULL;
  718.         }
  719.         else
  720.         {
  721.             regerror( REGERR_INTERNAL_ERROR_JUNK );
  722.             return NULL;
  723.         }
  724.         // NOTREACHED 
  725.     }
  726.  
  727.     return(ret);
  728. }
  729.  
  730. // regbranch - one alternative of an | operator
  731. //
  732. // Implements the concatenation operator.
  733.  
  734. LPTSTR CRegCompilerBase::regbranch(int *flagp)
  735. {
  736.     LPTSTR ret;
  737.     LPTSTR chain;
  738.     LPTSTR latest;
  739.     int flags;
  740.     int c;
  741.  
  742.     *flagp = WORST;                // Tentatively. 
  743.  
  744.     ret = regnode(BRANCH);
  745.     chain = NULL;
  746.     while ((c = *regparse) != '\0' && c != '|' && c != ')')
  747.     {
  748.         latest = regpiece(&flags);
  749.         if (latest == NULL)
  750.             return(NULL);
  751.         *flagp |= flags&HASWIDTH;
  752.         if (chain == NULL)        // First piece. 
  753.             *flagp |= flags&SPSTART;
  754.         else
  755.             regtail(chain, latest);
  756.         chain = latest;
  757.     }
  758.     if (chain == NULL)            // Loop ran zero times. 
  759.         (void) regnode(NOTHING);
  760.  
  761.     return(ret);
  762. }
  763.  
  764. // regpiece - something followed by possible [*+?]
  765. //
  766. // Note that the branching code sequences used for ? and the general cases
  767. // of * and + are somewhat optimized:  they use the same NOTHING node as
  768. // both the endmarker for their branch list and the body of the last branch.
  769. // It might seem that this node could be dispensed with entirely, but the
  770. // endmarker role is not redundant.
  771.  
  772. LPTSTR CRegCompilerBase::regpiece(int *flagp)
  773. {
  774.     LPTSTR ret;
  775.     TCHAR op;
  776.     LPTSTR next;
  777.     int flags;
  778.  
  779.     ret = regatom(&flags);
  780.     if (ret == NULL)
  781.         return(NULL);
  782.  
  783.     op = *regparse;
  784.     if (!ISREPN(op))
  785.     {
  786.         *flagp = flags;
  787.         return(ret);
  788.     }
  789.  
  790.     if (!(flags&HASWIDTH) && op != _T( '?' ))
  791.     {
  792.         regerror( REGERR_OP_COULD_BE_EMPTY );
  793.         return NULL;
  794.     }
  795.     switch (op)
  796.     {
  797.         case _T( '*' ):    *flagp = WORST|SPSTART;                break;
  798.         case _T( '+' ):    *flagp = WORST|SPSTART|HASWIDTH;    break;
  799.         case _T( '?' ):    *flagp = WORST;                        break;
  800.     }
  801.  
  802.     if (op == _T( '*' ) && (flags&SIMPLE))
  803.         reginsert(STAR, ret);
  804.     else if (op == _T( '*' ))
  805.     {
  806.         // Emit x* as (x&|), where & means "self". 
  807.         reginsert(BRANCH, ret);                // Either x 
  808.         regoptail(ret, regnode(BACK));        // and loop 
  809.         regoptail(ret, ret);                // back 
  810.         regtail(ret, regnode(BRANCH));        // or 
  811.         regtail(ret, regnode(NOTHING));        // null. 
  812.     }
  813.     else if (op == _T( '+' ) && (flags&SIMPLE))
  814.         reginsert(PLUS, ret);
  815.     else if (op == _T( '+' ))
  816.     {
  817.         // Emit x+ as x(&|), where & means "self". 
  818.         next = regnode(BRANCH);                // Either 
  819.         regtail(ret, next);
  820.         regtail(regnode(BACK), ret);        // loop back 
  821.         regtail(next, regnode(BRANCH));        // or 
  822.         regtail(ret, regnode(NOTHING));        // null. 
  823.     }
  824.     else if (op == _T( '?' ))
  825.     {
  826.         // Emit x? as (x|) 
  827.         reginsert(BRANCH, ret);                // Either x 
  828.         regtail(ret, regnode(BRANCH));        // or 
  829.         next = regnode(NOTHING);            // null. 
  830.         regtail(ret, next);
  831.         regoptail(ret, next);
  832.     }
  833.     regparse++;
  834.     if (ISREPN(*regparse))
  835.     {
  836.         regerror( REGERR_NESTED_OP );
  837.         return NULL;
  838.     }
  839.  
  840.     return(ret);
  841. }
  842.  
  843. // regatom - the lowest level
  844. //
  845. // Optimization:  gobbles an entire sequence of ordinary characters so that
  846. // it can turn them into a single node, which is smaller to store and
  847. // faster to run.  Backslashed characters are exceptions, each becoming a
  848. // separate node; the code is simpler that way and it's not worth fixing.
  849.  
  850. LPTSTR CRegCompilerBase::regatom(int * flagp)
  851. {
  852.     LPTSTR ret;
  853.     int flags;
  854.  
  855.     *flagp = WORST;        // Tentatively. 
  856.  
  857.     switch ( *regparse++ )
  858.     {
  859.         // FIXME: these chars only have meaning at beg/end of pat?
  860.         case '^':
  861.             ret = regnode(BOL);
  862.             break;
  863.         case '$':
  864.             ret = regnode(EOL);
  865.             break;
  866.         case '.':
  867.             ret = regnode(ANY);
  868.             *flagp |= HASWIDTH|SIMPLE;
  869.             break;
  870.         case '[':
  871.         {
  872.             int range;
  873.             int rangeend;
  874.             int c;
  875.  
  876.             if (*regparse == '^')
  877.             {    // Complement of range. 
  878.                 ret = regnode(ANYBUT);
  879.                 regparse++;
  880.             }
  881.             else
  882.                 ret = regnode(ANYOF);
  883.             if ((c = *regparse) == ']' || c == '-')
  884.             {
  885.                 regc(c);
  886.                 regparse++;
  887.             }
  888.             while ((c = *regparse++ ) != '\0' && c != ']')
  889.             {
  890.                 if (c != '-')
  891.                     regc(c);
  892.                 else if ((c = *regparse) == ']' || c == '\0')
  893.                     regc('-');
  894.                 else
  895.                 {
  896.                     range = (TCHAR)*(regparse-2);
  897.                     rangeend = (TCHAR)c;
  898.                     if (range > rangeend)
  899.                     {
  900.                         regerror( REGERR_INVALID_RANGE );
  901.                         return NULL;
  902.                     }
  903.                     for (range++; range <= rangeend; range++)
  904.                         regc(range);
  905.                     regparse++;
  906.                 }
  907.             }
  908.             regc('\0');
  909.             if (c != ']')
  910.             {
  911.                 regerror( REGERR_UNMATCHED_BRACE );
  912.                 return NULL;
  913.             }
  914.             *flagp |= HASWIDTH|SIMPLE;
  915.             break;
  916.         }
  917.         case '(':
  918.             ret = reg(1, &flags);
  919.             if (ret == NULL)
  920.                 return(NULL);
  921.             *flagp |= flags&(HASWIDTH|SPSTART);
  922.             break;
  923.         case '\0':
  924.         case '|':
  925.         case ')':
  926.             // supposed to be caught earlier 
  927.             regerror( REGERR_INTERNAL_UNEXPECTED_CHAR );
  928.             return NULL;
  929.         case '?':
  930.         case '+':
  931.         case '*':
  932.             {
  933.                 regerror( REGERR_OP_FOLLOWS_NOTHING );
  934.                 return NULL;
  935.             }
  936.         case '\\':
  937.             switch (*regparse++)
  938.             {
  939.                 case '\0':
  940.                 {
  941.                     regerror( REGERR_TRAILING_ESC );
  942.                     return NULL;
  943.                 }
  944.                 case '<':
  945.                     ret = regnode(WORDA);
  946.                     break;
  947.                 case '>':
  948.                     ret = regnode(WORDZ);
  949.                     break;
  950.                 /* FIXME: Someday handle \1, \2, ... */
  951.                 default:
  952.                     /* Handle general quoted chars in exact-match routine */
  953.                     goto de_fault;
  954.             }
  955.             break;
  956.     de_fault:
  957.     default:
  958.         // Encode a string of characters to be matched exactly.
  959.         //
  960.         // This is a bit tricky due to quoted chars and due to
  961.         // '*', '+', and '?' taking the SINGLE char previous
  962.         // as their operand.
  963.         //
  964.         // On entry, the char at regparse[-1] is going to go
  965.         // into the string, no matter what it is.  (It could be
  966.         // following a \ if we are entered from the '\' case.)
  967.         // 
  968.         // Basic idea is to pick up a good char in  ch  and
  969.         // examine the next char.  If it's *+? then we twiddle.
  970.         // If it's \ then we frozzle.  If it's other magic char
  971.         // we push  ch  and terminate the string.  If none of the
  972.         // above, we push  ch  on the string and go around again.
  973.         //
  974.         //  regprev  is used to remember where "the current char"
  975.         // starts in the string, if due to a *+? we need to back
  976.         // up and put the current char in a separate, 1-char, string.
  977.         // When  regprev  is NULL,  ch  is the only char in the
  978.         // string; this is used in *+? handling, and in setting
  979.         // flags |= SIMPLE at the end.
  980.         {
  981.             TCHAR *regprev;
  982.             register TCHAR ch;
  983.  
  984.             regparse--;            /* Look at cur char */
  985.             ret = regnode(EXACTLY);
  986.             for ( regprev = 0 ; ; ) {
  987.                 ch = *regparse++;    /* Get current char */
  988.                 switch (*regparse) {    /* look at next one */
  989.  
  990.                 default:
  991.                     regc(ch);    /* Add cur to string */
  992.                     break;
  993.  
  994.                 case '.': case '[': case '(':
  995.                 case ')': case '|': case '\n':
  996.                 case '$': case '^':
  997.                 case '\0':
  998.                 /* FIXME, $ and ^ should not always be magic */
  999.                 magic:
  1000.                     regc(ch);    /* dump cur char */
  1001.                     goto done;    /* and we are done */
  1002.  
  1003.                 case '?': case '+': case '*':
  1004.                     if (!regprev)     /* If just ch in str, */
  1005.                         goto magic;    /* use it */
  1006.                     /* End mult-char string one early */
  1007.                     regparse = regprev; /* Back up parse */
  1008.                     goto done;
  1009.  
  1010.                 case '\\':
  1011.                     regc(ch);    /* Cur char OK */
  1012.                     switch (regparse[1]){ /* Look after \ */
  1013.                     case '\0':
  1014.                     case '<':
  1015.                     case '>':
  1016.                     /* FIXME: Someday handle \1, \2, ... */
  1017.                         goto done; /* Not quoted */
  1018.                     default:
  1019.                         /* Backup point is \, scan                             * point is after it. */
  1020.                         regprev = regparse;
  1021.                         regparse++; 
  1022.                         continue;    /* NOT break; */
  1023.                     }
  1024.                 }
  1025.                 regprev = regparse;    /* Set backup point */
  1026.             }
  1027.         done:
  1028.             regc('\0');
  1029.             *flagp |= HASWIDTH;
  1030.             if (!regprev)        /* One char? */
  1031.                 *flagp |= SIMPLE;
  1032.         }
  1033.         break;
  1034.     }
  1035.  
  1036.     return(ret);
  1037. }
  1038.  
  1039. ////////////////////////////////////////////////////////////////////////////////
  1040. // regexec and friends
  1041.  
  1042. // Work-variable struct for regexec().
  1043.  
  1044. class CRegExecutor : public CRegProgramAccessor
  1045. {
  1046.     friend bool regexp::regexec( LPCTSTR str );
  1047.  
  1048.     LPTSTR reginput;        // String-input pointer. 
  1049.     LPTSTR regbol;            // Beginning of input, for ^ check. 
  1050.     LPTSTR * regstartp;        // Pointer to startp array. 
  1051.     LPTSTR * regendp;        // Ditto for endp.
  1052.  
  1053.     regexp * prog;
  1054. public:
  1055.     CRegExecutor( regexp * prog, LPTSTR string );
  1056. protected:
  1057.     bool regtry( LPTSTR string );
  1058.     bool regmatch( LPTSTR prog );
  1059.     size_t regrepeat( LPTSTR node );
  1060. };
  1061.  
  1062. CRegExecutor::CRegExecutor( regexp * p, LPTSTR string )
  1063.     : regbol( string ),
  1064.     regstartp( p->startp ),
  1065.     regendp( p->endp ),
  1066.     prog(p)
  1067. {
  1068. }
  1069.  
  1070. #ifdef _RE_DEBUG
  1071. int regnarrate = 0;
  1072. #endif
  1073.  
  1074. // regexec - match a regexp against a string
  1075.  
  1076. bool regexp::regexec( LPCTSTR str )
  1077. {
  1078.     LPTSTR string = (LPTSTR)str;    // avert const poisoning 
  1079.  
  1080.     // Be paranoid. 
  1081.     if ( string == NULL )
  1082.     {
  1083.         regerror( REGERR_NULLARG );
  1084.         return false;
  1085.     }
  1086.     if (program == NULL)
  1087.     {
  1088.         regerror( REGERR_NO_REGEXP );
  1089.         return false;
  1090.     }
  1091.  
  1092.     // Check validity of program. 
  1093.     if (*program != MAGIC)
  1094.     {
  1095.         regerror( REGERR_CORRUPTED );
  1096.         return false;
  1097.     }
  1098.  
  1099.     // If there is a "must appear" string, look for it. 
  1100.     if ( regmust != NULL && _tcsstr( string, regmust ) == NULL )
  1101.         return false;
  1102.  
  1103.     CRegExecutor executor( this, string );
  1104.  
  1105.     // Simplest case:  anchored match need be tried only once. 
  1106.     if ( reganch )
  1107.         return( executor.regtry( string ) );
  1108.  
  1109.     // Messy cases:  unanchored match. 
  1110.     if ( regstart != '\0' )
  1111.     {
  1112.         // We know what TCHAR it must start with. 
  1113.         for ( LPTSTR s = string; s != NULL; s = _tcschr( s+1 , regstart ) )
  1114.             if ( executor.regtry( s) )
  1115.                 return true;
  1116.         return false;
  1117.     }
  1118.     else
  1119.     {
  1120.         // We don't -- general case. 
  1121.         for ( LPTSTR s = string; ! executor.regtry( s ); s++ )
  1122.             if (*s == '\0')
  1123.                 return false;
  1124.     }
  1125.     return true;
  1126. }
  1127.  
  1128. // regtry - try match at specific point
  1129. bool CRegExecutor::regtry( LPTSTR string )
  1130. {
  1131.     int i;
  1132.     LPTSTR * stp;
  1133.     LPTSTR * enp;
  1134.  
  1135.     reginput = string;
  1136.  
  1137.     stp = prog->startp;
  1138.     enp = prog->endp;
  1139.     for (i = Regexp::NSUBEXP; i > 0; i--)
  1140.     {
  1141.         *stp++ = NULL;
  1142.         *enp++ = NULL;
  1143.     }
  1144.     if ( regmatch( prog->program + 1 ) )
  1145.     {
  1146.         prog->startp[0] = string;
  1147.         prog->endp[0] = reginput;
  1148.         return true;
  1149.     }
  1150.     else
  1151.         return false;
  1152. }
  1153.  
  1154. // regmatch - main matching routine
  1155. //
  1156. // Conceptually the strategy is simple:  check to see whether the current
  1157. // node matches, call self recursively to see whether the rest matches,
  1158. // and then act accordingly.  In practice we make some effort to avoid
  1159. // recursion, in particular by going through "ordinary" nodes (that don't
  1160. // need to know whether the rest of the match failed) by a loop instead of
  1161. // by recursion.
  1162.  
  1163. bool CRegExecutor::regmatch( LPTSTR prog )
  1164. {
  1165.     LPTSTR scan;    // Current node. 
  1166.     LPTSTR next;        // Next node. 
  1167.  
  1168. #ifdef _RE_DEBUG
  1169.     if (prog != NULL && regnarrate)
  1170.         _ftprintf(stderr, _T( "%s(\n" ), regprop(prog));
  1171. #endif
  1172.     for (scan = prog; scan != NULL; scan = next)
  1173.     {
  1174. #ifdef _RE_DEBUG
  1175.         if (regnarrate)
  1176.             _ftprintf(stderr, _T( "%s...\n" ), regprop(scan));
  1177. #endif
  1178.         next = regnext(scan);
  1179.  
  1180.         switch (OP(scan))
  1181.         {
  1182.             case BOL:
  1183.                 if (reginput != regbol)
  1184.                     return false;
  1185.                 break;
  1186.             case EOL:
  1187.                 if (*reginput != '\0')
  1188.                     return false;
  1189.                 break;
  1190.             case WORDA:
  1191.                 /* Must be looking at a letter, digit, or _ */
  1192.                 if ((!isalnum(*reginput)) && *reginput != '_')
  1193.                     return(0);
  1194.                 /* Prev must be BOL or nonword */
  1195.                 if (reginput > regbol &&
  1196.                     (isalnum(reginput[-1]) || reginput[-1] == '_'))
  1197.                     return(0);
  1198.                 break;
  1199.             case WORDZ:
  1200.                 /* Must be looking at non letter, digit, or _ */
  1201.                 if (isalnum(*reginput) || *reginput == '_')
  1202.                     return(0);
  1203.                 /* We don't care what the previous char was */
  1204.                 break;
  1205.             case ANY:
  1206.                 if (*reginput == '\0')
  1207.                     return false;
  1208.                 reginput++;
  1209.                 break;
  1210.             case EXACTLY:
  1211.             {
  1212.                 size_t len;
  1213.                 LPTSTR const opnd = OPERAND(scan);
  1214.  
  1215.                 // Inline the first character, for speed. 
  1216.                 if (*opnd != *reginput)
  1217.                     return false;
  1218.                 len = _tcslen(opnd);
  1219.                 if (len > 1 && _tcsncmp(opnd, reginput, len) != 0)
  1220.                     return false;
  1221.                 reginput += len;
  1222.  
  1223.                 break;
  1224.             }
  1225.             case ANYOF:
  1226.                 if (*reginput == '\0' ||
  1227.                         _tcschr(OPERAND(scan), *reginput) == NULL)
  1228.                     return false;
  1229.                 reginput++;
  1230.                 break;
  1231.             case ANYBUT:
  1232.                 if (*reginput == '\0' ||
  1233.                         _tcschr(OPERAND(scan), *reginput) != NULL)
  1234.                     return false;
  1235.                 reginput++;
  1236.                 break;
  1237.             case NOTHING:
  1238.                 break;
  1239.             case BACK:
  1240.                 break;
  1241.             case OPEN+1: case OPEN+2: case OPEN+3:
  1242.             case OPEN+4: case OPEN+5: case OPEN+6:
  1243.             case OPEN+7: case OPEN+8: case OPEN+9:
  1244.             {
  1245.                 const int no = OP(scan) - OPEN;
  1246.                 LPTSTR const input = reginput;
  1247.  
  1248.                 if (regmatch(next))
  1249.                 {
  1250.                     // Don't set startp if some later
  1251.                     // invocation of the same parentheses
  1252.                     // already has.
  1253.                      
  1254.                     if (regstartp[no] == NULL)
  1255.                         regstartp[no] = input;
  1256.                     return true;
  1257.                 }
  1258.                 else
  1259.                     return false;
  1260.                 break;
  1261.             }
  1262.             case CLOSE+1: case CLOSE+2: case CLOSE+3:
  1263.             case CLOSE+4: case CLOSE+5: case CLOSE+6:
  1264.             case CLOSE+7: case CLOSE+8: case CLOSE+9:
  1265.             {
  1266.                 const int no = OP(scan) - CLOSE;
  1267.                 LPTSTR const input = reginput;
  1268.  
  1269.                 if (regmatch(next))
  1270.                 {
  1271.                     // Don't set endp if some later
  1272.                     // invocation of the same parentheses
  1273.                     // already has.
  1274.                      
  1275.                     if (regendp[no] == NULL)
  1276.                         regendp[no] = input;
  1277.                     return true;
  1278.                 }
  1279.                 else
  1280.                     return false;
  1281.                 break;
  1282.             }
  1283.             case BRANCH:
  1284.             {
  1285.                 LPTSTR const save = reginput;
  1286.  
  1287.                 if (OP(next) != BRANCH)        // No choice. 
  1288.                     next = OPERAND(scan);    // Avoid recursion. 
  1289.                 else
  1290.                 {
  1291.                     while (OP(scan) == BRANCH)
  1292.                     {
  1293.                         if (regmatch(OPERAND(scan)))
  1294.                             return true;
  1295.                         reginput = save;
  1296.                         scan = regnext(scan);
  1297.                     }
  1298.                     return false;
  1299.                     // NOTREACHED 
  1300.                 }
  1301.                 break;
  1302.             }
  1303.             case STAR: case PLUS:
  1304.             {
  1305.                 const TCHAR nextch = (OP(next) == EXACTLY) ? *OPERAND(next) : '\0';
  1306.                 size_t no;
  1307.                 LPTSTR const save = reginput;
  1308.                 const size_t min = (OP(scan) == STAR) ? 0 : 1;
  1309.  
  1310.                 for (no = regrepeat(OPERAND(scan)) + 1; no > min; no--)
  1311.                 {
  1312.                     reginput = save + no - 1;
  1313.                     // If it could work, try it. 
  1314.                     if (nextch == '\0' || *reginput == nextch)
  1315.                         if (regmatch(next))
  1316.                             return true;
  1317.                 }
  1318.                 return false;
  1319.                 break;
  1320.             }
  1321.             case END:
  1322.                 return true;    // Success! 
  1323.                 break;
  1324.             default:
  1325.                 regerror( REGERR_CORRUPTION );
  1326.                 return false;
  1327.                 break;
  1328.         }
  1329.     }
  1330.  
  1331.     // We get here only if there's trouble -- normally "case END" is
  1332.     // the terminating point.
  1333.  
  1334.     regerror( REGERR_CORRUPTED_POINTERS );
  1335.     return false;
  1336. }
  1337.  
  1338. // regrepeat - report how many times something simple would match
  1339.  
  1340. size_t CRegExecutor::regrepeat( LPTSTR node )
  1341. {
  1342.     size_t count;
  1343.     LPTSTR scan;
  1344.     TCHAR ch;
  1345.  
  1346.     switch (OP(node))
  1347.     {
  1348.         case ANY:
  1349.             return(_tcslen(reginput));
  1350.             break;
  1351.         case EXACTLY:
  1352.             ch = *OPERAND(node);
  1353.             count = 0;
  1354.             for (scan = reginput; *scan == ch; scan++)
  1355.                 count++;
  1356.             return(count);
  1357.             break;
  1358.         case ANYOF:
  1359.             return(_tcsspn(reginput, OPERAND(node)));
  1360.             break;
  1361.         case ANYBUT:
  1362.             return(_tcscspn(reginput, OPERAND(node)));
  1363.             break;
  1364.         default:        // Oh dear.  Called inappropriately. 
  1365.             regerror( REGERR_BAD_REGREPEAT );
  1366.             return(0);    // Best compromise. 
  1367.             break;
  1368.     }
  1369.     // NOTREACHED 
  1370. }
  1371.  
  1372. #ifdef _RE_DEBUG
  1373.  
  1374. // regdump - dump a regexp onto stdout in vaguely comprehensible form
  1375.  
  1376. void regexp::regdump()
  1377. {
  1378.     LPTSTR s;
  1379.     TCHAR op = EXACTLY;    // Arbitrary non-END op. 
  1380.     LPTSTR next;
  1381.  
  1382.     s = _tcsinc(program);
  1383.     while (op != END)
  1384.     {    // While that wasn't END last time... 
  1385.         op = OP(s);
  1386.         _tprintf(_T( "%2d%s" ), s-program, regprop(s));    // Where, what. 
  1387.         next = regnext(s);
  1388.         if (next == NULL)        // Next ptr. 
  1389.             _tprintf(_T( "(0)" ));
  1390.         else 
  1391.             _tprintf(_T( "(%d)" ), (s-program)+(next-s));
  1392.         s += 3;
  1393.         if (op == ANYOF || op == ANYBUT || op == EXACTLY)
  1394.         {
  1395.             // Literal string, where present. 
  1396.             while (*s != '\0')
  1397.             {
  1398.                 _puttchar(*s);
  1399.                 s = _tcsinc(s);
  1400.             }
  1401.             s = _tcsinc(s);
  1402.         }
  1403.         _puttchar('\n');
  1404.     }
  1405.  
  1406.     // Header fields of interest. 
  1407.     if (regstart != '\0')
  1408.         _tprintf(_T( "start `%c' " ), regstart);
  1409.     if (reganch)
  1410.         _tprintf(_T( "anchored " ));
  1411.     if (regmust != NULL)
  1412.         _tprintf(_T( "must have \"%s\"" ), regmust);
  1413.     _tprintf(_T( "\n" ));
  1414. }
  1415.  
  1416. // regprop - printable representation of opcode
  1417.  
  1418. #define OUTPUT(s) case s: p = _T( #s ); break
  1419. LPTSTR CRegProgramAccessor::regprop( LPTSTR op )
  1420. {
  1421.     LPTSTR p;
  1422.     static TCHAR buf[50];
  1423.  
  1424.     (void) _tcscpy(buf, _T( ":" ));
  1425.  
  1426.     switch (OP(op))
  1427.     {
  1428.     OUTPUT( BOL );
  1429.     OUTPUT( EOL );
  1430.     OUTPUT( ANY );
  1431.     OUTPUT( ANYOF );
  1432.     OUTPUT( ANYBUT );
  1433.     OUTPUT( BRANCH );
  1434.     OUTPUT( EXACTLY );
  1435.     OUTPUT( NOTHING );
  1436.     OUTPUT( BACK );
  1437.     OUTPUT( END );
  1438.     OUTPUT( STAR );
  1439.     OUTPUT( PLUS );
  1440.     OUTPUT( WORDA );
  1441.     OUTPUT( WORDZ );
  1442.     case OPEN+1: case OPEN+2: case OPEN+3:
  1443.     case OPEN+4: case OPEN+5: case OPEN+6:
  1444.     case OPEN+7: case OPEN+8: case OPEN+9:
  1445.         _stprintf(buf+_tcslen(buf), _T( "OPEN%d" ), OP(op)-OPEN);
  1446.         p = NULL;
  1447.         break;
  1448.     case CLOSE+1: case CLOSE+2: case CLOSE+3:
  1449.     case CLOSE+4: case CLOSE+5: case CLOSE+6:
  1450.     case CLOSE+7: case CLOSE+8: case CLOSE+9:
  1451.         _stprintf(buf+_tcslen(buf), _T( "CLOSE%d" ), OP(op)-CLOSE);
  1452.         p = NULL;
  1453.         break;
  1454.     default:
  1455.         regerror( REGERR_CORRUPTED_OPCODE );
  1456.         break;
  1457.     }
  1458.     if (p != NULL)
  1459.         (void) _tcscat(buf, p);
  1460.     return(buf);
  1461. }
  1462. #endif
  1463.  
  1464. ///////////////////////////////////////////////////////////////////////////////
  1465.  
  1466. Regexp::Regexp()
  1467.     : rc(0),
  1468.     m_string(0)
  1469. {
  1470. }
  1471.  
  1472. Regexp::Regexp( LPCTSTR exp, BOOL iCase )
  1473. : rc( new regexp( exp ? exp : _T(""), iCase ) ),
  1474.     m_string( 0 )
  1475. {
  1476. }
  1477.  
  1478. Regexp::Regexp( const Regexp &r )
  1479.     : rc( r.rc ),
  1480.     m_sError(r.m_sError),
  1481.     m_string(r.m_string)
  1482. {
  1483.     if ( rc )
  1484.         rc->count++;
  1485. }
  1486.  
  1487. void Regexp::SetRegExp(LPCTSTR exp, BOOL iCase)
  1488. {
  1489.     if ( rc && rc->count-- == 0 )
  1490.     {
  1491.         delete rc;
  1492.         rc = 0;
  1493.     }
  1494.  
  1495.     rc = new regexp(exp ? exp : _T(""), iCase);
  1496. }
  1497.  
  1498. const Regexp & Regexp::operator=( const Regexp & r )
  1499. {
  1500.     if ( this != &r )
  1501.     {
  1502.         if ( rc && rc->count-- == 0 )
  1503.             delete rc;
  1504.  
  1505.         rc = r.rc;
  1506.         if ( rc )
  1507.             rc->count++;
  1508.  
  1509.         m_string = r.m_string;
  1510.         m_sError = r.m_sError;
  1511.     }    
  1512.     return *this;
  1513. }
  1514.  
  1515. Regexp::~Regexp()
  1516. {
  1517.     if ( rc && rc->count-- == 0 )
  1518.         delete rc;
  1519. }
  1520.  
  1521. bool Regexp::Match(LPCTSTR s, long lStartPos)
  1522. {
  1523.     if ((s == 0) || (lstrlen(s) < lStartPos))
  1524.         return false;
  1525.  
  1526.     ClearErrorString();
  1527.     m_string = s + lStartPos;
  1528.     bool ret = false;
  1529.     if ( rc )
  1530.     {
  1531.         // copy on write !
  1532.  
  1533.         if ( rc->count )
  1534.         {
  1535.             rc->count--;
  1536.             rc = rc->getCopy();
  1537.         }
  1538.  
  1539.         ret = rc->regexec( s + lStartPos );
  1540.         int i = 0;
  1541.         if ( ret )
  1542.             for ( i = 0; i < Regexp::NSUBEXP && rc->startp[i] ; i++ )
  1543.                 ;
  1544.         rc->numSubs = i - 1;
  1545.     }
  1546.     else
  1547.         m_sError = CRegErrorHandler::FindErr( REGERR_NO_REGEXP );
  1548.     return ret;
  1549. }
  1550.  
  1551. str_t Regexp::GetReplaceString( LPCTSTR source ) const
  1552. {
  1553.     ClearErrorString();
  1554.     if ( rc )
  1555.         return rc->GetReplaceString( source );
  1556.     else
  1557.         m_sError = CRegErrorHandler::FindErr( REGERR_NO_REGEXP );
  1558.     return _T( "" );
  1559. }
  1560.  
  1561. int Regexp::SubStrings() const
  1562. {
  1563.     ClearErrorString();
  1564.     int ret = -1;
  1565.     if ( rc )
  1566.         ret = rc->numSubs;
  1567.     else
  1568.         m_sError = CRegErrorHandler::FindErr( REGERR_NO_REGEXP );
  1569.     return ret;
  1570. }
  1571.  
  1572. int Regexp::SubStart( unsigned int i ) const
  1573. {
  1574.     ClearErrorString();
  1575.     int ret = -1;
  1576.     if ( rc )
  1577.         ret = rc->startp[safeIndex(i)] - m_string;
  1578.     else
  1579.         m_sError = CRegErrorHandler::FindErr( REGERR_NO_REGEXP );
  1580.     return ret;
  1581. }
  1582.  
  1583. int Regexp::SubLength( unsigned int i ) const
  1584. {
  1585.     ClearErrorString();
  1586.     int ret = -1;
  1587.     if ( rc )
  1588.     {
  1589.         i = safeIndex(i);
  1590.         ret = rc->endp[i] - rc->startp[i];
  1591.     }
  1592.     else
  1593.         m_sError = CRegErrorHandler::FindErr( REGERR_NO_REGEXP );
  1594.     return ret;
  1595. }
  1596.  
  1597. bool Regexp::CompiledOK() const
  1598. {
  1599.     return rc ? rc->Status() : false;
  1600. }
  1601.  
  1602. #ifdef _RE_DEBUG
  1603. void Regexp::Dump()
  1604. {
  1605.     if ( rc )
  1606.         rc->regdump();
  1607. #if defined( _DEBUG )
  1608.     else
  1609.         TRACE0( "No regexp to dump out\n" );
  1610. #endif
  1611. }
  1612. #endif
  1613.  
  1614. int Regexp::safeIndex( unsigned int i ) const
  1615. {
  1616.     return i < Regexp::NSUBEXP ? i : Regexp::NSUBEXP;
  1617. }
  1618.  
  1619. str_t Regexp::operator[]( unsigned int i ) const
  1620. {
  1621.     ClearErrorString();
  1622.     #ifdef _DEBUG
  1623.      assert(rc);
  1624.     #endif
  1625.     if ( rc )
  1626.     {
  1627.         str_t buffer;
  1628.         buffer.assign(rc->startp[i], SubLength(i));
  1629.         return buffer;
  1630.     }
  1631.     else
  1632.     {
  1633.         m_sError = CRegErrorHandler::FindErr( REGERR_NO_REGEXP );
  1634.         return _T("");
  1635.     }
  1636. }
  1637.  
  1638. void regexp::ignoreCase( const TCHAR * in, TCHAR * out )
  1639. {
  1640.     // copy in to out making every top level character a [Aa] set
  1641.     BOOL inRange = FALSE;
  1642.     while( *in )
  1643.     {
  1644.         if ( *in == '[' )
  1645.             inRange = TRUE;
  1646.         if ( *in == ']' )
  1647.             inRange = FALSE;
  1648.         if ( ! inRange && isalpha( *in ) )
  1649.         {
  1650.             *out++ = '[';
  1651.             *out++ = (TCHAR)toupper( *in );
  1652.             *out++ = (TCHAR)tolower( *in );
  1653.             *out++ = ']';
  1654.         }
  1655.         else
  1656.             *out++ = *in;
  1657.         in++;
  1658.     }
  1659.     *out = 0;
  1660. }
  1661.  
  1662. // GetReplaceString    - Converts a replace expression to a string
  1663. //                    - perform substitutions after a regexp match
  1664. // Returns            - The resultant string
  1665. str_t regexp::GetReplaceString( const TCHAR* sReplaceExp ) const
  1666. {
  1667.     str_t szEmpty( _T( "" ) );
  1668.  
  1669.     TCHAR *src = (TCHAR *)sReplaceExp;
  1670.     TCHAR *buf;
  1671.     TCHAR c;
  1672.     int no;
  1673.     size_t len;
  1674.  
  1675.     if( sReplaceExp == NULL )
  1676.     {
  1677.         regerror( REGERR_NULL_TO_REGSUB  );
  1678.         return szEmpty;
  1679.     }
  1680.     if ( program == NULL )
  1681.         return szEmpty;
  1682.  
  1683.     if ( *program != MAGIC)
  1684.     {
  1685.         regerror( REGERR_DAMAGED_REGEXP_REGSUB );
  1686.         return szEmpty;
  1687.     }
  1688.  
  1689.     // First compute the length of the string
  1690.     int replacelen = 0;
  1691.     while ((c = *src++) != _T('\0')) 
  1692.     {
  1693.         if (c == _T('&'))
  1694.             no = 0;
  1695.         else if (c == _T('\\') && isdigit(*src))
  1696.             no = *src++ - _T('0');
  1697.         else
  1698.             no = -1;
  1699.  
  1700.         if (no < 0) 
  1701.         {    
  1702.             // Ordinary character. 
  1703.             if (c == _T('\\') && (*src == _T('\\') || *src == _T('&')))
  1704.                 c = *src++;
  1705.             replacelen++;
  1706.         } 
  1707.         else if (startp[no] != NULL && endp[no] != NULL &&
  1708.                     endp[no] > startp[no]) 
  1709.         {
  1710.             // Get tagged expression
  1711.             len = endp[no] - startp[no];
  1712.             replacelen += len;
  1713.         }
  1714.     }
  1715.  
  1716.     str_t szReplace;
  1717.     szReplace.resize(replacelen);
  1718.     buf = szReplace.begin();
  1719.  
  1720.     // Now we can create the string
  1721.     src = (TCHAR *)sReplaceExp;
  1722.     while ((c = *src++) != _T('\0')) 
  1723.     {
  1724.         if (c == _T('&'))
  1725.             no = 0;
  1726.         else if (c == _T('\\') && isdigit(*src))
  1727.             no = *src++ - _T('0');
  1728.         else
  1729.             no = -1;
  1730.  
  1731.         if (no < 0) 
  1732.         {    
  1733.             // Ordinary character. 
  1734.             if (c == _T('\\') && (*src == _T('\\') || *src == _T('&')))
  1735.                 c = *src++;
  1736.             *buf++ = c;
  1737.         } 
  1738.         else if (startp[no] != NULL && endp[no] != NULL &&
  1739.                     endp[no] > startp[no]) 
  1740.         {
  1741.             // Get tagged expression
  1742.             len = endp[no] - startp[no];
  1743.             _tcsncpy(buf, startp[no], len);
  1744.             buf += len;
  1745.             if (len != 0 && *(buf-1) == _T( '\0' ))
  1746.             {    /* strncpy hit NUL. */
  1747.                 regerror( REGERR_DAMAGED_MATCH_STRING );
  1748.                 return szEmpty;
  1749.             }
  1750.         }
  1751.     }
  1752.  
  1753.     return szReplace;
  1754. }
  1755.  
  1756. str_t Regexp::GetErrorString() const
  1757. {
  1758.     // make sure that if status == 0 that we have an error string
  1759.     #ifdef _DEBUG
  1760.      assert( ( ! CompiledOK() ) ? ( rc ? rc->GetErrorString() : m_sError).size() != 0 : 1 );
  1761.     #endif
  1762.     return rc ? rc->GetErrorString() : m_sError ;
  1763. }
  1764.  
  1765. short Regexp::GetErrorNumber() const
  1766. {
  1767.     return short(rc ? rc->GetErrorNumber() : 0);
  1768. }
  1769.  
  1770. void Regexp::ClearErrorString() const
  1771. {
  1772.     if ( rc )
  1773.         rc->ClearErrorString();
  1774.     m_sError.erase();
  1775. }
  1776.  
  1777.