PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/amanda/tags/3_1_0_mac01/common-src/match.c

#
C | 822 lines | 623 code | 87 blank | 112 comment | 286 complexity | f54a4938902d6baf6b67c1648b24e1ac MD5 | raw file
  1. /*
  2. * Amanda, The Advanced Maryland Automatic Network Disk Archiver
  3. * Copyright (c) 1991-1998 University of Maryland at College Park
  4. * All Rights Reserved.
  5. *
  6. * Permission to use, copy, modify, distribute, and sell this software and its
  7. * documentation for any purpose is hereby granted without fee, provided that
  8. * the above copyright notice appear in all copies and that both that
  9. * copyright notice and this permission notice appear in supporting
  10. * documentation, and that the name of U.M. not be used in advertising or
  11. * publicity pertaining to distribution of the software without specific,
  12. * written prior permission. U.M. makes no representations about the
  13. * suitability of this software for any purpose. It is provided "as is"
  14. * without express or implied warranty.
  15. *
  16. * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
  18. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  20. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  21. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22. *
  23. * Authors: the Amanda Development Team. Its members are listed in a
  24. * file named AUTHORS, in the root directory of this distribution.
  25. */
  26. /*
  27. * $Id: match.c,v 1.23 2006/05/25 01:47:12 johnfranks Exp $
  28. *
  29. * functions for checking and matching regular expressions
  30. */
  31. #include "amanda.h"
  32. #include "match.h"
  33. #include <regex.h>
  34. static int match_word(const char *glob, const char *word, const char separator);
  35. static char *tar_to_regex(const char *glob);
  36. char *
  37. validate_regexp(
  38. const char * regex)
  39. {
  40. regex_t regc;
  41. int result;
  42. static char errmsg[STR_SIZE];
  43. if ((result = regcomp(&regc, regex,
  44. REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
  45. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  46. return errmsg;
  47. }
  48. regfree(&regc);
  49. return NULL;
  50. }
  51. char *
  52. clean_regex(
  53. const char * str,
  54. gboolean anchor)
  55. {
  56. char *result;
  57. int j;
  58. size_t i;
  59. result = alloc(2*strlen(str)+3);
  60. j = 0;
  61. if (anchor)
  62. result[j++] = '^';
  63. for(i=0;i<strlen(str);i++) {
  64. if(!isalnum((int)str[i]))
  65. result[j++]='\\';
  66. result[j++]=str[i];
  67. }
  68. if (anchor)
  69. result[j++] = '$';
  70. result[j] = '\0';
  71. return result;
  72. }
  73. char *
  74. make_exact_host_expression(
  75. const char * host)
  76. {
  77. char *result;
  78. int j;
  79. size_t i;
  80. result = alloc(2*strlen(host)+3);
  81. j = 0;
  82. result[j++] = '^';
  83. for(i=0;i<strlen(host);i++) {
  84. /* quote host expression metcharacters *except* '.'. Note that
  85. * most of these are invalid in a DNS hostname anyway. */
  86. switch (host[i]) {
  87. case '\\':
  88. case '/':
  89. case '^':
  90. case '$':
  91. case '?':
  92. case '*':
  93. case '[':
  94. case ']':
  95. result[j++]='\\';
  96. /* fall through */
  97. default:
  98. result[j++]=host[i];
  99. }
  100. }
  101. result[j++] = '$';
  102. result[j] = '\0';
  103. return result;
  104. }
  105. char *
  106. make_exact_disk_expression(
  107. const char * disk)
  108. {
  109. char *result;
  110. int j;
  111. size_t i;
  112. result = alloc(2*strlen(disk)+3);
  113. j = 0;
  114. result[j++] = '^';
  115. for(i=0;i<strlen(disk);i++) {
  116. /* quote disk expression metcharacters *except* '/' */
  117. switch (disk[i]) {
  118. case '\\':
  119. case '.':
  120. case '^':
  121. case '$':
  122. case '?':
  123. case '*':
  124. case '[':
  125. case ']':
  126. result[j++]='\\';
  127. /* fall through */
  128. default:
  129. result[j++]=disk[i];
  130. }
  131. }
  132. result[j++] = '$';
  133. result[j] = '\0';
  134. return result;
  135. }
  136. int
  137. match(
  138. const char * regex,
  139. const char * str)
  140. {
  141. regex_t regc;
  142. int result;
  143. char errmsg[STR_SIZE];
  144. if((result = regcomp(&regc, regex,
  145. REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
  146. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  147. error(_("regex \"%s\": %s"), regex, errmsg);
  148. /*NOTREACHED*/
  149. }
  150. if((result = regexec(&regc, str, 0, 0, 0)) != 0
  151. && result != REG_NOMATCH) {
  152. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  153. error(_("regex \"%s\": %s"), regex, errmsg);
  154. /*NOTREACHED*/
  155. }
  156. regfree(&regc);
  157. return result == 0;
  158. }
  159. int
  160. match_no_newline(
  161. const char * regex,
  162. const char * str)
  163. {
  164. regex_t regc;
  165. int result;
  166. char errmsg[STR_SIZE];
  167. if((result = regcomp(&regc, regex,
  168. REG_EXTENDED|REG_NOSUB)) != 0) {
  169. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  170. error(_("regex \"%s\": %s"), regex, errmsg);
  171. /*NOTREACHED*/
  172. }
  173. if((result = regexec(&regc, str, 0, 0, 0)) != 0
  174. && result != REG_NOMATCH) {
  175. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  176. error(_("regex \"%s\": %s"), regex, errmsg);
  177. /*NOTREACHED*/
  178. }
  179. regfree(&regc);
  180. return result == 0;
  181. }
  182. char *
  183. validate_glob(
  184. const char * glob)
  185. {
  186. char *regex;
  187. regex_t regc;
  188. int result;
  189. static char errmsg[STR_SIZE];
  190. regex = glob_to_regex(glob);
  191. if ((result = regcomp(&regc, regex,
  192. REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
  193. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  194. amfree(regex);
  195. return errmsg;
  196. }
  197. regfree(&regc);
  198. amfree(regex);
  199. return NULL;
  200. }
  201. int
  202. match_glob(
  203. const char * glob,
  204. const char * str)
  205. {
  206. char *regex;
  207. regex_t regc;
  208. int result;
  209. char errmsg[STR_SIZE];
  210. regex = glob_to_regex(glob);
  211. if((result = regcomp(&regc, regex,
  212. REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
  213. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  214. error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
  215. /*NOTREACHED*/
  216. }
  217. if((result = regexec(&regc, str, 0, 0, 0)) != 0
  218. && result != REG_NOMATCH) {
  219. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  220. error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
  221. /*NOTREACHED*/
  222. }
  223. regfree(&regc);
  224. amfree(regex);
  225. return result == 0;
  226. }
  227. char *
  228. glob_to_regex(
  229. const char * glob)
  230. {
  231. char *regex;
  232. char *r;
  233. size_t len;
  234. int ch;
  235. int last_ch;
  236. /*
  237. * Allocate an area to convert into. The worst case is a five to
  238. * one expansion.
  239. */
  240. len = strlen(glob);
  241. regex = alloc(1 + len * 5 + 1 + 1);
  242. /*
  243. * Do the conversion:
  244. *
  245. * ? -> [^/]
  246. * * -> [^/]*
  247. * [!...] -> [^...]
  248. *
  249. * The following are given a leading backslash to protect them
  250. * unless they already have a backslash:
  251. *
  252. * ( ) { } + . ^ $ |
  253. *
  254. * Put a leading ^ and trailing $ around the result. If the last
  255. * non-escaped character is \ leave the $ off to cause a syntax
  256. * error when the regex is compiled.
  257. */
  258. r = regex;
  259. *r++ = '^';
  260. last_ch = '\0';
  261. for (ch = *glob++; ch != '\0'; last_ch = ch, ch = *glob++) {
  262. if (last_ch == '\\') {
  263. *r++ = (char)ch;
  264. ch = '\0'; /* so last_ch != '\\' next time */
  265. } else if (last_ch == '[' && ch == '!') {
  266. *r++ = '^';
  267. } else if (ch == '\\') {
  268. *r++ = (char)ch;
  269. } else if (ch == '*' || ch == '?') {
  270. *r++ = '[';
  271. *r++ = '^';
  272. *r++ = '/';
  273. *r++ = ']';
  274. if (ch == '*') {
  275. *r++ = '*';
  276. }
  277. } else if (ch == '('
  278. || ch == ')'
  279. || ch == '{'
  280. || ch == '}'
  281. || ch == '+'
  282. || ch == '.'
  283. || ch == '^'
  284. || ch == '$'
  285. || ch == '|') {
  286. *r++ = '\\';
  287. *r++ = (char)ch;
  288. } else {
  289. *r++ = (char)ch;
  290. }
  291. }
  292. if (last_ch != '\\') {
  293. *r++ = '$';
  294. }
  295. *r = '\0';
  296. return regex;
  297. }
  298. int
  299. match_tar(
  300. const char * glob,
  301. const char * str)
  302. {
  303. char *regex;
  304. regex_t regc;
  305. int result;
  306. char errmsg[STR_SIZE];
  307. regex = tar_to_regex(glob);
  308. if((result = regcomp(&regc, regex,
  309. REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
  310. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  311. error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
  312. /*NOTREACHED*/
  313. }
  314. if((result = regexec(&regc, str, 0, 0, 0)) != 0
  315. && result != REG_NOMATCH) {
  316. regerror(result, &regc, errmsg, SIZEOF(errmsg));
  317. error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
  318. /*NOTREACHED*/
  319. }
  320. regfree(&regc);
  321. amfree(regex);
  322. return result == 0;
  323. }
  324. static char *
  325. tar_to_regex(
  326. const char * glob)
  327. {
  328. char *regex;
  329. char *r;
  330. size_t len;
  331. int ch;
  332. int last_ch;
  333. /*
  334. * Allocate an area to convert into. The worst case is a five to
  335. * one expansion.
  336. */
  337. len = strlen(glob);
  338. regex = alloc(1 + len * 5 + 1 + 1);
  339. /*
  340. * Do the conversion:
  341. *
  342. * ? -> [^/]
  343. * * -> .*
  344. * [!...] -> [^...]
  345. *
  346. * The following are given a leading backslash to protect them
  347. * unless they already have a backslash:
  348. *
  349. * ( ) { } + . ^ $ |
  350. *
  351. * Put a leading ^ and trailing $ around the result. If the last
  352. * non-escaped character is \ leave the $ off to cause a syntax
  353. * error when the regex is compiled.
  354. */
  355. r = regex;
  356. *r++ = '^';
  357. last_ch = '\0';
  358. for (ch = *glob++; ch != '\0'; last_ch = ch, ch = *glob++) {
  359. if (last_ch == '\\') {
  360. *r++ = (char)ch;
  361. ch = '\0'; /* so last_ch != '\\' next time */
  362. } else if (last_ch == '[' && ch == '!') {
  363. *r++ = '^';
  364. } else if (ch == '\\') {
  365. *r++ = (char)ch;
  366. } else if (ch == '*') {
  367. *r++ = '.';
  368. *r++ = '*';
  369. } else if (ch == '?') {
  370. *r++ = '[';
  371. *r++ = '^';
  372. *r++ = '/';
  373. *r++ = ']';
  374. } else if (ch == '('
  375. || ch == ')'
  376. || ch == '{'
  377. || ch == '}'
  378. || ch == '+'
  379. || ch == '.'
  380. || ch == '^'
  381. || ch == '$'
  382. || ch == '|') {
  383. *r++ = '\\';
  384. *r++ = (char)ch;
  385. } else {
  386. *r++ = (char)ch;
  387. }
  388. }
  389. if (last_ch != '\\') {
  390. *r++ = '$';
  391. }
  392. *r = '\0';
  393. return regex;
  394. }
  395. static int
  396. match_word(
  397. const char * glob,
  398. const char * word,
  399. const char separator)
  400. {
  401. char *regex;
  402. char *r;
  403. size_t len;
  404. int ch;
  405. int last_ch;
  406. int next_ch;
  407. size_t lenword;
  408. char *mword, *nword;
  409. char *mglob, *nglob;
  410. char *g;
  411. const char *w;
  412. int i;
  413. lenword = strlen(word);
  414. nword = (char *)alloc(lenword + 3);
  415. if (separator == '/' && lenword > 2 && word[0] == '\\' && word[1] == '\\' && !strchr(word, '/')) {
  416. /* Convert all "\" to '/' */
  417. mword = (char *)alloc(lenword + 1);
  418. r = mword;
  419. w = word;
  420. while (*w != '\0') {
  421. if (*w == '\\') {
  422. *r++ = '/';
  423. w += 1;
  424. } else {
  425. *r++ = *w++;
  426. }
  427. }
  428. *r++ = '\0';
  429. lenword = strlen(word);
  430. /* Convert all "\\" to '/' */
  431. mglob = (char *)alloc(strlen(glob) + 1);
  432. r = mglob;
  433. w = glob;
  434. while (*w != '\0') {
  435. if (*w == '\\' && *(w+1) == '\\') {
  436. *r++ = '/';
  437. w += 2;
  438. } else {
  439. *r++ = *w++;
  440. }
  441. }
  442. *r++ = '\0';
  443. } else {
  444. mword = stralloc(word);
  445. mglob = stralloc(glob);
  446. }
  447. r = nword;
  448. w = mword;
  449. if(lenword == 1 && *w == separator) {
  450. *r++ = separator;
  451. *r++ = separator;
  452. }
  453. else {
  454. if(*w != separator)
  455. *r++ = separator;
  456. while(*w != '\0')
  457. *r++ = *w++;
  458. if(*(r-1) != separator)
  459. *r++ = separator;
  460. }
  461. *r = '\0';
  462. /*
  463. * Allocate an area to convert into. The worst case is a six to
  464. * one expansion.
  465. */
  466. len = strlen(mglob);
  467. regex = (char *)alloc(1 + len * 6 + 1 + 1 + 2 + 2);
  468. r = regex;
  469. nglob = stralloc(mglob);
  470. g = nglob;
  471. if((len == 1 && nglob[0] == separator) ||
  472. (len == 2 && nglob[0] == '^' && nglob[1] == separator) ||
  473. (len == 2 && nglob[0] == separator && nglob[1] == '$') ||
  474. (len == 3 && nglob[0] == '^' && nglob[1] == separator &&
  475. nglob[2] == '$')) {
  476. *r++ = '^';
  477. *r++ = '\\';
  478. *r++ = separator;
  479. *r++ = '\\';
  480. *r++ = separator;
  481. *r++ = '$';
  482. }
  483. else {
  484. /*
  485. * Do the conversion:
  486. *
  487. * ? -> [^\separator]
  488. * * -> [^\separator]*
  489. * [!...] -> [^...]
  490. * ** -> .*
  491. *
  492. * The following are given a leading backslash to protect them
  493. * unless they already have a backslash:
  494. *
  495. * ( ) { } + . ^ $ |
  496. *
  497. * If the last
  498. * non-escaped character is \ leave it to cause a syntax
  499. * error when the regex is compiled.
  500. */
  501. if(*g == '^') {
  502. *r++ = '^';
  503. *r++ = '\\'; /* escape the separator */
  504. *r++ = separator;
  505. g++;
  506. if(*g == separator) g++;
  507. }
  508. else if(*g != separator) {
  509. *r++ = '\\'; /* add a leading \separator */
  510. *r++ = separator;
  511. }
  512. last_ch = '\0';
  513. for (ch = *g++; ch != '\0'; last_ch = ch, ch = *g++) {
  514. next_ch = *g;
  515. if (last_ch == '\\') {
  516. *r++ = (char)ch;
  517. ch = '\0'; /* so last_ch != '\\' next time */
  518. } else if (last_ch == '[' && ch == '!') {
  519. *r++ = '^';
  520. } else if (ch == '\\') {
  521. *r++ = (char)ch;
  522. } else if (ch == '*' || ch == '?') {
  523. if(ch == '*' && next_ch == '*') {
  524. *r++ = '.';
  525. g++;
  526. }
  527. else {
  528. *r++ = '[';
  529. *r++ = '^';
  530. *r++ = '\\';
  531. *r++ = separator;
  532. *r++ = ']';
  533. }
  534. if (ch == '*') {
  535. *r++ = '*';
  536. }
  537. } else if (ch == '$' && next_ch == '\0') {
  538. if(last_ch != separator) {
  539. *r++ = '\\';
  540. *r++ = separator;
  541. }
  542. *r++ = (char)ch;
  543. } else if ( ch == '('
  544. || ch == ')'
  545. || ch == '{'
  546. || ch == '}'
  547. || ch == '+'
  548. || ch == '.'
  549. || ch == '^'
  550. || ch == '$'
  551. || ch == '|') {
  552. *r++ = '\\';
  553. *r++ = (char)ch;
  554. } else {
  555. *r++ = (char)ch;
  556. }
  557. }
  558. if(last_ch != '\\') {
  559. if(last_ch != separator && last_ch != '$') {
  560. *r++ = '\\';
  561. *r++ = separator; /* add a trailing \separator */
  562. }
  563. }
  564. }
  565. *r = '\0';
  566. i = match(regex,nword);
  567. amfree(mword);
  568. amfree(mglob);
  569. amfree(nword);
  570. amfree(nglob);
  571. amfree(regex);
  572. return i;
  573. }
  574. int
  575. match_host(
  576. const char * glob,
  577. const char * host)
  578. {
  579. char *lglob, *lhost;
  580. char *c;
  581. const char *d;
  582. int i;
  583. lglob = (char *)alloc(strlen(glob)+1);
  584. c = lglob, d=glob;
  585. while( *d != '\0')
  586. *c++ = (char)tolower(*d++);
  587. *c = *d;
  588. lhost = (char *)alloc(strlen(host)+1);
  589. c = lhost, d=host;
  590. while( *d != '\0')
  591. *c++ = (char)tolower(*d++);
  592. *c = *d;
  593. i = match_word(lglob, lhost, (int)'.');
  594. amfree(lglob);
  595. amfree(lhost);
  596. return i;
  597. }
  598. int
  599. match_disk(
  600. const char * glob,
  601. const char * disk)
  602. {
  603. return match_word(glob, disk, '/');
  604. }
  605. static int
  606. alldigits(
  607. const char *str)
  608. {
  609. while (*str) {
  610. if (!isdigit((int)*(str++)))
  611. return 0;
  612. }
  613. return 1;
  614. }
  615. int
  616. match_datestamp(
  617. const char * dateexp,
  618. const char * datestamp)
  619. {
  620. char *dash;
  621. size_t len, len_suffix;
  622. size_t len_prefix;
  623. char firstdate[100], lastdate[100];
  624. char mydateexp[100];
  625. int match_exact;
  626. if(strlen(dateexp) >= 100 || strlen(dateexp) < 1) {
  627. goto illegal;
  628. }
  629. /* strip and ignore an initial "^" */
  630. if(dateexp[0] == '^') {
  631. strncpy(mydateexp, dateexp+1, sizeof(mydateexp)-1);
  632. mydateexp[sizeof(mydateexp)-1] = '\0';
  633. }
  634. else {
  635. strncpy(mydateexp, dateexp, sizeof(mydateexp)-1);
  636. mydateexp[sizeof(mydateexp)-1] = '\0';
  637. }
  638. if(mydateexp[strlen(mydateexp)-1] == '$') {
  639. match_exact = 1;
  640. mydateexp[strlen(mydateexp)-1] = '\0'; /* strip the trailing $ */
  641. }
  642. else
  643. match_exact = 0;
  644. /* a single dash represents a date range */
  645. if((dash = strchr(mydateexp,'-'))) {
  646. if(match_exact == 1 || strchr(dash+1, '-')) {
  647. goto illegal;
  648. }
  649. /* format: XXXYYYY-ZZZZ, indicating dates XXXYYYY to XXXZZZZ */
  650. len = (size_t)(dash - mydateexp); /* length of XXXYYYY */
  651. len_suffix = strlen(dash) - 1; /* length of ZZZZ */
  652. if (len_suffix > len) goto illegal;
  653. len_prefix = len - len_suffix; /* length of XXX */
  654. dash++;
  655. strncpy(firstdate, mydateexp, len);
  656. firstdate[len] = '\0';
  657. strncpy(lastdate, mydateexp, len_prefix);
  658. strncpy(&(lastdate[len_prefix]), dash, len_suffix);
  659. lastdate[len] = '\0';
  660. if (!alldigits(firstdate) || !alldigits(lastdate))
  661. goto illegal;
  662. if (strncmp(firstdate, lastdate, strlen(firstdate)) > 0)
  663. goto illegal;
  664. return ((strncmp(datestamp, firstdate, strlen(firstdate)) >= 0) &&
  665. (strncmp(datestamp, lastdate , strlen(lastdate)) <= 0));
  666. }
  667. else {
  668. if (!alldigits(mydateexp))
  669. goto illegal;
  670. if(match_exact == 1) {
  671. return (strcmp(datestamp, mydateexp) == 0);
  672. }
  673. else {
  674. return (strncmp(datestamp, mydateexp, strlen(mydateexp)) == 0);
  675. }
  676. }
  677. illegal:
  678. error(_("Illegal datestamp expression %s"),dateexp);
  679. /*NOTREACHED*/
  680. }
  681. int
  682. match_level(
  683. const char * levelexp,
  684. const char * level)
  685. {
  686. char *dash;
  687. long int low, hi, level_i;
  688. char mylevelexp[100];
  689. int match_exact;
  690. if(strlen(levelexp) >= 100 || strlen(levelexp) < 1) {
  691. error(_("Illegal level expression %s"),levelexp);
  692. /*NOTREACHED*/
  693. }
  694. if(levelexp[0] == '^') {
  695. strncpy(mylevelexp, levelexp+1, strlen(levelexp)-1);
  696. mylevelexp[strlen(levelexp)-1] = '\0';
  697. }
  698. else {
  699. strncpy(mylevelexp, levelexp, strlen(levelexp));
  700. mylevelexp[strlen(levelexp)] = '\0';
  701. }
  702. if(mylevelexp[strlen(mylevelexp)] == '$') {
  703. match_exact = 1;
  704. mylevelexp[strlen(mylevelexp)] = '\0';
  705. }
  706. else
  707. match_exact = 0;
  708. if((dash = strchr(mylevelexp,'-'))) {
  709. if(match_exact == 1) {
  710. goto illegal;
  711. }
  712. *dash = '\0';
  713. if (!alldigits(mylevelexp) || !alldigits(dash+1)) goto illegal;
  714. errno = 0;
  715. low = strtol(mylevelexp, (char **) NULL, 10);
  716. if (errno) goto illegal;
  717. hi = strtol(dash+1, (char **) NULL, 10);
  718. if (errno) goto illegal;
  719. level_i = strtol(level, (char **) NULL, 10);
  720. if (errno) goto illegal;
  721. return ((level_i >= low) && (level_i <= hi));
  722. }
  723. else {
  724. if (!alldigits(mylevelexp)) goto illegal;
  725. if(match_exact == 1) {
  726. return (strcmp(level, mylevelexp) == 0);
  727. }
  728. else {
  729. return (strncmp(level, mylevelexp, strlen(mylevelexp)) == 0);
  730. }
  731. }
  732. illegal:
  733. error(_("Illegal level expression %s"),levelexp);
  734. /*NOTREACHED*/
  735. }