PageRenderTime 58ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/brlcad/tags/rel-7-14-2/src/libbu/fnmatch.c

https://bitbucket.org/vrrm/brl-cad-copy-for-fast-history-browsing-in-git
C | 341 lines | 221 code | 32 blank | 88 comment | 119 complexity | 68ef92a2b02627cae08353c8b9eec438 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, LGPL-2.1, Apache-2.0, AGPL-3.0, LGPL-3.0, GPL-3.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, 0BSD, BSD-3-Clause
  1. /* F N M A T C H . C
  2. * BRL-CAD
  3. *
  4. * Copyright (c) 1993-2009 United States Government as represented by
  5. * the U.S. Army Research Laboratory.
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public License
  9. * version 2.1 as published by the Free Software Foundation.
  10. *
  11. * This library is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this file; see the file named COPYING for more
  18. * information.
  19. */
  20. /** @file fnmatch.c
  21. *
  22. * Based off of OpenBSD's fnmatch.c v 1.13 2006/03/31
  23. *
  24. * Copyright (c) 1989, 1993, 1994
  25. * The Regents of the University of California. All rights reserved.
  26. *
  27. * This code is derived from software contributed to Berkeley by
  28. * Guido van Rossum.
  29. *
  30. * Redistribution and use in source and binary forms, with or without
  31. * modification, are permitted provided that the following conditions
  32. * are met:
  33. * 1. Redistributions of source code must retain the above copyright
  34. * notice, this list of conditions and the following disclaimer.
  35. * 2. Redistributions in binary form must reproduce the above copyright
  36. * notice, this list of conditions and the following disclaimer in the
  37. * documentation and/or other materials provided with the distribution.
  38. * 3. Neither the name of the University nor the names of its contributors
  39. * may be used to endorse or promote products derived from this software
  40. * without specific prior written permission.
  41. *
  42. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  43. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  44. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  45. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  46. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  47. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  48. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  49. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  50. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  51. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  52. * SUCH DAMAGE.
  53. */
  54. /*
  55. * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
  56. * Compares a filename or pathname to a pattern.
  57. */
  58. #include "common.h"
  59. #include <stdlib.h>
  60. #include <ctype.h>
  61. #include <string.h>
  62. #include "bio.h"
  63. #include "bu.h"
  64. #define _BU_FNMATCH_H_
  65. #define BU_FNM_NOMATCH 1 /* Match failed. */
  66. #define BU_FNM_NOSYS 2 /* Function not supported (unused). */
  67. #define BU_FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
  68. #define BU_FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
  69. #define BU_FNM_PERIOD 0x04 /* Period must be matched by period. */
  70. #define BU_FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
  71. #define BU_FNM_IGNORECASE BU_CASEFOLD
  72. #define BU_FNM_FILE_NAME BU_FNM_PATHNAME
  73. #define BU_FNM_EOS '\0'
  74. #define BU_FNM_RANGE_MATCH 1
  75. #define BU_FNM_RANGE_NOMATCH 0
  76. #define BU_FNM_RANGE_ERROR (-1)
  77. /* isblank appears to be obsolete in newer ctype.h files so use
  78. * ccblank instead when looking for the "blank" character class.
  79. */
  80. static int
  81. ccblank(int c)
  82. {
  83. #ifdef isblank
  84. return isblank(c);
  85. #else
  86. return (c == ' ' || c == '\t');
  87. #endif
  88. }
  89. typedef struct _charclass {
  90. char *idstring; /* identifying string */
  91. int (*checkfun)(int); /* testing function */
  92. } CHARCLASS;
  93. static CHARCLASS charclasses[] = {
  94. { "alnum", isalnum },
  95. { "alpha", isalpha },
  96. { "blank", ccblank },
  97. { "cntrl", iscntrl },
  98. { "digit", isdigit },
  99. { "graph", isgraph },
  100. { "lower", islower },
  101. { "print", isprint },
  102. { "punct", ispunct },
  103. { "space", isspace },
  104. { "upper", isupper },
  105. { "xdigit", isxdigit },
  106. };
  107. static int
  108. classcompare(const void *a, const void *b)
  109. {
  110. return (strcmp(((CHARCLASS *)a)->idstring, ((CHARCLASS *)b)->idstring));
  111. }
  112. static CHARCLASS *
  113. findclass(char *charclass)
  114. {
  115. CHARCLASS tmp;
  116. tmp.idstring = charclass;
  117. return ((CHARCLASS *)bsearch(&tmp, charclasses, sizeof(charclasses)/sizeof(CHARCLASS), sizeof(CHARCLASS), classcompare));
  118. }
  119. static int
  120. charclassmatch(const char *pattern, char test, int *s)
  121. {
  122. char c;
  123. int counter = 0;
  124. int resultholder = 0;
  125. struct bu_vls classname;
  126. CHARCLASS *ctclass;
  127. bu_vls_init(&classname);
  128. while ((c = *pattern++) && (c != ':') && (resultholder != -1)) {
  129. if (c == BU_FNM_EOS) resultholder = -1;
  130. counter++;
  131. }
  132. c = *pattern++;
  133. if (c != ']') resultholder = -1;
  134. bu_vls_strncpy(&classname, pattern-counter-2, counter);
  135. if ((ctclass = findclass(bu_vls_addr(&classname))) == NULL) {
  136. bu_log("Unknown character class type: %s\n", bu_vls_addr(&classname));
  137. resultholder = -1;
  138. } else {
  139. /*bu_log("classname: %s, test char = %c, (class->checkfun)=%d\n", bu_vls_addr(&classname), test, (ctclass->checkfun)(test));*/
  140. if ((ctclass->checkfun)(test) != 0) {
  141. resultholder = counter;
  142. } else {
  143. resultholder = 0;
  144. }
  145. }
  146. *s = resultholder;
  147. bu_vls_free(&classname);
  148. return counter;
  149. }
  150. static int
  151. _rangematch(const char *pattern, char test, int flags, char **newp)
  152. {
  153. int negate, ok, s, incpattern;
  154. char c, c2;
  155. /*
  156. * A bracket expression starting with an unquoted circumflex
  157. * character produces unspecified results (IEEE 1003.2-1992,
  158. * 3.13.2). This implementation treats it like '!', for
  159. * consistency with the regular expression syntax.
  160. * J.T. Conklin (conklin@ngai.kaleida.com)
  161. */
  162. if ((negate = (*pattern == '!' || *pattern == '^')))
  163. ++pattern;
  164. if (flags & BU_CASEFOLD)
  165. test = (char)tolower((unsigned char)test);
  166. ok = 0;
  167. /*
  168. * A right bracket shall lose its special meaning and represent
  169. * itself in a bracket expression if it occurs first in the list.
  170. * -- POSIX.2 2.8.3.2
  171. */
  172. c = *pattern++;
  173. do {
  174. if (c == '\\' && !(flags & BU_FNM_NOESCAPE))
  175. c = *pattern++;
  176. if (c == BU_FNM_EOS)
  177. return (BU_FNM_RANGE_ERROR);
  178. if (c == '/' && (flags & BU_FNM_PATHNAME))
  179. return (BU_FNM_RANGE_NOMATCH);
  180. if ((flags & BU_CASEFOLD))
  181. c = (char)tolower((unsigned char)c);
  182. if (*pattern == '-'
  183. && (c2 = *(pattern+1)) != BU_FNM_EOS && c2 != ']') {
  184. pattern += 2;
  185. if (c2 == '\\' && !(flags & BU_FNM_NOESCAPE))
  186. c2 = *pattern++;
  187. if (c2 == BU_FNM_EOS)
  188. return (BU_FNM_RANGE_ERROR);
  189. if (flags & BU_CASEFOLD)
  190. c2 = (char)tolower((unsigned char)c2);
  191. if (c <= test && test <= c2)
  192. ok = 1;
  193. } else if (c == test) {
  194. ok = 1;
  195. } else if ((c == '[') && (*pattern == ':')) {
  196. incpattern = charclassmatch(pattern+1, test, &s);
  197. if (s == -1) return (BU_FNM_RANGE_ERROR);
  198. if (s > 0) ok = 1;
  199. pattern = pattern + incpattern + 3;
  200. }
  201. } while ((c = *pattern++) != ']');
  202. *newp = (char *)pattern;
  203. return (ok == negate ? BU_FNM_RANGE_NOMATCH : BU_FNM_RANGE_MATCH);
  204. }
  205. int
  206. bu_fnmatch(const char *pattern, const char *string, int flags)
  207. {
  208. const char *stringstart;
  209. char *newp;
  210. char c, test;
  211. for (stringstart = string;;) {
  212. switch (c = *pattern++) {
  213. case BU_FNM_EOS:
  214. if ((flags & BU_FNM_LEADING_DIR) && *string == '/')
  215. return (0);
  216. return (*string == BU_FNM_EOS ? 0 : BU_FNM_NOMATCH);
  217. case '?':
  218. if (*string == BU_FNM_EOS)
  219. return (BU_FNM_NOMATCH);
  220. if (*string == '/' && (flags & BU_FNM_PATHNAME))
  221. return (BU_FNM_NOMATCH);
  222. if (*string == '.' && (flags & BU_FNM_PERIOD) &&
  223. (string == stringstart ||
  224. ((flags & BU_FNM_PATHNAME) && *(string - 1) == '/')))
  225. return (BU_FNM_NOMATCH);
  226. ++string;
  227. break;
  228. case '*':
  229. c = *pattern;
  230. /* Collapse multiple stars. */
  231. while (c == '*')
  232. c = *++pattern;
  233. if (*string == '.' && (flags & BU_FNM_PERIOD) &&
  234. (string == stringstart ||
  235. ((flags & BU_FNM_PATHNAME) && *(string - 1) == '/')))
  236. return (BU_FNM_NOMATCH);
  237. /* Optimize for pattern with * at end or before /. */
  238. if (c == BU_FNM_EOS) {
  239. if (flags & BU_FNM_PATHNAME)
  240. return ((flags & BU_FNM_LEADING_DIR) ||
  241. strchr(string, '/') == NULL ?
  242. 0 : BU_FNM_NOMATCH);
  243. else
  244. return (0);
  245. } else if (c == '/' && (flags & BU_FNM_PATHNAME)) {
  246. if ((string = strchr(string, '/')) == NULL)
  247. return (BU_FNM_NOMATCH);
  248. break;
  249. }
  250. /* General case, use recursion. */
  251. while ((test = *string) != BU_FNM_EOS) {
  252. if (!bu_fnmatch(pattern, string, flags & ~BU_FNM_PERIOD))
  253. return (0);
  254. if (test == '/' && (flags & BU_FNM_PATHNAME))
  255. break;
  256. ++string;
  257. }
  258. return (BU_FNM_NOMATCH);
  259. case '[':
  260. if (*string == BU_FNM_EOS)
  261. return (BU_FNM_NOMATCH);
  262. if (*string == '/' && (flags & BU_FNM_PATHNAME))
  263. return (BU_FNM_NOMATCH);
  264. if (*string == '.' && (flags & BU_FNM_PERIOD) &&
  265. (string == stringstart ||
  266. ((flags & BU_FNM_PATHNAME) && *(string - 1) == '/')))
  267. return (BU_FNM_NOMATCH);
  268. switch (_rangematch(pattern, *string, flags, &newp)) {
  269. case BU_FNM_RANGE_ERROR:
  270. /* not a good range, treat as normal text */
  271. goto normal;
  272. case BU_FNM_RANGE_MATCH:
  273. pattern = newp;
  274. break;
  275. case BU_FNM_RANGE_NOMATCH:
  276. return (BU_FNM_NOMATCH);
  277. }
  278. ++string;
  279. break;
  280. case '\\':
  281. if (!(flags & BU_FNM_NOESCAPE)) {
  282. if ((c = *pattern++) == BU_FNM_EOS) {
  283. c = '\\';
  284. --pattern;
  285. }
  286. }
  287. /* FALLTHROUGH */
  288. default:
  289. normal:
  290. if (c != *string && !((flags & BU_CASEFOLD) &&
  291. (tolower((unsigned char)c) ==
  292. tolower((unsigned char)*string))))
  293. return (BU_FNM_NOMATCH);
  294. ++string;
  295. break;
  296. }
  297. }
  298. /* NOTREACHED */
  299. return 0;
  300. }
  301. /*
  302. * Local Variables:
  303. * tab-width: 8
  304. * mode: C
  305. * indent-tabs-mode: t
  306. * c-file-style: "stroustrup"
  307. * End:
  308. * ex: shiftwidth=4 tabstop=8
  309. */