fnmatch.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. fnmatch.c
  9. Abstract:
  10. This module implements the fnmatch function, which is used to match
  11. patterns.
  12. Author:
  13. Evan Green 10-Feb-2015
  14. Environment:
  15. User Mode C Library
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include "libcp.h"
  21. #include <assert.h>
  22. #include <ctype.h>
  23. #include <fnmatch.h>
  24. #include <string.h>
  25. //
  26. // ---------------------------------------------------------------- Definitions
  27. //
  28. //
  29. // ------------------------------------------------------ Data Type Definitions
  30. //
  31. //
  32. // ----------------------------------------------- Internal Function Prototypes
  33. //
  34. int
  35. ClpFnMatch (
  36. const char *Pattern,
  37. const char *String,
  38. const char *Start,
  39. int Flags
  40. );
  41. INT
  42. ClpFnMatchPatternSet (
  43. const char *Pattern,
  44. char Character,
  45. int Flags,
  46. const char **PatternAfterSet
  47. );
  48. //
  49. // -------------------------------------------------------------------- Globals
  50. //
  51. //
  52. // ------------------------------------------------------------------ Functions
  53. //
  54. LIBC_API
  55. int
  56. fnmatch (
  57. const char *Pattern,
  58. const char *String,
  59. int Flags
  60. )
  61. /*++
  62. Routine Description:
  63. This routine matches patterns as described by POSIX in the shell grammar
  64. sections of "Patterns Matching a Single Character", "Patterns Matching
  65. Multiple Characters", and "Patterns Used for Filename Expansion".
  66. Arguments:
  67. Pattern - Supplies a pointer to the null terminated pattern string.
  68. String - Supplies a pointer to the null terminated string to match against.
  69. Flags - Supplies a bitfield of flags governing the behavior of the matching
  70. function. See FNM_* definitions.
  71. Return Value:
  72. 0 if the pattern matches.
  73. FNM_NOMATCH if the pattern does not match.
  74. -1 on error.
  75. --*/
  76. {
  77. return ClpFnMatch(Pattern, String, String, Flags);
  78. }
  79. //
  80. // --------------------------------------------------------- Internal Functions
  81. //
  82. int
  83. ClpFnMatch (
  84. const char *Pattern,
  85. const char *String,
  86. const char *Start,
  87. int Flags
  88. )
  89. /*++
  90. Routine Description:
  91. This routine represents the inner worker for the fnmatch function, which
  92. may be called recursively.
  93. Arguments:
  94. Pattern - Supplies a pointer to the null terminated pattern string.
  95. String - Supplies a pointer to the null terminated string to match against.
  96. Start - Supplies the original beginning of the string to match against.
  97. Flags - Supplies a bitfield of flags governing the behavior of the matching
  98. function. See FNM_* definitions.
  99. Return Value:
  100. 0 if the pattern matches.
  101. FNM_NOMATCH if the pattern does not match.
  102. -1 on error.
  103. --*/
  104. {
  105. const char *PatternAfterSet;
  106. CHAR PatternCharacter;
  107. INT SetResult;
  108. CHAR StringCharacter;
  109. while (TRUE) {
  110. PatternCharacter = *Pattern;
  111. Pattern += 1;
  112. StringCharacter = *String;
  113. switch (PatternCharacter) {
  114. case '\0':
  115. if (((Flags & FNM_LEADING_DIR) != 0) && (StringCharacter == '/')) {
  116. return 0;
  117. }
  118. if (StringCharacter == PatternCharacter) {
  119. return 0;
  120. }
  121. return FNM_NOMATCH;
  122. //
  123. // Question mark matches any character.
  124. //
  125. case '?':
  126. if (StringCharacter == '\0') {
  127. return FNM_NOMATCH;
  128. }
  129. //
  130. // If the pathname flag is set, don't match wildcards against
  131. // slashes.
  132. //
  133. if ((StringCharacter == '/') && ((Flags & FNM_PATHNAME) != 0)) {
  134. return FNM_NOMATCH;
  135. }
  136. //
  137. // If the period flag is set, then a leading period is matched
  138. // explicitly. If the pathname flag is set, then leading means at
  139. // the beginning of a path component. Otherwise, leading means at
  140. // the beginning of the string.
  141. //
  142. if ((StringCharacter == '.') && ((Flags & FNM_PERIOD) != 0)) {
  143. if (String == Start) {
  144. return FNM_NOMATCH;
  145. } else if (((Flags & FNM_PATHNAME) != 0) &&
  146. (*(String - 1) == '/')) {
  147. return FNM_NOMATCH;
  148. }
  149. }
  150. String += 1;
  151. break;
  152. //
  153. // Asterisks match a bunch of any character.
  154. //
  155. case '*':
  156. //
  157. // Collapse multiple asterisks in a row.
  158. //
  159. while (*Pattern == '*') {
  160. Pattern += 1;
  161. }
  162. //
  163. // Again, if the period flag is set, then leading periods don't
  164. // match against wildcards.
  165. //
  166. if ((StringCharacter == '.') && ((Flags & FNM_PERIOD) != 0)) {
  167. if (String == Start) {
  168. return FNM_NOMATCH;
  169. } else if (((Flags & FNM_PATHNAME) != 0) &&
  170. (*(String - 1) == '/')) {
  171. return FNM_NOMATCH;
  172. }
  173. }
  174. //
  175. // Specially handle an asterisk at the end of the pattern.
  176. //
  177. if (*Pattern == '\0') {
  178. if ((Flags & FNM_PATHNAME) == 0) {
  179. return 0;
  180. }
  181. //
  182. // The pathname flag is set. If there are no more path
  183. // components, or the leading directory flag is set, then this
  184. // matches. Otherwise, it doesn't.
  185. //
  186. if (((Flags & FNM_LEADING_DIR) != 0) ||
  187. (strchr(String, '/') == NULL)) {
  188. return 0;
  189. }
  190. return FNM_NOMATCH;
  191. //
  192. // If the next pattern character is a path separator and the
  193. // pathname flag is set, then the star only matches up to the next
  194. // slash.
  195. //
  196. } else if ((*Pattern == '/') && ((Flags & FNM_PATHNAME) != 0)) {
  197. String = strchr(String, '/');
  198. if (String == NULL) {
  199. return FNM_NOMATCH;
  200. }
  201. //
  202. // The asterisk matched up to the next slash.
  203. //
  204. break;
  205. }
  206. //
  207. // Detrermine how much of the string to chew through with the
  208. // asterisk.
  209. //
  210. while (StringCharacter != '\0') {
  211. if (ClpFnMatch(Pattern, String, Start, Flags) == 0) {
  212. return 0;
  213. }
  214. StringCharacter = *String;
  215. if ((StringCharacter == '/') && ((Flags & FNM_PATHNAME) != 0)) {
  216. break;
  217. }
  218. String += 1;
  219. }
  220. return FNM_NOMATCH;
  221. //
  222. // An open bracket matches a set of characters or a character class.
  223. //
  224. case '[':
  225. if (StringCharacter == '\0') {
  226. return FNM_NOMATCH;
  227. }
  228. //
  229. // Slashes don't match wildcards if the pathname flag is set.
  230. //
  231. if ((StringCharacter == '/') && ((Flags & FNM_PATHNAME) != 0)) {
  232. return FNM_NOMATCH;
  233. }
  234. //
  235. // Leading periods don't match wildcards if the period flag is set.
  236. //
  237. if ((StringCharacter == '.') && ((Flags & FNM_PERIOD) != 0)) {
  238. if (String == Start) {
  239. return FNM_NOMATCH;
  240. } else if (((Flags & FNM_PATHNAME) != 0) &&
  241. (*(String - 1) == '/')) {
  242. return FNM_NOMATCH;
  243. }
  244. }
  245. SetResult = ClpFnMatchPatternSet(Pattern,
  246. StringCharacter,
  247. Flags,
  248. &PatternAfterSet);
  249. if (SetResult == 0) {
  250. Pattern = PatternAfterSet;
  251. String += 1;
  252. break;
  253. } else if (SetResult == FNM_NOMATCH) {
  254. return FNM_NOMATCH;
  255. }
  256. //
  257. // Fall through if the pattern set had an error and treat it as a
  258. // normal character. The lack of a break is intentional.
  259. //
  260. default:
  261. //
  262. // This is the normal character area. If it's a backslash, then the
  263. // normal character is actually the next character (unless
  264. // escaping was disabled).
  265. //
  266. if (PatternCharacter == '\\') {
  267. if ((Flags & FNM_NOESCAPE) == 0) {
  268. PatternCharacter = *Pattern;
  269. Pattern += 1;
  270. }
  271. }
  272. if (PatternCharacter != StringCharacter) {
  273. if (((Flags & FNM_CASEFOLD) == 0) ||
  274. (tolower(PatternCharacter) != tolower(StringCharacter))) {
  275. return FNM_NOMATCH;
  276. }
  277. }
  278. String += 1;
  279. break;
  280. }
  281. }
  282. //
  283. // Execution never gets here.
  284. //
  285. assert(FALSE);
  286. return -1;
  287. }
  288. INT
  289. ClpFnMatchPatternSet (
  290. const char *Pattern,
  291. char Character,
  292. int Flags,
  293. const char **PatternAfterSet
  294. )
  295. /*++
  296. Routine Description:
  297. This routine matches against a character against a character set.
  298. Arguments:
  299. Pattern - Supplies a pointer to the null terminated pattern string.
  300. Character - Supplies the character to match against.
  301. Flags - Supplies a bitfield of flags governing the behavior of the matching
  302. function. See FNM_* definitions.
  303. PatternAfterSet - Supplies a pointer where the advanced pattern string will
  304. be returned on success.
  305. Return Value:
  306. 0 if the pattern matches.
  307. FNM_NOMATCH if the pattern does not match.
  308. -1 on error.
  309. --*/
  310. {
  311. char EndCharacter;
  312. BOOL Found;
  313. BOOL Negated;
  314. char PatternCharacter;
  315. const char *PatternStart;
  316. //
  317. // Treat a ! or a ^ as a negation of the character set.
  318. //
  319. Negated = FALSE;
  320. if ((*Pattern == '!') || (*Pattern == '^')) {
  321. Negated = TRUE;
  322. Pattern += 1;
  323. }
  324. if ((Flags & FNM_CASEFOLD) != 0) {
  325. Character = tolower(Character);
  326. }
  327. PatternStart = Pattern;
  328. Found = FALSE;
  329. while (TRUE) {
  330. //
  331. // Look for the closing bracket, and stop looping once found. If the
  332. // closing bracket is the very first character, it is treated as a
  333. // normal character.
  334. //
  335. if ((*Pattern == ']') && (Pattern > PatternStart)) {
  336. Pattern += 1;
  337. break;
  338. //
  339. // This wasn't a valid pattern set if the string ended before a closing
  340. // bracket (ie. [abc).
  341. //
  342. } else if (*Pattern == '\0') {
  343. return -1;
  344. //
  345. // If the pathname flag is set, slashes had better not be in the
  346. // pattern.
  347. //
  348. } else if ((*Pattern == '/') && ((Flags & FNM_PATHNAME) != 0)) {
  349. return FNM_NOMATCH;
  350. //
  351. // Backslash escapes characters (unless disabled).
  352. //
  353. } else if ((*Pattern == '\\') && ((Flags & FNM_NOESCAPE) == 0)) {
  354. Pattern += 1;
  355. }
  356. PatternCharacter = *Pattern;
  357. Pattern += 1;
  358. if ((Flags & FNM_CASEFOLD) != 0) {
  359. PatternCharacter = tolower(PatternCharacter);
  360. }
  361. //
  362. // Handle a range, like a-f.
  363. //
  364. if ((*Pattern == '-') && (*(Pattern + 1) != '\0') &&
  365. (*(Pattern + 1) != ']')) {
  366. Pattern += 1;
  367. if ((*Pattern == '\\') && ((Flags & FNM_NOESCAPE) == 0) &&
  368. (*(Pattern + 1) != '\0')) {
  369. Pattern += 1;
  370. }
  371. EndCharacter = *Pattern;
  372. Pattern += 1;
  373. if (EndCharacter == '\0') {
  374. return -1;
  375. }
  376. if ((Flags & FNM_CASEFOLD) != 0) {
  377. EndCharacter = tolower(EndCharacter);
  378. }
  379. if ((Character >= PatternCharacter) &&
  380. (Character <= EndCharacter)) {
  381. Found = TRUE;
  382. }
  383. //
  384. // Otherwise, just look to see if this particular character matches.
  385. //
  386. } else if (Character == PatternCharacter) {
  387. Found = TRUE;
  388. }
  389. }
  390. *PatternAfterSet = Pattern;
  391. if (Negated != FALSE) {
  392. if (Found != FALSE) {
  393. Found = FALSE;
  394. } else {
  395. Found = TRUE;
  396. }
  397. }
  398. if (Found != FALSE) {
  399. return 0;
  400. }
  401. return FNM_NOMATCH;
  402. }