1 /*
2  * ##/%% variable matching code ripped out of ash shell for code sharing
3  *
4  * This code is derived from software contributed to Berkeley by
5  * Kenneth Almquist.
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  *
9  * Copyright (c) 1989, 1991, 1993, 1994
10  *      The Regents of the University of California.  All rights reserved.
11  *
12  * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
13  * was re-ported from NetBSD and debianized.
14  */
15 #ifdef STANDALONE
16 # include <stdbool.h>
17 # include <stdio.h>
18 # include <stdlib.h>
19 # include <string.h>
20 # include <unistd.h>
21 # define FAST_FUNC /* nothing */
22 # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
23 # define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
24 #else
25 # include "libbb.h"
26 #endif
27 #include <fnmatch.h>
28 #include "match.h"
29 
scan_and_match(char * string,const char * pattern,unsigned flags)30 char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
31 {
32 	char *loc;
33 	char *end;
34 	unsigned len = strlen(string);
35 	int early_exit;
36 
37 	/* We can stop the scan early only if the string part
38 	 * we are matching against is shrinking, and the pattern has
39 	 * an unquoted "star" at the corresponding end. There are two cases.
40 	 * Case 1:
41 	 * "qwerty" does not match against pattern "*zy",
42 	 * no point in trying to match "werty", "erty" etc:
43 	 */
44 	early_exit = (flags == (SCAN_MOVE_FROM_LEFT + SCAN_MATCH_RIGHT_HALF) && pattern[0] == '*');
45 
46 	if (flags & SCAN_MOVE_FROM_LEFT) {
47 		loc = string;
48 		end = string + len + 1;
49 	} else {
50 		loc = string + len;
51 		end = string - 1;
52 		if (flags == (SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF)) {
53 			/* Case 2:
54 			 * "qwerty" does not match against pattern "qz*",
55 			 * no point in trying to match "qwert", "qwer" etc:
56 			 */
57 			const char *p = pattern + strlen(pattern);
58 			if (--p >= pattern && *p == '*') {
59 				early_exit = 1;
60 				while (--p >= pattern && *p == '\\')
61 					early_exit ^= 1;
62 			}
63 		}
64 	}
65 
66 	while (loc != end) {
67 		char c;
68 		int r;
69 
70 		c = *loc;
71 		if (flags & SCAN_MATCH_LEFT_HALF) {
72 			*loc = '\0';
73 			r = fnmatch(pattern, string, 0);
74 			*loc = c;
75 		} else {
76 			r = fnmatch(pattern, loc, 0);
77 		}
78 		if (r == 0) /* match found */
79 			return loc;
80 		if (early_exit) {
81 #ifdef STANDALONE
82 			printf("(early exit) ");
83 #endif
84 			break;
85 		}
86 
87 		if (flags & SCAN_MOVE_FROM_LEFT) {
88 			loc++;
89 		} else {
90 			loc--;
91 		}
92 	}
93 	return NULL;
94 }
95 
96 #ifdef STANDALONE
main(int argc,char * argv[])97 int main(int argc, char *argv[])
98 {
99 	char *string;
100 	char *op;
101 	char *pattern;
102 	char *loc;
103 
104 	setvbuf(stdout, NULL, _IONBF, 0);
105 
106 	if (!argv[1]) {
107 		puts(
108 			"Usage: match <test> [test...]\n\n"
109 			"Where a <test> is the form: <string><op><match>\n"
110 			"This is to test the shell ${var#val} expression type.\n\n"
111 			"e.g. `match 'abc#a*'` -> bc"
112 		);
113 		return 1;
114 	}
115 
116 	while (*++argv) {
117 		size_t off;
118 		unsigned scan_flags;
119 
120 		string = *argv;
121 		off = strcspn(string, "#%");
122 		if (!off) {
123 			printf("invalid format\n");
124 			continue;
125 		}
126 		op = string + off;
127 		scan_flags = pick_scan(op[0], op[1]);
128 
129 		printf("'%s': flags:%x, ", string, scan_flags);
130 		pattern = op + 1;
131 		if (op[0] == op[1])
132 			pattern++;
133 		op[0] = '\0';
134 
135 		loc = scan_and_match(string, pattern, scan_flags);
136 
137 		if (scan_flags & SCAN_MATCH_LEFT_HALF) {
138 			printf("'%s'\n", loc);
139 		} else {
140 			if (loc)
141 				*loc = '\0';
142 			printf("'%s'\n", string);
143 		}
144 	}
145 
146 	return 0;
147 }
148 #endif
149