home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume29 / regexglb / part01 < prev    next >
Encoding:
Text File  |  1992-04-05  |  17.1 KB  |  541 lines

  1. Newsgroups: comp.sources.misc
  2. From: johnk@wrq.com (John Kercheval)
  3. Subject:  v29i057:  regex-glob - A *IX sh style REGEX Globber, Part01/01
  4. Message-ID: <1992Apr5.031310.29499@sparky.imd.sterling.com>
  5. X-Md4-Signature: eca5160d31c8c1ca5323232fa55bc5e5
  6. Date: Sun, 5 Apr 1992 03:13:10 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: johnk@wrq.com (John Kercheval)
  10. Posting-number: Volume 29, Issue 57
  11. Archive-name: regex-glob/part01
  12. Environment: C
  13.  
  14. Here 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. I submit this without copyright and with the clear understanding that
  22. this code may be used by anyone, for any reason, with any modifications
  23. and without any guarantees, warrantee or statements of usability of any
  24. sort.
  25.  
  26. Having gotten those cow chips out of the way, these routines are fairly
  27. well tested and reasonably fast.  This parser has been submitted to profilers 
  28. at various stages of development and has come through quite well.  
  29.  
  30. Here is my own reinvention of the wheel.  Please enjoy it's use and I 
  31. hope it is of some help to those with need ....
  32.  
  33.                   jbk
  34. ----------
  35. #! /bin/sh
  36. # This is a shell archive.  Remove anything before this line, then unpack
  37. # it by saving it into a file and typing "sh file".  To overwrite existing
  38. # files, type "sh file -c".  You can also feed this as standard input via
  39. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  40. # will see the following message at the end:
  41. #        "End of shell archive."
  42. # Contents:  Makefile README match.c match.h
  43. # Wrapped by vixie@cognition.pa.dec.com on Tue Mar 10 22:38:16 1992
  44. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  45. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  46.   echo shar: Will not clobber existing file \"'Makefile'\"
  47. else
  48. echo shar: Extracting \"'Makefile'\" \(446 characters\)
  49. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  50. X# Makefile by vixie@decwrl, 9-mar-92
  51. X
  52. DESTROOT =
  53. DESTSYS = ${DESTROOT}/usr/localsys
  54. DESTSHR = ${DESTROOT}/usr/localshr
  55. DESTINC = ${DESTSHR}/include
  56. DESTLIB = ${DESTSYS}/lib
  57. X
  58. all: match.h libmatch.a
  59. X
  60. clean:
  61. X    rm -f *.CKP *.BAK *~ *.o
  62. X    rm -f libmatch.a
  63. X
  64. install: all
  65. X    install -c match.h ${DESTINC}/match.h
  66. X    install -c libmatch.a ${DESTLIB}/libmatch.a
  67. X
  68. libmatch.a: match.h match.c
  69. X    ${CC} -c ${CFLAGS} match.c
  70. X    rm -f libmatch.a
  71. X    mv match.o libmatch.a
  72. END_OF_FILE
  73. if test 446 -ne `wc -c <'Makefile'`; then
  74.     echo shar: \"'Makefile'\" unpacked with wrong size!
  75. fi
  76. # end of 'Makefile'
  77. fi
  78. if test -f 'README' -a "${1}" != "-c" ; then 
  79.   echo shar: Will not clobber existing file \"'README'\"
  80. else
  81. echo shar: Extracting \"'README'\" \(2247 characters\)
  82. sed "s/^X//" >'README' <<'END_OF_FILE'
  83. X02-20-91 Seattle, WA
  84. X
  85. X
  86. Here is a *IX wildcard globber I butchered, hacked and cajoled together
  87. after seeing and hearing about and becoming disgusted with several similar
  88. routines which had one or more of the following attributes:  slow, buggy,
  89. required large levels of recursion on matches, required grotesque levels
  90. of recursion on failing matches using '*', full of caveats about usability
  91. or copyrights.
  92. X
  93. I submit this without copyright and with the clear understanding that
  94. this code may be used by anyone, for any reason, with any modifications
  95. and without any guarantees, warrantee or statements of usability of any
  96. sort.
  97. X
  98. Having gotten those cow chips out of the way, these routines are fairly
  99. well tested and reasonably fast.  I have made an effort to fail on all
  100. bad patterns and to quickly determine failing '*' patterns.  This parser
  101. will also do quite a bit of the '*' matching via quick linear loops versus
  102. the standard blind recursive descent.
  103. X
  104. This parser has been submitted to profilers at various stages of development
  105. and has come through quite well.  If the last millisecond is important to
  106. you then some time can be shaved by using stack allocated variables in
  107. place of many of the pointer follows (which may be done fairly often) found
  108. in regex_match and regex_match_after_star (ie *p, *t).
  109. X
  110. No attempt is made to provide general [pat,pat] comparisons.  The specific
  111. subcases supplied by these routines is [pat,text] which is sufficient
  112. for the large majority of cases (should you care).
  113. X
  114. Since regex_match may return one of three different values depending upon
  115. the pattern and text I have made a simple shell for convenience (match()).
  116. Also included is an is_pattern routine to quickly check a potential pattern
  117. for regex special characters.  I even placed this all in a header file for
  118. you lazy folks!
  119. X
  120. Having said all that, here is my own reinvention of the wheel.  Please
  121. enjoy it's use and I hope it is of some help to those with need ....
  122. X
  123. X
  124. X                                jbk
  125. X
  126. XFrom: johnk@wrq.com
  127. Date: 22 Feb 91 01:15:43 GMT
  128. Organization: Walker Richer & Quinn, Inc., Seattle, WA
  129. X
  130. John Kercheval -- 127 NW Bowdion Pl #105 -- Seattle, WA  98107-4960
  131. Home(Voice): (206) 547-4676  --------  Work (Voice): (206) 324-0350
  132. END_OF_FILE
  133. if test 2247 -ne `wc -c <'README'`; then
  134.     echo shar: \"'README'\" unpacked with wrong size!
  135. fi
  136. # end of 'README'
  137. fi
  138. if test -f 'match.c' -a "${1}" != "-c" ; then 
  139.   echo shar: Will not clobber existing file \"'match.c'\"
  140. else
  141. echo shar: Extracting \"'match.c'\" \(9354 characters\)
  142. sed "s/^X//" >'match.c' <<'END_OF_FILE'
  143. X/*
  144. X EPSHeader
  145. X
  146. X   File: match.c
  147. X   Author: J. Kercheval
  148. X   Created: Sat, 01/05/1991  22:21:49
  149. X*/
  150. X/*
  151. X EPSRevision History
  152. X
  153. X   J. Kercheval  Wed, 02/20/1991  22:29:01  Released to Public Domain
  154. X*/
  155. X
  156. X/*
  157. X   Wildcard Pattern Matching
  158. X*/
  159. X
  160. X
  161. X#include "match.h"
  162. X
  163. X#define ABORT 2     /* end of search indicator */
  164. X
  165. BOOLEAN regex_match_after_star (char *pattern, char *text);
  166. X
  167. X/*----------------------------------------------------------------------------
  168. X*
  169. X* Return TRUE if PATTERN has any special wildcard characters
  170. X*
  171. X----------------------------------------------------------------------------*/
  172. X
  173. BOOLEAN is_pattern (char *p)
  174. X{
  175. X    while ( *p ) {
  176. X        switch ( *p++ ) {
  177. X            case '?':
  178. X            case '*':
  179. X            case '[':
  180. X                return TRUE;
  181. X            case '\\':
  182. X                if ( !*p++ ) return FALSE;
  183. X        }
  184. X    }
  185. X    return FALSE;
  186. X}
  187. X
  188. X
  189. X/*----------------------------------------------------------------------------
  190. X*
  191. X*  Match the pattern PATTERN against the string TEXT;
  192. X*  return TRUE if it matches, FALSE otherwise.
  193. X*
  194. X*  A match means the entire string TEXT is used up in matching.
  195. X*
  196. X*  In the pattern string:
  197. X*       `*' matches any sequence of characters
  198. X*       `?' matches any character
  199. X*       [SET] matches any character in the specified set,
  200. X*       [!SET] or [^SET] matches any character not in the specified set.
  201. X*
  202. X*  Note: the standard regex character '+' (one or more) should by
  203. X*        simulated by using "?*" which is equivelant here.
  204. X*
  205. X*  A set is composed of characters or ranges; a range looks like
  206. X*  character hyphen character (as in 0-9 or A-Z).
  207. X*  [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
  208. X*  Any other character in the pattern must be matched exactly.
  209. X*
  210. X*  To suppress the special syntactic significance of any of `[]*?!^-\',
  211. X*  and match the character exactly, precede it with a `\'.
  212. X*
  213. X----------------------------------------------------------------------------*/
  214. X
  215. BOOLEAN regex_match ( register char *p, register char *t )
  216. X{
  217. X    register char range_start, range_end;  /* start and end in range */
  218. X
  219. X    BOOLEAN invert;             /* is this [..] or [!..] */
  220. X    BOOLEAN member_match;       /* have I matched the [..] construct? */
  221. X    BOOLEAN loop;               /* should I terminate? */
  222. X
  223. X    for ( ; *p; p++, t++ ) {
  224. X
  225. X        /* if this is the end of the text then this is the end of the match */
  226. X        if (!*t) {
  227. X            return ( *p == '*' && *++p == '\0' ) ? TRUE : ABORT;
  228. X        }
  229. X
  230. X        /* determine and react to pattern type */
  231. X        switch ( *p ) {
  232. X
  233. X            /* single any character match */
  234. X            case '?':
  235. X                break;
  236. X
  237. X            /* multiple any character match */
  238. X            case '*':
  239. X                return regex_match_after_star (p, t);
  240. X
  241. X            /* [..] construct, single member/exclusion character match */
  242. X            case '[': {
  243. X
  244. X                /* move to beginning of range */
  245. X                p++;
  246. X
  247. X                /* check if this is a member match or exclusion match */
  248. X                invert = FALSE;
  249. X                if ( *p == '!' || *p == '^') {
  250. X                    invert = TRUE;
  251. X                    p++;
  252. X                }
  253. X
  254. X                /* if closing bracket here or at range start then we have a
  255. X                   malformed pattern */
  256. X                if ( *p == ']' ) {
  257. X                    return ABORT;
  258. X                }
  259. X
  260. X                member_match = FALSE;
  261. X                loop = TRUE;
  262. X
  263. X                while ( loop ) {
  264. X
  265. X                    /* if end of construct then loop is done */
  266. X                    if (*p == ']') {
  267. X                        loop = FALSE;
  268. X                        continue;
  269. X                    }
  270. X
  271. X                    /* matching a '!', '^', '-', '\' or a ']' */
  272. X                    if ( *p == '\\' ) {
  273. X                        range_start = range_end = *++p;
  274. X                    }
  275. X                    else {
  276. X                        range_start = range_end = *p;
  277. X                    }
  278. X
  279. X                    /* if end of pattern then bad pattern (Missing ']') */
  280. X                    if (!range_start)
  281. X                        return ABORT;
  282. X
  283. X                    /* move to next pattern char */
  284. X                    p++;
  285. X
  286. X                    /* check for range bar */
  287. X                    if (*p == '-') {
  288. X
  289. X                        /* get the range end */
  290. X                        range_end = *++p;
  291. X
  292. X                        /* special character range end */
  293. X                        if (range_end == '\\')
  294. X                            range_end = *++p;
  295. X
  296. X                        /* if end of pattern or construct then bad pattern */
  297. X                        if (range_end == '\0' || range_end == ']')
  298. X                            return ABORT;
  299. X                    }
  300. X
  301. X                    /* if the text character is in range then match found.
  302. X                       make sure the range letters have the proper
  303. X                       relationship to one another before comparison */
  304. X                    if ( range_start < range_end  ) {
  305. X                        if (*t >= range_start && *t <= range_end) {
  306. X                            member_match = TRUE;
  307. X                            loop = FALSE;
  308. X                        }
  309. X                    }
  310. X                    else {
  311. X                        if (*t >= range_end && *t <= range_start) {
  312. X                            member_match = TRUE;
  313. X                            loop = FALSE;
  314. X                        }
  315. X                    }
  316. X                }
  317. X
  318. X                /* if there was a match in an exclusion set then no match */
  319. X                /* if there was no match in a member set then no match */
  320. X                if ((invert && member_match) ||
  321. X                   !(invert || member_match))
  322. X                    return FALSE;
  323. X
  324. X                /* if this is not an exclusion then skip the rest of the [...]
  325. X                    construct that already matched. */
  326. X                if (member_match) {
  327. X                    while (*p != ']') {
  328. X
  329. X                        /* bad pattern (Missing ']') */
  330. X                        if (!*p)
  331. X                            return ABORT;
  332. X
  333. X                        /* skip exact match */
  334. X                        if (*p == '\\') {
  335. X                            p++;
  336. X                        }
  337. X
  338. X                        /* move to next pattern char */
  339. X                        p++;
  340. X                    }
  341. X                }
  342. X
  343. X                break;
  344. X            }
  345. X
  346. X            /* next character is quoted and must match exactly */
  347. X            case '\\':
  348. X
  349. X                /* move pattern pointer to quoted char and fall through */
  350. X                p++;
  351. X
  352. X            /* must match this character exactly */
  353. X            default:
  354. X                if (*p != *t)
  355. X                    return FALSE;
  356. X        }
  357. X    }
  358. X
  359. X    /* if end of text not reached then the pattern fails */
  360. X    return !*t;
  361. X}
  362. X
  363. X
  364. X/*----------------------------------------------------------------------------
  365. X*
  366. X* recursively call regex_match with final segment of PATTERN and of TEXT.
  367. X*
  368. X----------------------------------------------------------------------------*/
  369. X
  370. BOOLEAN regex_match_after_star (register char *p, register char *t)
  371. X{
  372. X    register BOOLEAN match;
  373. X    register nextp;
  374. X
  375. X    /* pass over existing ? and * in pattern */
  376. X    while ( *p == '?' || *p == '*' ) {
  377. X
  378. X        /* take one char for each ? */
  379. X        if ( *p == '?' ) {
  380. X
  381. X            /* if end of text then no match */
  382. X            if ( !*t++ ) {
  383. X                return ABORT;
  384. X            }
  385. X        }
  386. X
  387. X        /* move to next char in pattern */
  388. X        p++;
  389. X    }
  390. X
  391. X    /* if end of pattern we have matched regardless of text left */
  392. X    if ( !*p ) {
  393. X        return TRUE;
  394. X    }
  395. X
  396. X    /* get the next character to match which must be a literal or '[' */
  397. X    nextp = *p;
  398. X    if ( nextp == '\\' )
  399. X        nextp = p[1];
  400. X
  401. X    /* Continue until we run out of text or definite result seen */
  402. X    match = FALSE;
  403. X    while ( match == FALSE ) {
  404. X
  405. X        /* a precondition for matching is that the next character
  406. X           in the pattern match the next character in the text or that
  407. X           the next pattern is the beginning of a range.  Increment text
  408. X           pointer as we go here */
  409. X        if ( *p == *t || nextp == '[' ) {
  410. X            match = regex_match(p, t);
  411. X        }
  412. X
  413. X        /* if the end of text is reached then no match */
  414. X        if ( !*t++ ) match = ABORT;
  415. X    }
  416. X
  417. X    /* return result */
  418. X    return match;
  419. X}
  420. X
  421. X/*----------------------------------------------------------------------------
  422. X*
  423. X* This is a shell to regex_match to return only a true BOOLEAN value
  424. X*
  425. X----------------------------------------------------------------------------*/
  426. X
  427. BOOLEAN match( char *p, char *t)
  428. X{
  429. X    return ( regex_match(p,t) == TRUE ) ? TRUE : FALSE;
  430. X}
  431. X
  432. X
  433. X#ifdef TEST
  434. X
  435. X    /*
  436. X    * This test main expects as first arg the pattern and as second arg
  437. X    * the match string.  Output is yaeh or nay on match.
  438. X    */
  439. X
  440. X#include <stdio.h>
  441. X
  442. X    int main(int argc, char *argv[])
  443. X    {
  444. X        if (argc == 3) {
  445. X            if (is_pattern(argv[1])) {
  446. X                if (match(argv[1],argv[2])) {
  447. X                    printf("    Match Successful\n");
  448. X                }
  449. X                else {
  450. X                    printf("    Match Fails\n");
  451. X                }
  452. X            }
  453. X            else {
  454. X                printf("    Bad Pattern\n");
  455. X            }
  456. X        }
  457. X        else printf("    Bad Breath\n");
  458. X        return(0);
  459. X    }
  460. X
  461. X#endif
  462. END_OF_FILE
  463. if test 9354 -ne `wc -c <'match.c'`; then
  464.     echo shar: \"'match.c'\" unpacked with wrong size!
  465. fi
  466. # end of 'match.c'
  467. fi
  468. if test -f 'match.h' -a "${1}" != "-c" ; then 
  469.   echo shar: Will not clobber existing file \"'match.h'\"
  470. else
  471. echo shar: Extracting \"'match.h'\" \(1691 characters\)
  472. sed "s/^X//" >'match.h' <<'END_OF_FILE'
  473. X/*
  474. X EPSHeader
  475. X
  476. X   File: match.h
  477. X   Author: J. Kercheval
  478. X   Created: Sat, 01/05/1991  22:27:18
  479. X*/
  480. X/*
  481. X EPSRevision History
  482. X
  483. X   J. Kercheval  Wed, 02/20/1991  22:28:37  Released to Public Domain
  484. X*/
  485. X
  486. X/*
  487. X   Wildcard Pattern Matching
  488. X*/
  489. X
  490. X#ifndef BOOLEAN
  491. X# define BOOLEAN int
  492. X# define TRUE 1
  493. X# define FALSE 0
  494. X#endif
  495. X
  496. X/*----------------------------------------------------------------------------
  497. X*
  498. X*  Match the pattern PATTERN against the string TEXT;
  499. X*  return TRUE if it matches, FALSE otherwise.
  500. X*
  501. X*  A match means the entire string TEXT is used up in matching.
  502. X*
  503. X*  In the pattern string:
  504. X*       `*' matches any sequence of characters
  505. X*       `?' matches any character
  506. X*       [SET] matches any character in the specified set,
  507. X*       [!SET] or [^SET] matches any character not in the specified set.
  508. X*
  509. X*  Note: the standard regex character '+' (one or more) should by
  510. X*        simulated by using "?*" which is equivelant here.
  511. X*
  512. X*  A set is composed of characters or ranges; a range looks like
  513. X*  character hyphen character (as in 0-9 or A-Z).
  514. X*  [0-9a-zA-Z_] is the set of characters allowed in C identifiers.
  515. X*  Any other character in the pattern must be matched exactly.
  516. X*
  517. X*  To suppress the special syntactic significance of any of `[]*?!^-\',
  518. X*  and match the character exactly, precede it with a `\'.
  519. X*
  520. X----------------------------------------------------------------------------*/
  521. X
  522. BOOLEAN match (char *pattern, char *text);
  523. X
  524. X/*----------------------------------------------------------------------------
  525. X*
  526. X* Return TRUE if PATTERN has any special wildcard characters
  527. X*
  528. X----------------------------------------------------------------------------*/
  529. X
  530. BOOLEAN is_pattern (char *pattern);
  531. END_OF_FILE
  532. if test 1691 -ne `wc -c <'match.h'`; then
  533.     echo shar: \"'match.h'\" unpacked with wrong size!
  534. fi
  535. # end of 'match.h'
  536. fi
  537. echo shar: End of shell archive.
  538. exit 0
  539.  
  540. exit 0 # Just in case...
  541.