regexp.c revision 293290
1103285Sikob/* 2103285Sikob * Copyright (c) 1980, 1993 3103285Sikob * The Regents of the University of California. All rights reserved. 4103285Sikob * 5103285Sikob * 6103285Sikob * Redistribution and use in source and binary forms, with or without 7103285Sikob * modification, are permitted provided that the following conditions 8103285Sikob * are met: 9103285Sikob * 1. Redistributions of source code must retain the above copyright 10103285Sikob * notice, this list of conditions and the following disclaimer. 11103285Sikob * 2. Redistributions in binary form must reproduce the above copyright 12103285Sikob * notice, this list of conditions and the following disclaimer in the 13103285Sikob * documentation and/or other materials provided with the distribution. 14103285Sikob * 4. Neither the name of the University nor the names of its contributors 15103285Sikob * may be used to endorse or promote products derived from this software 16103285Sikob * without specific prior written permission. 17103285Sikob * 18103285Sikob * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20103285Sikob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21103285Sikob * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22103285Sikob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24103285Sikob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25103285Sikob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28103285Sikob * SUCH DAMAGE. 29103285Sikob */ 30103285Sikob 31103285Sikob#include <sys/cdefs.h> 32103285Sikob 33103285Sikob__FBSDID("$FreeBSD: stable/10/usr.bin/vgrind/regexp.c 293290 2016-01-07 00:40:51Z bdrewery $"); 34103285Sikob 35103285Sikob#ifndef lint 36103285Sikobstatic const char copyright[] = 37103285Sikob"@(#) Copyright (c) 1980, 1993\n\ 38103285Sikob The Regents of the University of California. All rights reserved.\n"; 39103285Sikob#endif 40103285Sikob 41103285Sikob#ifndef lint 42103285Sikobstatic const char sccsid[] = "@(#)regexp.c 8.1 (Berkeley) 6/6/93"; 43103285Sikob#endif 44103285Sikob 45103285Sikob#include <ctype.h> 46103285Sikob#include <stdlib.h> 47103285Sikob#include <stdbool.h> 48103285Sikob#include <string.h> 49103285Sikob 50103285Sikob#include "extern.h" 51103285Sikob 52103285Sikobstatic void expconv(void); 53103285Sikob 54103285Sikobbool _escaped; /* true if we are currently x_escaped */ 55103285Sikobchar *s_start; /* start of string */ 56103285Sikobbool l_onecase; /* true if upper and lower equivalent */ 57103285Sikob 58103285Sikob#define makelower(c) (isupper((c)) ? tolower((c)) : (c)) 59103285Sikob 60103285Sikob/* STRNCMP - like strncmp except that we convert the 61108281Ssimokawa * first string to lower case before comparing 62103285Sikob * if l_onecase is set. 63108281Ssimokawa */ 64103285Sikob 65103285Sikobint 66103285SikobSTRNCMP(register char *s1, register char *s2, register int len) 67103285Sikob{ 68103285Sikob if (l_onecase) { 69103285Sikob do 70103285Sikob if (*s2 - makelower(*s1)) 71103285Sikob return (*s2 - makelower(*s1)); 72103285Sikob else { 73103285Sikob s2++; 74103285Sikob s1++; 75103285Sikob } 76103285Sikob while (--len); 77103285Sikob } else { 78103285Sikob do 79103285Sikob if (*s2 - *s1) 80103285Sikob return (*s2 - *s1); 81103285Sikob else { 82103285Sikob s2++; 83103285Sikob s1++; 84103285Sikob } 85103285Sikob while (--len); 86103285Sikob } 87106543Ssimokawa return(0); 88103285Sikob} 89106543Ssimokawa 90103285Sikob/* The following routine converts an irregular expression to 91105620Ssimokawa * internal format. 92103285Sikob * 93103285Sikob * Either meta symbols (\a \d or \p) or character strings or 94103285Sikob * operations ( alternation or perenthesizing ) can be 95103285Sikob * specified. Each starts with a descriptor byte. The descriptor 96103285Sikob * byte has STR set for strings, META set for meta symbols 97103285Sikob * and OPER set for operations. 98108642Ssimokawa * The descriptor byte can also have the OPT bit set if the object 99108642Ssimokawa * defined is optional. Also ALT can be set to indicate an alternation. 100103285Sikob * 101103285Sikob * For metasymbols the byte following the descriptor byte identities 102103285Sikob * the meta symbol (containing an ascii 'a', 'd', 'p', '|', or '('). For 103103285Sikob * strings the byte after the descriptor is a character count for 104103285Sikob * the string: 105103285Sikob * 106103285Sikob * meta symbols := descriptor 107103285Sikob * symbol 108103285Sikob * 109103285Sikob * strings := descriptor 110103285Sikob * character count 111103285Sikob * the string 112103285Sikob * 113106813Ssimokawa * operatins := descriptor 114106813Ssimokawa * symbol 115106813Ssimokawa * character count 116103285Sikob */ 117103285Sikob 118103285Sikob/* 119103285Sikob * handy macros for accessing parts of match blocks 120103285Sikob */ 121103285Sikob#define MSYM(A) (*(A+1)) /* symbol in a meta symbol block */ 122103285Sikob#define MNEXT(A) (A+2) /* character following a metasymbol block */ 123103285Sikob 124103285Sikob#define OSYM(A) (*(A+1)) /* symbol in an operation block */ 125103285Sikob#define OCNT(A) (*(A+2)) /* character count */ 126103285Sikob#define ONEXT(A) (A+3) /* next character after the operation */ 127103285Sikob#define OPTR(A) (A+*(A+2)) /* place pointed to by the operator */ 128103285Sikob 129103285Sikob#define SCNT(A) (*(A+1)) /* byte count of a string */ 130103285Sikob#define SSTR(A) (A+2) /* address of the string */ 131103285Sikob#define SNEXT(A) (A+2+*(A+1)) /* character following the string */ 132109179Ssimokawa 133103285Sikob/* 134103285Sikob * bit flags in the descriptor 135103285Sikob */ 136103285Sikob#define OPT 1 137103285Sikob#define STR 2 138103285Sikob#define META 4 139103285Sikob#define ALT 8 140103285Sikob#define OPER 16 141103285Sikob 142103285Sikobstatic char *ccre; /* pointer to current position in converted exp*/ 143103285Sikobstatic char *ure; /* pointer current position in unconverted exp */ 144103285Sikob 145103285Sikob/* re: unconverted irregular expression */ 146103285Sikobchar * 147103285Sikobconvexp(char *re) 148103285Sikob{ 149103285Sikob register char *cre; /* pointer to converted regular expression */ 150103285Sikob 151103285Sikob /* allocate room for the converted expression */ 152103285Sikob if (re == NULL) 153103285Sikob return (NULL); 154103285Sikob if (*re == '\0') 155103285Sikob return (NULL); 156103285Sikob cre = malloc(4 * strlen(re) + 3); 157103285Sikob ccre = cre; 158103285Sikob ure = re; 159103285Sikob 160103285Sikob /* start the conversion with a \a */ 161103285Sikob *cre = META | OPT; 162103285Sikob MSYM(cre) = 'a'; 163103285Sikob ccre = MNEXT(cre); 164103285Sikob 165103285Sikob /* start the conversion (its recursive) */ 166103285Sikob expconv (); 167103285Sikob *ccre = 0; 168103285Sikob return (cre); 169103285Sikob} 170103285Sikob 171103285Sikobstatic void 172103285Sikobexpconv() 173103285Sikob{ 174103285Sikob register char *cs; /* pointer to current symbol in converted exp */ 175103285Sikob register char c; /* character being processed */ 176103285Sikob register char *acs; /* pinter to last alternate */ 177103285Sikob register int temp; 178103285Sikob 179109227Ssimokawa /* let the conversion begin */ 180103285Sikob acs = NULL; 181103285Sikob cs = NULL; 182103285Sikob while (*ure) { 183103285Sikob switch (c = *ure++) { 184103285Sikob 185109227Ssimokawa case '\\': 186103285Sikob switch (c = *ure++) { 187109282Ssimokawa 188109282Ssimokawa /* escaped characters are just characters */ 189109282Ssimokawa default: 190109282Ssimokawa if (cs == NULL || (*cs & STR) == 0) { 191109282Ssimokawa cs = ccre; 192109179Ssimokawa *cs = STR; 193109179Ssimokawa SCNT(cs) = 1; 194109179Ssimokawa ccre += 2; 195109179Ssimokawa } else 196109179Ssimokawa SCNT(cs)++; 197109179Ssimokawa *ccre++ = c; 198109179Ssimokawa break; 199109179Ssimokawa 200109179Ssimokawa /* normal(?) metacharacters */ 201109179Ssimokawa case 'a': 202109179Ssimokawa case 'd': 203109179Ssimokawa case 'e': 204109179Ssimokawa case 'p': 205109179Ssimokawa if (acs != NULL && acs != cs) { 206109179Ssimokawa do { 207109179Ssimokawa temp = OCNT(acs); 208103285Sikob OCNT(acs) = ccre - acs; 209103285Sikob acs -= temp; 210103285Sikob } while (temp != 0); 211103285Sikob acs = NULL; 212103285Sikob } 213103285Sikob cs = ccre; 214103285Sikob *cs = META; 215103285Sikob MSYM(cs) = c; 216103285Sikob ccre = MNEXT(cs); 217103285Sikob break; 218103285Sikob } 219103285Sikob break; 220103285Sikob 221103285Sikob /* just put the symbol in */ 222103285Sikob case '^': 223103285Sikob case '$': 224103285Sikob if (acs != NULL && acs != cs) { 225103285Sikob do { 226103285Sikob temp = OCNT(acs); 227103285Sikob OCNT(acs) = ccre - acs; 228103285Sikob acs -= temp; 229103285Sikob } while (temp != 0); 230103285Sikob acs = NULL; 231103285Sikob } 232103285Sikob cs = ccre; 233103285Sikob *cs = META; 234103285Sikob MSYM(cs) = c; 235103285Sikob ccre = MNEXT(cs); 236103285Sikob break; 237103285Sikob 238103285Sikob /* mark the last match sequence as optional */ 239103285Sikob case '?': 240103285Sikob if (cs) 241103285Sikob *cs = *cs | OPT; 242103285Sikob break; 243103285Sikob 244103285Sikob /* recurse and define a subexpression */ 245109227Ssimokawa case '(': 246109227Ssimokawa if (acs != NULL && acs != cs) { 247103285Sikob do { 248103285Sikob temp = OCNT(acs); 249103285Sikob OCNT(acs) = ccre - acs; 250103285Sikob acs -= temp; 251103285Sikob } while (temp != 0); 252103285Sikob acs = NULL; 253103285Sikob } 254103285Sikob cs = ccre; 255103285Sikob *cs = OPER; 256103285Sikob OSYM(cs) = '('; 257103285Sikob ccre = ONEXT(cs); 258103285Sikob expconv(); 259103285Sikob OCNT(cs) = ccre - cs; /* offset to next symbol */ 260103285Sikob break; 261103285Sikob 262103285Sikob /* return from a recursion */ 263103285Sikob case ')': 264103285Sikob if (acs != NULL) { 265103285Sikob do { 266103285Sikob temp = OCNT(acs); 267103285Sikob OCNT(acs) = ccre - acs; 268103285Sikob acs -= temp; 269103285Sikob } while (temp != 0); 270103285Sikob acs = NULL; 271103285Sikob } 272103285Sikob cs = ccre; 273103285Sikob *cs = META; 274103285Sikob MSYM(cs) = c; 275103285Sikob ccre = MNEXT(cs); 276103285Sikob return; 277103285Sikob 278103285Sikob /* mark the last match sequence as having an alternate */ 279103285Sikob /* the third byte will contain an offset to jump over the */ 280103285Sikob /* alternate match in case the first did not fail */ 281103285Sikob case '|': 282103285Sikob if (acs != NULL && acs != cs) 283108995Ssimokawa OCNT(ccre) = ccre - acs; /* make a back pointer */ 284103285Sikob else 285103285Sikob OCNT(ccre) = 0; 286103285Sikob *cs |= ALT; 287103285Sikob cs = ccre; 288103285Sikob *cs = OPER; 289103285Sikob OSYM(cs) = '|'; 290103285Sikob ccre = ONEXT(cs); 291103285Sikob acs = cs; /* remember that the pointer is to be filles */ 292103285Sikob break; 293103285Sikob 294103285Sikob /* if its not a metasymbol just build a scharacter string */ 295103285Sikob default: 296103285Sikob if (cs == NULL || (*cs & STR) == 0) { 297106810Ssimokawa cs = ccre; 298106790Ssimokawa *cs = STR; 299103285Sikob SCNT(cs) = 1; 300103285Sikob ccre = SSTR(cs); 301103285Sikob } else 302103285Sikob SCNT(cs)++; 303103285Sikob *ccre++ = c; 304103285Sikob break; 305103285Sikob } 306103285Sikob } 307106810Ssimokawa if (acs != NULL) { 308106810Ssimokawa do { 309106810Ssimokawa temp = OCNT(acs); 310103285Sikob OCNT(acs) = ccre - acs; 311106813Ssimokawa acs -= temp; 312103285Sikob } while (temp != 0); 313103285Sikob acs = NULL; 314103285Sikob } 315103285Sikob return; 316103285Sikob} 317103285Sikob/* end of convertre */ 318103285Sikob 319103285Sikob 320103285Sikob/* 321103285Sikob * The following routine recognises an irregular expression 322103285Sikob * with the following special characters: 323103285Sikob * 324103285Sikob * \? - means last match was optional 325103285Sikob * \a - matches any number of characters 326108701Ssimokawa * \d - matches any number of spaces and tabs 327103285Sikob * \p - matches any number of alphanumeric 328103285Sikob * characters. The 329103285Sikob * characters matched will be copied into 330103285Sikob * the area pointed to by 'name'. 331103285Sikob * \| - alternation 332103285Sikob * \( \) - grouping used mostly for alternation and 333103285Sikob * optionality 334103285Sikob * 335103285Sikob * The irregular expression must be translated to internal form 336103285Sikob * prior to calling this routine 337103285Sikob * 338103285Sikob * The value returned is the pointer to the first non \a 339103285Sikob * character matched. 340103285Sikob */ 341103285Sikob 342103285Sikob/* 343103285Sikob * s: string to check for a match in 344103285Sikob * re: a converted irregular expression 345103285Sikob * mstring: where to put whatever matches a \p 346103285Sikob */ 347103285Sikobchar * 348103285Sikobexpmatch (register char *s, register char *re, register char *mstring) 349103285Sikob{ 350103285Sikob register char *cs; /* the current symbol */ 351103285Sikob register char *ptr,*s1; /* temporary pointer */ 352103285Sikob bool matched; /* a temporary bool */ 353103285Sikob 354103285Sikob /* initial conditions */ 355103285Sikob if (re == NULL) 356103285Sikob return (NULL); 357106790Ssimokawa cs = re; 358103285Sikob matched = false; 359103285Sikob 360103285Sikob /* loop till expression string is exhausted (or at least pretty tired) */ 361103285Sikob while (*cs) { 362103285Sikob switch (*cs & (OPER | STR | META)) { 363108655Ssimokawa 364108655Ssimokawa /* try to match a string */ 365103285Sikob case STR: 366103285Sikob matched = !STRNCMP (s, SSTR(cs), SCNT(cs)); 367103285Sikob if (matched) { 368103285Sikob 369103285Sikob /* hoorah it matches */ 370103285Sikob s += SCNT(cs); 371103285Sikob cs = SNEXT(cs); 372103285Sikob } else if (*cs & ALT) { 373103285Sikob 374103285Sikob /* alternation, skip to next expression */ 375103285Sikob cs = SNEXT(cs); 376103285Sikob } else if (*cs & OPT) { 377103285Sikob 378103285Sikob /* the match is optional */ 379103285Sikob cs = SNEXT(cs); 380103285Sikob matched = 1; /* indicate a successful match */ 381103285Sikob } else { 382103285Sikob 383103285Sikob /* no match, error return */ 384103285Sikob return (NULL); 385103285Sikob } 386103285Sikob break; 387103285Sikob 388103285Sikob /* an operator, do something fancy */ 389103285Sikob case OPER: 390103285Sikob switch (OSYM(cs)) { 391103285Sikob 392103285Sikob /* this is an alternation */ 393103285Sikob case '|': 394103285Sikob if (matched) 395103285Sikob 396103285Sikob /* last thing in the alternation was a match, skip ahead */ 397103285Sikob cs = OPTR(cs); 398106815Ssimokawa else 399103285Sikob 400103285Sikob /* no match, keep trying */ 401103285Sikob cs = ONEXT(cs); 402103285Sikob break; 403103285Sikob 404103285Sikob /* this is a grouping, recurse */ 405103285Sikob case '(': 406103285Sikob ptr = expmatch(s, ONEXT(cs), mstring); 407103285Sikob if (ptr != NULL) { 408103285Sikob 409103285Sikob /* the subexpression matched */ 410103285Sikob matched = 1; 411103285Sikob s = ptr; 412103285Sikob } else if (*cs & ALT) { 413103285Sikob 414103285Sikob /* alternation, skip to next expression */ 415103285Sikob matched = 0; 416103285Sikob } else if (*cs & OPT) { 417103285Sikob 418103285Sikob /* the match is optional */ 419103285Sikob matched = 1; /* indicate a successful match */ 420103285Sikob } else { 421103285Sikob 422103285Sikob /* no match, error return */ 423103285Sikob return (NULL); 424103285Sikob } 425103285Sikob cs = OPTR(cs); 426103285Sikob break; 427103285Sikob } 428103285Sikob break; 429103285Sikob 430103285Sikob /* try to match a metasymbol */ 431103285Sikob case META: 432106790Ssimokawa switch (MSYM(cs)) { 433106790Ssimokawa 434106790Ssimokawa /* try to match anything and remember what was matched */ 435103285Sikob case 'p': 436103285Sikob /* 437103285Sikob * This is really the same as trying the match the 438103285Sikob * remaining parts of the expression to any subset 439103285Sikob * of the string. 440103285Sikob */ 441103285Sikob s1 = s; 442103285Sikob do { 443103285Sikob ptr = expmatch(s1, MNEXT(cs), mstring); 444103285Sikob if (ptr != NULL && s1 != s) { 445103285Sikob 446103285Sikob /* we have a match, remember the match */ 447103285Sikob strncpy (mstring, s, s1 - s); 448103285Sikob mstring[s1 - s] = '\0'; 449103285Sikob return (ptr); 450103285Sikob } else if (ptr != NULL && (*cs & OPT)) { 451103285Sikob 452103285Sikob /* it was aoptional so no match is ok */ 453103285Sikob return (ptr); 454103285Sikob } else if (ptr != NULL) { 455103285Sikob 456103285Sikob /* not optional and we still matched */ 457103285Sikob return (NULL); 458103285Sikob } 459103285Sikob if (!(isalnum(*s1) || *s1 == '_' || 460103285Sikob /* C++ destructor */ 461103285Sikob *s1 == '~' || 462103285Sikob /* C++ scope operator */ 463103285Sikob (strlen(s1) > 1 && *s1 == ':' && s1[1] == ':' && 464103285Sikob (s1++, true)))) 465103285Sikob return (NULL); 466106790Ssimokawa if (*s1 == '\\') 467103285Sikob _escaped = _escaped ? false : true; 468103285Sikob else 469103285Sikob _escaped = false; 470108281Ssimokawa } while (*s1++); 471103285Sikob return (NULL); 472103285Sikob 473106790Ssimokawa /* try to match anything */ 474103285Sikob case 'a': 475103285Sikob /* 476103285Sikob * This is really the same as trying the match the 477103285Sikob * remaining parts of the expression to any subset 478103285Sikob * of the string. 479103285Sikob */ 480103285Sikob s1 = s; 481103285Sikob do { 482103285Sikob ptr = expmatch(s1, MNEXT(cs), mstring); 483103285Sikob if (ptr != NULL && s1 != s) { 484103285Sikob 485103285Sikob /* we have a match */ 486103285Sikob return (ptr); 487103285Sikob } else if (ptr != NULL && (*cs & OPT)) { 488103285Sikob 489103285Sikob /* it was aoptional so no match is ok */ 490103285Sikob return (ptr); 491103285Sikob } else if (ptr != NULL) { 492103285Sikob 493103285Sikob /* not optional and we still matched */ 494103285Sikob return (NULL); 495103285Sikob } 496108276Ssimokawa if (*s1 == '\\') 497103285Sikob _escaped = _escaped ? false : true; 498103285Sikob else 499103285Sikob _escaped = false; 500103285Sikob } while (*s1++); 501103285Sikob return (NULL); 502103285Sikob 503103285Sikob /* fail if we are currently _escaped */ 504103285Sikob case 'e': 505103285Sikob if (_escaped) 506103285Sikob return(NULL); 507103285Sikob cs = MNEXT(cs); 508108276Ssimokawa break; 509103285Sikob 510103285Sikob /* match any number of tabs and spaces */ 511103285Sikob case 'd': 512103285Sikob ptr = s; 513103285Sikob while (*s == ' ' || *s == '\t') 514103285Sikob s++; 515103285Sikob if (s != ptr || s == s_start) { 516103285Sikob 517108853Ssimokawa /* match, be happy */ 518108853Ssimokawa matched = 1; 519108853Ssimokawa cs = MNEXT(cs); 520108853Ssimokawa } else if (*s == '\n' || *s == '\0') { 521108853Ssimokawa 522108853Ssimokawa /* match, be happy */ 523108853Ssimokawa matched = 1; 524103285Sikob cs = MNEXT(cs); 525103285Sikob } else if (*cs & ALT) { 526103285Sikob 527103285Sikob /* try the next part */ 528103285Sikob matched = 0; 529103285Sikob cs = MNEXT(cs); 530103285Sikob } else if (*cs & OPT) { 531103285Sikob 532103285Sikob /* doesn't matter */ 533103285Sikob matched = 1; 534103285Sikob cs = MNEXT(cs); 535103285Sikob } else 536103285Sikob 537103285Sikob /* no match, error return */ 538103285Sikob return (NULL); 539103285Sikob break; 540103285Sikob 541103285Sikob /* check for end of line */ 542103285Sikob case '$': 543103285Sikob if (*s == '\0' || *s == '\n') { 544103285Sikob 545103285Sikob /* match, be happy */ 546103285Sikob s++; 547103285Sikob matched = 1; 548103285Sikob cs = MNEXT(cs); 549103285Sikob } else if (*cs & ALT) { 550103285Sikob 551103285Sikob /* try the next part */ 552103285Sikob matched = 0; 553103285Sikob cs = MNEXT(cs); 554106790Ssimokawa } else if (*cs & OPT) { 555103285Sikob 556103285Sikob /* doesn't matter */ 557103285Sikob matched = 1; 558103285Sikob cs = MNEXT(cs); 559103285Sikob } else 560103285Sikob 561103285Sikob /* no match, error return */ 562103285Sikob return (NULL); 563103285Sikob break; 564106790Ssimokawa 565103285Sikob /* check for start of line */ 566103285Sikob case '^': 567103285Sikob if (s == s_start) { 568103285Sikob 569103285Sikob /* match, be happy */ 570103285Sikob matched = 1; 571103285Sikob cs = MNEXT(cs); 572103285Sikob } else if (*cs & ALT) { 573103285Sikob 574103285Sikob /* try the next part */ 575103285Sikob matched = 0; 576103285Sikob cs = MNEXT(cs); 577103285Sikob } else if (*cs & OPT) { 578103285Sikob 579103285Sikob /* doesn't matter */ 580103285Sikob matched = 1; 581103285Sikob cs = MNEXT(cs); 582103285Sikob } else 583103285Sikob 584103285Sikob /* no match, error return */ 585103285Sikob return (NULL); 586103285Sikob break; 587103285Sikob 588106790Ssimokawa /* end of a subexpression, return success */ 589103285Sikob case ')': 590106790Ssimokawa return (s); 591103285Sikob } 592106790Ssimokawa break; 593106790Ssimokawa } 594103285Sikob } 595103285Sikob return (s); 596103285Sikob} 597103285Sikob