home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume17 / regex / part01 next >
Encoding:
Internet Message Format  |  1991-03-25  |  43.2 KB

  1. From: johnk@wrq.com (John Kercheval)
  2. Newsgroups: comp.sources.misc
  3. Subject: v17i069:  regex - REGEX Globber (Wild Card Matching), Part01/01
  4. Message-ID: <1991Mar25.220130.22238@sparky.IMD.Sterling.COM>
  5. Date: 25 Mar 91 22:01:30 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: eef2860b 4b30a023 e30080d9 612cca42
  8.  
  9. Submitted-by: John Kercheval <johnk@wrq.com>
  10. Posting-number: Volume 17, Issue 69
  11. Archive-name: regex/part01
  12.  
  13. Here is the shar archive of V1.10 of REGEX Globber.
  14. This is a *IX wildcard globber I butchered, hacked and cajoled together
  15. after seeing and hearing about and becoming disgusted with several similar
  16. routines which had one or more of the following attributes:  slow, buggy,
  17. required large levels of recursion on matches, required grotesque levels
  18. of recursion on failing matches using '*', full of caveats about usability
  19. or copyrights.
  20.  
  21. These routines are fairly well tested and reasonably fast.  I have made 
  22. an effort to fail on all bad patterns and to quickly determine failing '*' 
  23. patterns.  This parser will also do quite a bit of the '*' matching via 
  24. quick linear loops versus the standard blind recursive descent.
  25.  
  26. This parser has been submitted to profilers at various stages of development
  27. and has come through quite well.  If the last millisecond is important to
  28. you then some time can be shaved by using stack allocated variables in
  29. place of many of the pointer follows (which may be done fairly often) found
  30. in regex_match and regex_match_after_star (ie *p, *t).
  31.  
  32. No attempt is made to provide general [pat,pat] comparisons.  The specific
  33. subcases supplied by these routines is [pat,text] which is sufficient
  34. for the large majority of cases (should you care).
  35.  
  36. Since regex_match may return one of three different values depending upon
  37. the pattern and text I have made a simple shell for convenience (match()).
  38. Also included is an is_pattern routine to quickly check a potential pattern
  39. for regex special characters.  I even placed this all in a header file for
  40. you lazy folks!
  41.  
  42. Having said all that, here is my own reinvention of the wheel.  Please
  43. enjoy it's use and I hope it is of some help to those with need ....
  44.  
  45.                                 jbk
  46. ----
  47. #! /bin/sh
  48. # This is a shell archive.  Remove anything before this line, then feed it
  49. # into a shell via "sh file" or similar.  To overwrite existing files,
  50. # type "sh file -c".
  51. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  52. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  53. # Contents:  match.! match.c match.doc match.h matchmak matchtst.bat
  54. #   readme.doc
  55. # Wrapped by kent@sparky on Mon Mar 25 14:33:28 1991
  56. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  57. echo If this archive is complete, you will see the following message:
  58. echo '          "shar: End of archive 1 (of 1)."'
  59. if test -f 'match.!' -a "${1}" != "-c" ; then 
  60.   echo shar: Will not clobber existing file \"'match.!'\"
  61. else
  62.   echo shar: Extracting \"'match.!'\" \(950 characters\)
  63.   sed "s/^X//" >'match.!' <<'END_OF_FILE'
  64. X
  65. X..............................................................................
  66. X..                                                                          ..
  67. X.                    REGEX Globber (Wild Card Matching)                      .
  68. X.                                                                            .
  69. X.               A *IX SH style pattern matcher written in C                  .
  70. X.                   V1.10 Dedicated to the Public Domain                     .
  71. X.                                                                            .
  72. X.                                March  12, 1991                             .
  73. X.                                 J. Kercheval                               .
  74. X.                         [72450,3702] -- johnk@wrq.com                      .
  75. X..                                                                          ..
  76. X..............................................................................
  77. X
  78. END_OF_FILE
  79.   if test 950 -ne `wc -c <'match.!'`; then
  80.     echo shar: \"'match.!'\" unpacked with wrong size!
  81.   fi
  82.   # end of 'match.!'
  83. fi
  84. if test -f 'match.c' -a "${1}" != "-c" ; then 
  85.   echo shar: Will not clobber existing file \"'match.c'\"
  86. else
  87.   echo shar: Extracting \"'match.c'\" \(16844 characters\)
  88.   sed "s/^X//" >'match.c' <<'END_OF_FILE'
  89. X/*
  90. X EPSHeader
  91. X
  92. X   File: match.c
  93. X   Author: J. Kercheval
  94. X   Created: Sat, 01/05/1991  22:21:49
  95. X*/
  96. X/*
  97. X EPSRevision History
  98. X
  99. X   J. Kercheval  Wed, 02/20/1991  22:29:01  Released to Public Domain
  100. X   J. Kercheval  Fri, 02/22/1991  15:29:01  fix '\' bugs (two :( of them)
  101. X   J. Kercheval  Sun, 03/10/1991  19:31:29  add error return to matche()
  102. X   J. Kercheval  Sun, 03/10/1991  20:11:11  add is_valid_pattern code
  103. X   J. Kercheval  Sun, 03/10/1991  20:37:11  beef up main()
  104. X   J. Kercheval  Tue, 03/12/1991  22:25:10  Released as V1.1 to Public Domain
  105. X*/
  106. X
  107. X/*
  108. X   Wildcard Pattern Matching
  109. X*/
  110. X
  111. X
  112. X#include "match.h"
  113. X
  114. Xint matche_after_star (register char *pattern, register char *text);
  115. Xint fast_match_after_star (register char *pattern, register char *text);
  116. X
  117. X/*----------------------------------------------------------------------------
  118. X*
  119. X* Return TRUE if PATTERN has any special wildcard characters
  120. X*
  121. X----------------------------------------------------------------------------*/
  122. X
  123. XBOOLEAN is_pattern (char *p)
  124. X{
  125. X    while ( *p ) {
  126. X        switch ( *p++ ) {
  127. X            case '?':
  128. X            case '*':
  129. X            case '[':
  130. X            case '\\':
  131. X                return TRUE;
  132. X        }
  133. X    }
  134. X    return FALSE;
  135. X}
  136. X
  137. X
  138. X/*----------------------------------------------------------------------------
  139. X*
  140. X* Return TRUE if PATTERN has is a well formed regular expression according
  141. X* to the above syntax
  142. X*
  143. X* error_type is a return code based on the type of pattern error.  Zero is
  144. X* returned in error_type if the pattern is a valid one.  error_type return
  145. X* values are as follows:
  146. X*
  147. X*   PATTERN_VALID - pattern is well formed
  148. X*   PATTERN_ESC   - pattern has invalid escape ('\' at end of pattern)
  149. X*   PATTERN_RANGE - [..] construct has a no end range in a '-' pair (ie [a-])
  150. X*   PATTERN_CLOSE - [..] construct has no end bracket (ie [abc-g )
  151. X*   PATTERN_EMPTY - [..] construct is empty (ie [])
  152. X*
  153. X----------------------------------------------------------------------------*/
  154. X
  155. XBOOLEAN is_valid_pattern (char *p, int *error_type)
  156. X{
  157. X    
  158. X    /* init error_type */
  159. X    *error_type = PATTERN_VALID;
  160. X    
  161. X    /* loop through pattern to EOS */
  162. X    while( *p ) {
  163. X
  164. X        /* determine pattern type */
  165. X        switch( *p ) {
  166. X
  167. X            /* check literal escape, it cannot be at end of pattern */
  168. X            case '\\':
  169. X                if( !*++p ) {
  170. X                    *error_type = PATTERN_ESC;
  171. X                    return FALSE;
  172. X                }
  173. X                p++;
  174. X                break;
  175. X
  176. X            /* the [..] construct must be well formed */
  177. X            case '[':
  178. X                p++;
  179. X
  180. X                /* if the next character is ']' then bad pattern */
  181. X                if ( *p == ']' ) {
  182. X                    *error_type = PATTERN_EMPTY;
  183. X                    return FALSE;
  184. X                }
  185. X                
  186. X                /* if end of pattern here then bad pattern */
  187. X                if ( !*p ) {
  188. X                    *error_type = PATTERN_CLOSE;
  189. X                    return FALSE;
  190. X                }
  191. X
  192. X                /* loop to end of [..] construct */
  193. X                while( *p != ']' ) {
  194. X
  195. X                    /* check for literal escape */
  196. X                    if( *p == '\\' ) {
  197. X                        p++;
  198. X
  199. X                        /* if end of pattern here then bad pattern */
  200. X                        if ( !*p++ ) {
  201. X                            *error_type = PATTERN_ESC;
  202. X                            return FALSE;
  203. X                        }
  204. X                    }
  205. X                    else
  206. X                        p++;
  207. X
  208. X                    /* if end of pattern here then bad pattern */
  209. X                    if ( !*p ) {
  210. X                        *error_type = PATTERN_CLOSE;
  211. X                        return FALSE;
  212. X                    }
  213. X
  214. X                    /* if this a range */
  215. X                    if( *p == '-' ) {
  216. X
  217. X                        /* we must have an end of range */
  218. X                        if ( !*++p || *p == ']' ) {
  219. X                            *error_type = PATTERN_RANGE;
  220. X                            return FALSE;
  221. X                        }
  222. X                        else {
  223. X
  224. X                            /* check for literal escape */
  225. X                            if( *p == '\\' )
  226. X                                p++;
  227. X
  228. X                            /* if end of pattern here then bad pattern */
  229. X                            if ( !*p++ ) {
  230. X                                *error_type = PATTERN_ESC;
  231. X                                return FALSE;
  232. X                            }
  233. X                        }
  234. X                    }
  235. X                }
  236. X                break;
  237. X
  238. X            /* all other characters are valid pattern elements */
  239. X            case '*':
  240. X            case '?':
  241. X            default:
  242. X                p++;                              /* "normal" character */
  243. X                break;
  244. X         }
  245. X     }
  246. X
  247. X     return TRUE;
  248. X}
  249. X
  250. X
  251. X/*----------------------------------------------------------------------------
  252. X*
  253. X*  Match the pattern PATTERN against the string TEXT;
  254. X*
  255. X*  returns MATCH_VALID if pattern matches, or an errorcode as follows
  256. X*  otherwise:
  257. X*
  258. X*            MATCH_PATTERN  - bad pattern
  259. X*            MATCH_LITERAL  - match failure on literal mismatch
  260. X*            MATCH_RANGE    - match failure on [..] construct
  261. X*            MATCH_ABORT    - premature end of text string
  262. X*            MATCH_END      - premature end of pattern string
  263. X*            MATCH_VALID    - valid match
  264. X*
  265. X*
  266. X*  A match means the entire string TEXT is used up in matching.
  267. X*
  268. X*  In the pattern string:
  269. X*       `*' matches any sequence of characters (zero or more)
  270. X*       `?' matches any character
  271. X*       [SET] matches any character in the specified set,
  272. X*       [!SET] or [^SET] matches any character not in the specified set.
  273. X*
  274. X*  A set is composed of characters or ranges; a range looks like
  275. X*  character hyphen character (as in 0-9 or A-Z).  [0-9a-zA-Z_] is the
  276. X*  minimal set of characters allowed in the [..] pattern construct.
  277. X*  Other characters are allowed (ie. 8 bit characters) if your system
  278. X*  will support them.
  279. X*
  280. X*  To suppress the special syntactic significance of any of `[]*?!^-\',
  281. X*  and match the character exactly, precede it with a `\'.
  282. X*
  283. X----------------------------------------------------------------------------*/
  284. X
  285. Xint matche ( register char *p, register char *t )
  286. X{
  287. X    register char range_start, range_end;  /* start and end in range */
  288. X
  289. X    BOOLEAN invert;             /* is this [..] or [!..] */
  290. X    BOOLEAN member_match;       /* have I matched the [..] construct? */
  291. X    BOOLEAN loop;               /* should I terminate? */
  292. X
  293. X    for ( ; *p; p++, t++ ) {
  294. X
  295. X        /* if this is the end of the text then this is the end of the match */
  296. X        if (!*t) {
  297. X            return ( *p == '*' && *++p == '\0' ) ? MATCH_VALID : MATCH_ABORT;
  298. X        }
  299. X
  300. X        /* determine and react to pattern type */
  301. X        switch ( *p ) {
  302. X
  303. X            /* single any character match */
  304. X            case '?':
  305. X                break;
  306. X
  307. X            /* multiple any character match */
  308. X            case '*':
  309. X                return matche_after_star (p, t);
  310. X
  311. X            /* [..] construct, single member/exclusion character match */
  312. X            case '[': {
  313. X
  314. X                /* move to beginning of range */
  315. X                p++;
  316. X
  317. X                /* check if this is a member match or exclusion match */
  318. X                invert = FALSE;
  319. X                if ( *p == '!' || *p == '^') {
  320. X                    invert = TRUE;
  321. X                    p++;
  322. X                }
  323. X
  324. X                /* if closing bracket here or at range start then we have a
  325. X                   malformed pattern */
  326. X                if ( *p == ']' ) {
  327. X                    return MATCH_PATTERN;
  328. X                }
  329. X
  330. X                member_match = FALSE;
  331. X                loop = TRUE;
  332. X
  333. X                while ( loop ) {
  334. X
  335. X                    /* if end of construct then loop is done */
  336. X                    if (*p == ']') {
  337. X                        loop = FALSE;
  338. X                        continue;
  339. X                    }
  340. X
  341. X                    /* matching a '!', '^', '-', '\' or a ']' */
  342. X                    if ( *p == '\\' ) {
  343. X                        range_start = range_end = *++p;
  344. X                    }
  345. X                    else {
  346. X                        range_start = range_end = *p;
  347. X                    }
  348. X
  349. X                    /* if end of pattern then bad pattern (Missing ']') */
  350. X                    if (!*p)
  351. X                        return MATCH_PATTERN;
  352. X
  353. X                    /* check for range bar */
  354. X                    if (*++p == '-') {
  355. X
  356. X                        /* get the range end */
  357. X                        range_end = *++p;
  358. X
  359. X                        /* if end of pattern or construct then bad pattern */
  360. X                        if (range_end == '\0' || range_end == ']')
  361. X                            return MATCH_PATTERN;
  362. X
  363. X                        /* special character range end */
  364. X                        if (range_end == '\\') {
  365. X                            range_end = *++p;
  366. X
  367. X                            /* if end of text then we have a bad pattern */
  368. X                            if (!range_end)
  369. X                                return MATCH_PATTERN;
  370. X                        }
  371. X
  372. X                        /* move just beyond this range */
  373. X                        p++;
  374. X                    }
  375. X
  376. X                    /* if the text character is in range then match found.
  377. X                       make sure the range letters have the proper
  378. X                       relationship to one another before comparison */
  379. X                    if ( range_start < range_end  ) {
  380. X                        if (*t >= range_start && *t <= range_end) {
  381. X                            member_match = TRUE;
  382. X                            loop = FALSE;
  383. X                        }
  384. X                    }
  385. X                    else {
  386. X                        if (*t >= range_end && *t <= range_start) {
  387. X                            member_match = TRUE;
  388. X                            loop = FALSE;
  389. X                        }
  390. X                    }
  391. X                }
  392. X
  393. X                /* if there was a match in an exclusion set then no match */
  394. X                /* if there was no match in a member set then no match */
  395. X                if ((invert && member_match) ||
  396. X                   !(invert || member_match))
  397. X                    return MATCH_RANGE;
  398. X
  399. X                /* if this is not an exclusion then skip the rest of the [...]
  400. X                    construct that already matched. */
  401. X                if (member_match) {
  402. X                    while (*p != ']') {
  403. X
  404. X                        /* bad pattern (Missing ']') */
  405. X                        if (!*p)
  406. X                            return MATCH_PATTERN;
  407. X
  408. X                        /* skip exact match */
  409. X                        if (*p == '\\') {
  410. X                            p++;
  411. X
  412. X                            /* if end of text then we have a bad pattern */
  413. X                            if (!*p)
  414. X                                return MATCH_PATTERN;
  415. X                        }
  416. X
  417. X                        /* move to next pattern char */
  418. X                        p++;
  419. X                    }
  420. X                }
  421. X
  422. X                break;
  423. X            }
  424. X
  425. X            /* next character is quoted and must match exactly */
  426. X            case '\\':
  427. X
  428. X                /* move pattern pointer to quoted char and fall through */
  429. X                p++;
  430. X
  431. X                /* if end of text then we have a bad pattern */
  432. X                if (!*p)
  433. X                    return MATCH_PATTERN;
  434. X
  435. X            /* must match this character exactly */
  436. X            default:
  437. X                if (*p != *t)
  438. X                    return MATCH_LITERAL;
  439. X        }
  440. X    }
  441. X
  442. X    /* if end of text not reached then the pattern fails */
  443. X    if ( *t )
  444. X        return MATCH_END;
  445. X    else
  446. X        return MATCH_VALID;
  447. X}
  448. X
  449. X
  450. X/*----------------------------------------------------------------------------
  451. X*
  452. X* recursively call matche() with final segment of PATTERN and of TEXT.
  453. X*
  454. X----------------------------------------------------------------------------*/
  455. X
  456. Xint matche_after_star (register char *p, register char *t)
  457. X{
  458. X    register int match = 0;
  459. X    register nextp;
  460. X
  461. X    /* pass over existing ? and * in pattern */
  462. X    while ( *p == '?' || *p == '*' ) {
  463. X
  464. X        /* take one char for each ? and + */
  465. X        if ( *p == '?' ) {
  466. X
  467. X            /* if end of text then no match */
  468. X            if ( !*t++ ) {
  469. X                return MATCH_ABORT;
  470. X            }
  471. X        }
  472. X
  473. X        /* move to next char in pattern */
  474. X        p++;
  475. X    }
  476. X
  477. X    /* if end of pattern we have matched regardless of text left */
  478. X    if ( !*p ) {
  479. X        return MATCH_VALID;
  480. X    }
  481. X
  482. X    /* get the next character to match which must be a literal or '[' */
  483. X    nextp = *p;
  484. X    if ( nextp == '\\' ) {
  485. X        nextp = p[1];
  486. X
  487. X        /* if end of text then we have a bad pattern */
  488. X        if (!nextp)
  489. X            return MATCH_PATTERN;
  490. X    }
  491. X
  492. X    /* Continue until we run out of text or definite result seen */
  493. X    do {
  494. X
  495. X        /* a precondition for matching is that the next character
  496. X           in the pattern match the next character in the text or that
  497. X           the next pattern char is the beginning of a range.  Increment
  498. X           text pointer as we go here */
  499. X        if ( nextp == *t || nextp == '[' ) {
  500. X            match = matche(p, t);
  501. X        }
  502. X
  503. X        /* if the end of text is reached then no match */
  504. X        if ( !*t++ ) match = MATCH_ABORT;
  505. X
  506. X    } while ( match != MATCH_VALID && 
  507. X              match != MATCH_ABORT &&
  508. X              match != MATCH_PATTERN);
  509. X
  510. X    /* return result */
  511. X    return match;
  512. X}
  513. X
  514. X
  515. X/*----------------------------------------------------------------------------
  516. X*
  517. X* match() is a shell to matche() to return only BOOLEAN values.
  518. X*
  519. X----------------------------------------------------------------------------*/
  520. X
  521. XBOOLEAN match( char *p, char *t )
  522. X{
  523. X    int error_type;
  524. X    error_type = matche(p,t);
  525. X    return (error_type == MATCH_VALID ) ? TRUE : FALSE;
  526. X}
  527. X
  528. X
  529. X#ifdef TEST
  530. X
  531. X    /*
  532. X    * This test main expects as first arg the pattern and as second arg
  533. X    * the match string.  Output is yaeh or nay on match.  If nay on
  534. X    * match then the error code is parsed and written.
  535. X    */
  536. X
  537. X    #include <stdio.h>
  538. X
  539. X    int main(int argc, char *argv[])
  540. X    {
  541. X        int error;
  542. X        int is_valid_error;
  543. X
  544. X        if (argc != 3) {
  545. X            printf("Usage:  MATCH Pattern Text\n");
  546. X        }
  547. X        else {
  548. X            printf("Pattern: %s\n", argv[1]);
  549. X            printf("Text   : %s\n", argv[2]);
  550. X            
  551. X            if (!is_pattern(argv[1])) {
  552. X                printf("    First Argument Is Not A Pattern\n");
  553. X            }
  554. X            else {
  555. X                error = matche(argv[1],argv[2]);
  556. X                is_valid_pattern(argv[1],&is_valid_error);
  557. X
  558. X                switch ( error ) {
  559. X                    case MATCH_VALID:
  560. X                        printf("    Match Successful");
  561. X                        if (is_valid_error != PATTERN_VALID)
  562. X                            printf(" -- is_valid_pattern() is complaining\n");
  563. X                        else
  564. X                            printf("\n");
  565. X                        break;
  566. X                    case MATCH_LITERAL:
  567. X                        printf("    Match Failed on Literal\n");
  568. X                        break;
  569. X                    case MATCH_RANGE:
  570. X                        printf("    Match Failed on [..]\n");
  571. X                        break;
  572. X                    case MATCH_ABORT:
  573. X                        printf("    Match Failed on Early Text Termination\n");
  574. X                        break;
  575. X                    case MATCH_END:
  576. X                        printf("    Match Failed on Early Pattern Termination\n");
  577. X                        break;
  578. X                    case MATCH_PATTERN:
  579. X                        switch ( is_valid_error ) {
  580. X                            case PATTERN_VALID:
  581. X                                printf("    Internal Disagreement On Pattern\n");
  582. X                                break;
  583. X                            case PATTERN_ESC:
  584. X                                printf("    Literal Escape at End of Pattern\n");
  585. X                                break;
  586. X                            case PATTERN_RANGE:
  587. X                                printf("    No End of Range in [..] Construct\n");
  588. X                                break;
  589. X                            case PATTERN_CLOSE:
  590. X                                printf("    [..] Construct is Open\n");
  591. X                                break;
  592. X                            case PATTERN_EMPTY:
  593. X                                printf("    [..] Construct is Empty\n");
  594. X                                break;
  595. X                            default:
  596. X                                printf("    Internal Error in is_valid_pattern()\n");
  597. X                        }
  598. X                        break;
  599. X                    default:
  600. X                        printf("    Internal Error in matche()\n");
  601. X                        break;
  602. X                }
  603. X            }
  604. X
  605. X        }
  606. X        return(0);
  607. X    }
  608. X
  609. X#endif
  610. END_OF_FILE
  611.   if test 16844 -ne `wc -c <'match.c'`; then
  612.     echo shar: \"'match.c'\" unpacked with wrong size!
  613.   fi
  614.   # end of 'match.c'
  615. fi
  616. if test -f 'match.doc' -a "${1}" != "-c" ; then 
  617.   echo shar: Will not clobber existing file \"'match.doc'\"
  618. else
  619.   echo shar: Extracting \"'match.doc'\" \(5288 characters\)
  620.   sed "s/^X//" >'match.doc' <<'END_OF_FILE'
  621. X
  622. X                    REGEX Globber (Wild Card Matching)
  623. X
  624. X               A *IX SH style pattern matcher written in C
  625. X                   V1.10 Dedicated to the Public Domain
  626. X
  627. X                                March  12, 1991
  628. X                                 J. Kercheval
  629. X                         [72450,3702] -- johnk@wrq.com
  630. X
  631. X
  632. X
  633. X
  634. X*IX SH style Regular Expressions
  635. X================================ 
  636. XThe *IX command SH is a working shell similar in feel to the MSDOS
  637. Xshell COMMAND.COM.  In point of fact much of what we see in our
  638. Xfamiliar DOS PROMPT was gleaned from the early UNIX shells available
  639. Xfor many of machines the people involved in the computing arena had
  640. Xat the time of the development of DOS and it's much maligned
  641. Xprecursor CP/M (although the UNIX shells were and are much more
  642. Xflexible and powerful then those on the current flock of micro
  643. Xmachines).  The designers of DOS and CP/M did some fairly strange
  644. Xthings with their command processor and OS.  One of those things was
  645. Xto only selectively adopt the regular expressions allowed within the
  646. X*IX shells.  Only '?' and '*' were allowed in filenames and even with
  647. Xthese the '*' was allowed only at the end of a pattern and in fact
  648. Xwhen used to specify the filename the '*' did not apply to extension.
  649. XThis gave rise to the all too common expression "*.*".
  650. X
  651. XREGEX Globber is a SH pattern matcher.  This allows such
  652. Xspecifications as *75.zip or * (equivelant to *.* in DOS lingo).
  653. XExpressions such as [a-e]*t would fit the name "apple.crt" or
  654. X"catspaw.bat" or "elegant".  This allows considerably wider
  655. Xflexibility in file specification, general parsing or any other
  656. Xcircumstance in which this type of pattern matching is wanted. 
  657. X
  658. XA match would mean that the entire string TEXT is used up in matching
  659. Xthe PATTERN and conversely the matched TEXT uses up the entire
  660. XPATTERN. 
  661. X
  662. XIn the specified pattern string:
  663. X     `*' matches any sequence of characters (zero or more)
  664. X     `?' matches any character
  665. X     `\' suppresses syntactic significance of a special character
  666. X     [SET] matches any character in the specified set,
  667. X     [!SET] or [^SET] matches any character not in the specified set.
  668. X
  669. XA set is composed of characters or ranges; a range looks like
  670. X'character hyphen character' (as in 0-9 or A-Z).  [0-9a-zA-Z_] is the
  671. Xminimal set of characters allowed in the [..] pattern construct.
  672. XOther characters are allowed (ie. 8 bit characters) if your system
  673. Xwill support them (it almost certainly will).
  674. X
  675. XTo suppress the special syntactic significance of any of `[]*?!^-\',
  676. Xand match the character exactly, precede it with a `\'.
  677. XTo view several examples of good and bad patterns and text see the
  678. Xoutput of MATCHTST.BAT
  679. X
  680. X
  681. XMATCH() and MATCHE()
  682. X====================
  683. X
  684. XThe match module as written has two parsing routines, one is matche()
  685. Xand the other is match().  Since match() is a call to matche() which
  686. Xsimply has its output mapped to a BOOLEAN value (ie TRUE if pattern
  687. Xmatches or FALSE otherwise), I will concentrate my explanations here
  688. Xon matche().
  689. X
  690. XThe purpose of matche() is to match a pattern against a string of
  691. Xtext (usually a file name or specification).  The match routine has
  692. Xextensive pattern validity checking built into it as part of the
  693. Xparser and allows for a robust pattern match.
  694. X
  695. XThe parser gives an error code on return of type int.  The error code
  696. Xwill be one of the the following defined values (defined in match.h):
  697. X    MATCH_PATTERN  - bad pattern or misformed pattern
  698. X    MATCH_LITERAL  - match failed on character match (standard
  699. X                     character)
  700. X    MATCH_RANGE    - match failure on character range ([..] construct)
  701. X    MATCH_ABORT    - premature end of text string (pattern longer
  702. X                     than text string)
  703. X    MATCH_END      - premature end of pattern string (text longer
  704. X                     than pattern called for)
  705. X    MATCH_VALID    - valid match using pattern
  706. X
  707. XThe functions are declared as follows:
  708. X
  709. X    BOOLEAN match (char *pattern, char *text);
  710. X
  711. X    int     matche(register char *pattern, register char *text);
  712. X
  713. X
  714. X
  715. XIS_VALID_PATTERN() and IS_PATTERN()
  716. X===================================
  717. X
  718. XThere are two routines for determining properties of a pattern
  719. Xstring.  The first, is_pattern(), is designed simply to determine if
  720. Xsome character exists within the text which is consistent with a SH
  721. Xregular expression (this function returns TRUE if so and FALSE if
  722. Xnot).  The second, is_valid_pattern() is designed to check the
  723. Xvalidity of a given pattern string (TRUE return if valid, FALSE if
  724. Xnot).  By 'validity', I mean well formed or syntactically correct.
  725. X
  726. XIn addition, is_valid_pattern() has as one of it's parameters a
  727. Xreturn code for determining the type of error found in the pattern if
  728. Xone exists.  The error codes are as follows and defined in match.h:
  729. X
  730. X    PATTERN_VALID - pattern is well formed
  731. X    PATTERN_ESC   - pattern has invalid literal escape ('\' at end of
  732. X                    pattern)
  733. X    PATTERN_RANGE - [..] construct has a no end range in a '-' pair
  734. X                    (ie [a-])
  735. X    PATTERN_CLOSE - [..] construct has no end bracket (ie [abc-g )
  736. X    PATTERN_EMPTY - [..] construct is empty (ie [])
  737. X
  738. XThe functions are declared as follows:
  739. X
  740. X    BOOLEAN is_valid_pattern (char *pattern, int *error_type);
  741. X
  742. X    BOOLEAN is_pattern (char *pattern);
  743. END_OF_FILE
  744.   if test 5288 -ne `wc -c <'match.doc'`; then
  745.     echo shar: \"'match.doc'\" unpacked with wrong size!
  746.   fi
  747.   # end of 'match.doc'
  748. fi
  749. if test -f 'match.h' -a "${1}" != "-c" ; then 
  750.   echo shar: Will not clobber existing file \"'match.h'\"
  751. else
  752.   echo shar: Extracting \"'match.h'\" \(3963 characters\)
  753.   sed "s/^X//" >'match.h' <<'END_OF_FILE'
  754. X/*
  755. X EPSHeader
  756. X
  757. X   File: match.h
  758. X   Author: J. Kercheval
  759. X   Created: Sat, 01/05/1991  22:27:18
  760. X*/
  761. X/*
  762. X EPSRevision History
  763. X
  764. X   J. Kercheval  Wed, 02/20/1991  22:28:37  Released to Public Domain
  765. X   J. Kercheval  Sun, 03/10/1991  18:02:56  add is_valid_pattern
  766. X   J. Kercheval  Sun, 03/10/1991  18:25:48  add error_type in is_valid_pattern
  767. X   J. Kercheval  Sun, 03/10/1991  18:47:47  error return from matche()
  768. X   J. Kercheval  Tue, 03/12/1991  22:24:49  Released as V1.1 to Public Domain
  769. X*/
  770. X
  771. X/*
  772. X   Wildcard Pattern Matching
  773. X*/
  774. X
  775. X#ifndef BOOLEAN
  776. X# define BOOLEAN int
  777. X# define TRUE 1
  778. X# define FALSE 0
  779. X#endif
  780. X
  781. X/* match defines */
  782. X#define MATCH_PATTERN  6    /* bad pattern */
  783. X#define MATCH_LITERAL  5    /* match failure on literal match */
  784. X#define MATCH_RANGE    4    /* match failure on [..] construct */
  785. X#define MATCH_ABORT    3    /* premature end of text string */
  786. X#define MATCH_END      2    /* premature end of pattern string */
  787. X#define MATCH_VALID    1    /* valid match */
  788. X
  789. X/* pattern defines */
  790. X#define PATTERN_VALID  0    /* valid pattern */
  791. X#define PATTERN_ESC   -1    /* literal escape at end of pattern */
  792. X#define PATTERN_RANGE -2    /* malformed range in [..] construct */
  793. X#define PATTERN_CLOSE -3    /* no end bracket in [..] construct */
  794. X#define PATTERN_EMPTY -4    /* [..] contstruct is empty */
  795. X
  796. X/*----------------------------------------------------------------------------
  797. X*
  798. X*  Match the pattern PATTERN against the string TEXT;
  799. X*
  800. X*       match() returns TRUE if pattern matches, FALSE otherwise.
  801. X*       matche() returns MATCH_VALID if pattern matches, or an errorcode
  802. X*           as follows otherwise:
  803. X*
  804. X*            MATCH_PATTERN  - bad pattern
  805. X*            MATCH_LITERAL  - match failure on literal mismatch
  806. X*            MATCH_RANGE    - match failure on [..] construct
  807. X*            MATCH_ABORT    - premature end of text string
  808. X*            MATCH_END      - premature end of pattern string
  809. X*            MATCH_VALID    - valid match
  810. X*
  811. X*
  812. X*  A match means the entire string TEXT is used up in matching.
  813. X*
  814. X*  In the pattern string:
  815. X*       `*' matches any sequence of characters (zero or more)
  816. X*       `?' matches any character
  817. X*       [SET] matches any character in the specified set,
  818. X*       [!SET] or [^SET] matches any character not in the specified set.
  819. X*
  820. X*  A set is composed of characters or ranges; a range looks like
  821. X*  character hyphen character (as in 0-9 or A-Z).  [0-9a-zA-Z_] is the
  822. X*  minimal set of characters allowed in the [..] pattern construct.
  823. X*  Other characters are allowed (ie. 8 bit characters) if your system
  824. X*  will support them.
  825. X*
  826. X*  To suppress the special syntactic significance of any of `[]*?!^-\',
  827. X*  and match the character exactly, precede it with a `\'.
  828. X*
  829. X----------------------------------------------------------------------------*/
  830. X
  831. XBOOLEAN match (char *pattern, char *text);
  832. X
  833. Xint     matche(register char *pattern, register char *text);
  834. X
  835. X/*----------------------------------------------------------------------------
  836. X*
  837. X* Return TRUE if PATTERN has any special wildcard characters
  838. X*
  839. X----------------------------------------------------------------------------*/
  840. X
  841. XBOOLEAN is_pattern (char *pattern);
  842. X
  843. X/*----------------------------------------------------------------------------
  844. X*
  845. X* Return TRUE if PATTERN has is a well formed regular expression according
  846. X* to the above syntax
  847. X*
  848. X* error_type is a return code based on the type of pattern error.  Zero is
  849. X* returned in error_type if the pattern is a valid one.  error_type return
  850. X* values are as follows:
  851. X*
  852. X*   PATTERN_VALID - pattern is well formed
  853. X*   PATTERN_ESC   - pattern has invalid escape ('\' at end of pattern)
  854. X*   PATTERN_RANGE - [..] construct has a no end range in a '-' pair (ie [a-])
  855. X*   PATTERN_CLOSE - [..] construct has no end bracket (ie [abc-g )
  856. X*   PATTERN_EMPTY - [..] construct is empty (ie [])
  857. X*
  858. X----------------------------------------------------------------------------*/
  859. X
  860. XBOOLEAN is_valid_pattern (char *pattern, int *error_type);
  861. END_OF_FILE
  862.   if test 3963 -ne `wc -c <'match.h'`; then
  863.     echo shar: \"'match.h'\" unpacked with wrong size!
  864.   fi
  865.   # end of 'match.h'
  866. fi
  867. if test -f 'matchmak' -a "${1}" != "-c" ; then 
  868.   echo shar: Will not clobber existing file \"'matchmak'\"
  869. else
  870.   echo shar: Extracting \"'matchmak'\" \(396 characters\)
  871.   sed "s/^X//" >'matchmak' <<'END_OF_FILE'
  872. X#
  873. X#
  874. X# Makefile for match.c
  875. X#
  876. X# Created 01-20-91 JBK
  877. X# Last Modified 02-13-91 JBK
  878. X#
  879. X#
  880. X
  881. XCC = cl
  882. X
  883. X#
  884. X# This is FLAGS for optimized version
  885. X#FLAGS = /c /AL /G2 /Ox /W4
  886. X
  887. X# 
  888. X# This is FLAGS for optimized version with main
  889. XFLAGS = /D TEST /AL /G2 /Ox /W4
  890. X
  891. X#
  892. X# This is FLAGS for debugging versions with main
  893. X#FLAGS = /D TEST /AL /G2 /Od /W4 /Zi /qc
  894. X
  895. X
  896. Xmatch.exe: match.c match.h
  897. X    $(CC) $(FLAGS) match.c
  898. END_OF_FILE
  899.   if test 396 -ne `wc -c <'matchmak'`; then
  900.     echo shar: \"'matchmak'\" unpacked with wrong size!
  901.   fi
  902.   # end of 'matchmak'
  903. fi
  904. if test -f 'matchtst.bat' -a "${1}" != "-c" ; then 
  905.   echo shar: Will not clobber existing file \"'matchtst.bat'\"
  906. else
  907.   echo shar: Extracting \"'matchtst.bat'\" \(3776 characters\)
  908.   sed "s/^X//" >'matchtst.bat' <<'END_OF_FILE'
  909. X@echo off
  910. X
  911. Xecho .
  912. Xecho Beginning MATCHTST
  913. Xecho .
  914. Xecho Creating file TEST.OUT
  915. Xecho .
  916. X
  917. XREM The following tests should match
  918. X
  919. Xmatch test?         testy                           > test.out
  920. Xmatch test*         test                            >> test.out
  921. Xmatch tes*t         test                            >> test.out
  922. Xmatch *test         test                            >> test.out
  923. Xmatch t*s*t         test                            >> test.out
  924. Xmatch t*s*t         tesseract                       >> test.out
  925. Xmatch t?s?          test                            >> test.out
  926. Xmatch ?s*t          psyot                           >> test.out
  927. Xmatch [a-z]s*t      asset                           >> test.out
  928. Xmatch s[!gh]t       set                             >> test.out
  929. Xmatch t[a-ce]st     test                            >> test.out
  930. Xmatch tea[ea-c]up   teacup                          >> test.out
  931. Xmatch [a-fh-z]*     jack                            >> test.out
  932. Xmatch \i\**         i*hello                         >> test.out
  933. Xmatch [\[-\]]       [                               >> test.out
  934. Xmatch [a-z\\]       \                               >> test.out
  935. Xmatch [a-z%_]       b                               >> test.out
  936. Xmatch [\]]          ]                               >> test.out
  937. Xmatch \i?*          itch                            >> test.out
  938. Xmatch \i?*          it                              >> test.out
  939. Xmatch ?*?*?t        test                            >> test.out
  940. Xmatch ?*?*?*?*      test                            >> test.out
  941. Xmatch *\]*\**\?*\[  ]this*is?atest[                 >> test.out
  942. Xmatch [a-\\]*       at                              >> test.out
  943. Xmatch [a-d\\-/]     c                               >> test.out
  944. Xmatch *t?l*his      bright-land-high-and-his        >> test.out
  945. X
  946. X
  947. XREM The following tests should fail
  948. X
  949. Xmatch test          test                            >> test.out
  950. Xmatch \             test                            >> test.out
  951. Xmatch tes\          test                            >> test.out
  952. Xmatch t*s*t         texxeract                       >> test.out
  953. Xmatch t?st          tst                             >> test.out
  954. Xmatch test?         test                            >> test.out
  955. Xmatch s[!e]t        set                             >> test.out
  956. Xmatch []            ]                               >> test.out
  957. Xmatch [             [                               >> test.out
  958. Xmatch [\[-\]        [                               >> test.out
  959. Xmatch [a            atest                           >> test.out
  960. Xmatch [a-           atest                           >> test.out
  961. Xmatch [a-z          atest                           >> test.out
  962. Xmatch [a-]*         atest                           >> test.out
  963. Xmatch [a-fh-z       jack                            >> test.out
  964. Xmatch [a-fh-z\]     jack                            >> test.out
  965. Xmatch [a-fh-z]      jack                            >> test.out
  966. Xmatch ?*?*?t*?      test                            >> test.out
  967. Xmatch *?????        test                            >> test.out
  968. Xmatch *\            test                            >> test.out
  969. Xmatch [a-e\         atest                           >> test.out
  970. Xmatch [a-\          atest                           >> test.out
  971. Xmatch [a-bd-\       atest                           >> test.out
  972. Xmatch *?*?t?        test                            >> test.out
  973. Xmatch t?            t                               >> test.out
  974. Xmatch ??*t          step                            >> test.out
  975. Xmatch [a-e]*[!t     eel                             >> test.out
  976. Xmatch [\            test                            >> test.out
  977. Xmatch \t[est        test                            >> test.out
  978. Xmatch ?*[]          hello                           >> test.out
  979. X
  980. Xecho MATCHTST Complete
  981. Xecho .
  982. END_OF_FILE
  983.   if test 3776 -ne `wc -c <'matchtst.bat'`; then
  984.     echo shar: \"'matchtst.bat'\" unpacked with wrong size!
  985.   fi
  986.   # end of 'matchtst.bat'
  987. fi
  988. if test -f 'readme.doc' -a "${1}" != "-c" ; then 
  989.   echo shar: Will not clobber existing file \"'readme.doc'\"
  990. else
  991.   echo shar: Extracting \"'readme.doc'\" \(6018 characters\)
  992.   sed "s/^X//" >'readme.doc' <<'END_OF_FILE'
  993. X03-12-91
  994. X
  995. XThis is V1.1 of REGEX Globber.
  996. X
  997. X
  998. X03-12-91
  999. X
  1000. XI have made a few changes to the match module which do several
  1001. Xthings.  The first change is an increase in bad pattern detection
  1002. Xduring a match.  It was possible, in some very unlikely cases, to
  1003. Xcook up a pattern which should result in an early bad match but which
  1004. Xwould actually cause problems for the parser.  In particular, the
  1005. Xsubcase where the literal escape '\' within an open [..] construct at
  1006. Xthe end of a pattern would end up with incorrect results.  I
  1007. Xproceeded to create some of these patterns, added them to my test
  1008. Xbattery and dove straight in.
  1009. X
  1010. XIn the interim I came across a posting to CompuServe (SMATCH by Stan
  1011. XAderman) which attempted to create a completely non-recursive
  1012. Ximplementation of match (I am not sure this is possible without
  1013. Xexplicitly creating your own stack or it's equivelant, like a binary
  1014. Xtree :-{ ).  While the code could not correctly handle multiple '*'
  1015. Xcharacters in the pattern, there was a few interesting ideas in the
  1016. Xposting.  On some occasions, running match over and over would be
  1017. Xcounter productive, especially and in particular when you have a bad
  1018. Xpattern.  I have added a fast routine, is_valid_pattern(), to
  1019. Xdetermine if the current pattern is well formed which should address
  1020. Xthis situation.
  1021. X
  1022. XOne other idea which I unceremoniously lifted from SMATCH was (in
  1023. Xhindsight a pretty obvious feature) the return of a meaningful error
  1024. Xcode from both the pattern validity routine and from match() (which I
  1025. Xrenamed to matche()).
  1026. X
  1027. XI also took some time to experiment with some ways to cut some time
  1028. Xoff the routine.  Since this is a SH pattern matcher whose intent is
  1029. Xprimarily for shell functions, the changes could not be algorithmic
  1030. Xchanges which relied on speedup over large input.  The differences in
  1031. Xexecution time were not very significant, but I did manage to gain
  1032. Xapproximately 5%-10% speedup when I removed the literal escape ('\')
  1033. Xparsing and pattern error checking.  For those of you who want to use
  1034. Xthis for filename wildcard usage, I would recommend doing this since
  1035. Xyou should use is_valid_pattern and is_pattern before going out and
  1036. Xfinding filenames and the dos path delimiter defaults to the
  1037. Xcharacter used for the literal escape ('\') anyway (Note: I will be
  1038. Xsoon be releasing a *IX style file parser in the FINDFILE, FINDNEXT
  1039. Xflavor soon to a Public Domain archive near you :-) ).
  1040. X
  1041. XI also briefly toyed with adding a non-SH regex character '+' to this
  1042. Xmodule but removed it again.  It was a performance hit of a few
  1043. Xpercent and would be mostly unused in any event.  For those
  1044. Xinterested in such a feature, the changes are truly minimal.  The
  1045. Xrequired extra work is: 
  1046. X
  1047. X   1) One case statement each in is_pattern() and is_valid_pattern() 
  1048. X   2) One case statement in matche()
  1049. X   3) One addition to a while conditional in matche_after_star()
  1050. X   4) One addition to an if conditional in matche_after_star()
  1051. X
  1052. XHint:  The case statements are all "case '+'" and the conditionals
  1053. X       have "|| *p == '+' " added to them.
  1054. X
  1055. XI have also included a file (MATCH.DOC) which describes matches use and
  1056. Xbackground as well as a little about regular expressions.
  1057. X
  1058. X                                jbk
  1059. X
  1060. X02-24-91
  1061. X
  1062. XThis is V1.01 of REGEX Globber.
  1063. X
  1064. X
  1065. X02-22-91 Seattle, WA
  1066. X
  1067. XHmm. Choke. (Foot in mouth). After griping about buggy routines and
  1068. Xliterally seconds after posting this code the first time,  I received
  1069. Xa wonderful new test evaluation tool which allows you to perform
  1070. Xcoverage analysis during testing.  Sure enough I found that about
  1071. X25% of the paths in the program were never traversed in my current
  1072. Xtest battery.  After swallowing my (overly large) pride and coming
  1073. Xup with a test battery which covered the entire path of the program
  1074. XI found a couple of minor logic bugs involving literal escapes (\)
  1075. Xwithin other patterns (ie [..] and * sequences).  I have repackaged
  1076. Xthese routines and included also the makefile I use and the test
  1077. Xbattery I use to make things a bit easier.
  1078. X
  1079. X                                jbk
  1080. X
  1081. X02-20-91 Seattle, WA
  1082. X
  1083. XHere is a *IX wildcard globber I butchered, hacked and cajoled together
  1084. Xafter seeing and hearing about and becoming disgusted with several similar
  1085. Xroutines which had one or more of the following attributes:  slow, buggy,
  1086. Xrequired large levels of recursion on matches, required grotesque levels
  1087. Xof recursion on failing matches using '*', full of caveats about usability
  1088. Xor copyrights.
  1089. X
  1090. XI submit this without copyright and with the clear understanding that
  1091. Xthis code may be used by anyone, for any reason, with any modifications
  1092. Xand without any guarantees, warrantee or statements of usability of any
  1093. Xsort.
  1094. X
  1095. XHaving gotten those cow chips out of the way, these routines are fairly
  1096. Xwell tested and reasonably fast.  I have made an effort to fail on all
  1097. Xbad patterns and to quickly determine failing '*' patterns.  This parser
  1098. Xwill also do quite a bit of the '*' matching via quick linear loops versus
  1099. Xthe standard blind recursive descent.
  1100. X
  1101. XThis parser has been submitted to profilers at various stages of development
  1102. Xand has come through quite well.  If the last millisecond is important to
  1103. Xyou then some time can be shaved by using stack allocated variables in
  1104. Xplace of many of the pointer follows (which may be done fairly often) found
  1105. Xin regex_match and regex_match_after_star (ie *p, *t).
  1106. X
  1107. XNo attempt is made to provide general [pat,pat] comparisons.  The specific
  1108. Xsubcases supplied by these routines is [pat,text] which is sufficient
  1109. Xfor the large majority of cases (should you care).
  1110. X
  1111. XSince regex_match may return one of three different values depending upon
  1112. Xthe pattern and text I have made a simple shell for convenience (match()).
  1113. XAlso included is an is_pattern routine to quickly check a potential pattern
  1114. Xfor regex special characters.  I even placed this all in a header file for
  1115. Xyou lazy folks!
  1116. X
  1117. XHaving said all that, here is my own reinvention of the wheel.  Please
  1118. Xenjoy it's use and I hope it is of some help to those with need ....
  1119. X
  1120. X
  1121. X                                jbk
  1122. X
  1123. END_OF_FILE
  1124.   if test 6018 -ne `wc -c <'readme.doc'`; then
  1125.     echo shar: \"'readme.doc'\" unpacked with wrong size!
  1126.   fi
  1127.   # end of 'readme.doc'
  1128. fi
  1129. echo shar: End of archive 1 \(of 1\).
  1130. cp /dev/null ark1isdone
  1131. MISSING=""
  1132. for I in 1 ; do
  1133.     if test ! -f ark${I}isdone ; then
  1134.     MISSING="${MISSING} ${I}"
  1135.     fi
  1136. done
  1137. if test "${MISSING}" = "" ; then
  1138.     echo You have the archive.
  1139.     rm -f ark[1-9]isdone
  1140. else
  1141.     echo You still must unpack the following archives:
  1142.     echo "        " ${MISSING}
  1143. fi
  1144. exit 0
  1145. exit 0 # Just in case...
  1146. -- 
  1147. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1148. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1149. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1150. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1151.