New API for converting globbing patterns to extended POSIX regex
* include/mailutils/opool.h (mu_nonlocal_jmp_t): New type. (mu_opool_setjmp,mu_opool_clrjmp): New functions. (mu_opool_setup_nonlocal_jump): New macro. * libmailutils/base/opool.c (_mu_opool)<jmp>: New field. (alloc_bucket): Do a non-local jump on out of memory condition, if jmp is not NULL. (mu_opool_setjmp,mu_opool_clrjmp): New functions. * libmailutils/base/glob.c: New file. * libmailutils/base/Makefile.am: Add glob.c * include/mailutils/glob.h: New file. * include/mailutils/mailutils.h: Include glob.h * libmailutils/tests/globtest.c: New file. * libmailutils/tests/globtest.at: New test. * libmailutils/tests/Makefile.am: Add new files. * libmailutils/tests/testsuite.at: Include new test.
Showing
9 changed files
with
456 additions
and
0 deletions
... | @@ -32,6 +32,7 @@ | ... | @@ -32,6 +32,7 @@ |
32 | #include <mailutils/error.h> | 32 | #include <mailutils/error.h> |
33 | #include <mailutils/filter.h> | 33 | #include <mailutils/filter.h> |
34 | #include <mailutils/folder.h> | 34 | #include <mailutils/folder.h> |
35 | #include <mailutils/glob.h> | ||
35 | #include <mailutils/header.h> | 36 | #include <mailutils/header.h> |
36 | #include <mailutils/iterator.h> | 37 | #include <mailutils/iterator.h> |
37 | #include <mailutils/kwd.h> | 38 | #include <mailutils/kwd.h> | ... | ... |
... | @@ -19,6 +19,7 @@ | ... | @@ -19,6 +19,7 @@ |
19 | #define _MAILUTILS_OPOOL_H | 19 | #define _MAILUTILS_OPOOL_H |
20 | 20 | ||
21 | #include <mailutils/types.h> | 21 | #include <mailutils/types.h> |
22 | #include <setjmp.h> | ||
22 | 23 | ||
23 | #ifndef MU_OPOOL_BUCKET_SIZE | 24 | #ifndef MU_OPOOL_BUCKET_SIZE |
24 | # define MU_OPOOL_BUCKET_SIZE 1024 | 25 | # define MU_OPOOL_BUCKET_SIZE 1024 |
... | @@ -33,6 +34,27 @@ int mu_opool_create (mu_opool_t *pret, int flags); | ... | @@ -33,6 +34,27 @@ int mu_opool_create (mu_opool_t *pret, int flags); |
33 | int mu_opool_set_bucket_size (mu_opool_t opool, size_t size); | 34 | int mu_opool_set_bucket_size (mu_opool_t opool, size_t size); |
34 | int mu_opool_get_bucket_size (mu_opool_t opool, size_t *psize); | 35 | int mu_opool_get_bucket_size (mu_opool_t opool, size_t *psize); |
35 | 36 | ||
37 | struct mu_nonlocal_jmp | ||
38 | { | ||
39 | jmp_buf buf; | ||
40 | struct mu_nonlocal_jmp *next; | ||
41 | }; | ||
42 | |||
43 | typedef struct mu_nonlocal_jmp mu_nonlocal_jmp_t; | ||
44 | |||
45 | void mu_opool_setjmp (mu_opool_t opool, mu_nonlocal_jmp_t *err); | ||
46 | void mu_opool_clrjmp (mu_opool_t opool); | ||
47 | |||
48 | #define mu_opool_setup_nonlocal_jump(p,jb) \ | ||
49 | do \ | ||
50 | { \ | ||
51 | int __rc = setjmp (jb.buf); \ | ||
52 | if (__rc) \ | ||
53 | return __rc; \ | ||
54 | mu_opool_setjmp (p, &jb); \ | ||
55 | } \ | ||
56 | while (0) | ||
57 | |||
36 | /* Merge all data from *SRC into *DST. If the latter is NULL, create | 58 | /* Merge all data from *SRC into *DST. If the latter is NULL, create |
37 | it. On success, free *SRC and initialize it with NULL. */ | 59 | it. On success, free *SRC and initialize it with NULL. */ |
38 | int mu_opool_union (mu_opool_t *dst, mu_opool_t *src); | 60 | int mu_opool_union (mu_opool_t *dst, mu_opool_t *src); | ... | ... |
libmailutils/base/glob.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 2007, 2009-2012, 2014-2016 Free Software Foundation, | ||
3 | Inc. | ||
4 | |||
5 | GNU Mailutils is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 3, or (at your option) | ||
8 | any later version. | ||
9 | |||
10 | GNU Mailutils is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ | ||
17 | |||
18 | #if HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | #include <mailutils/opool.h> | ||
22 | #include <mailutils/error.h> | ||
23 | #include <mailutils/errno.h> | ||
24 | #include <mailutils/glob.h> | ||
25 | #include <regex.h> | ||
26 | #include <string.h> | ||
27 | #include <stdlib.h> | ||
28 | |||
29 | static void | ||
30 | parse_character_class (unsigned char const *str, mu_opool_t pool, | ||
31 | unsigned char const **endp) | ||
32 | { | ||
33 | unsigned char const *cur; | ||
34 | |||
35 | cur = str + 1; | ||
36 | if (*cur == '!') | ||
37 | cur++; | ||
38 | if (*cur == ']') | ||
39 | cur++; | ||
40 | |||
41 | while (*cur && *cur != ']') | ||
42 | { | ||
43 | int c = *cur++; | ||
44 | if (c == '\\') | ||
45 | cur++; | ||
46 | else if (c >= 0xc2) | ||
47 | { | ||
48 | size_t len; | ||
49 | |||
50 | if (c < 0xe0) | ||
51 | len = 1; | ||
52 | else if (c < 0xf0) | ||
53 | len = 2; | ||
54 | else if (c < 0xf8) | ||
55 | len = 3; | ||
56 | else | ||
57 | /* Invalid UTF-8 sequence; skip. */ | ||
58 | continue; | ||
59 | |||
60 | while (len-- && *cur) | ||
61 | cur++; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | if (*cur == ']') | ||
66 | { | ||
67 | /* Valid character class */ | ||
68 | mu_opool_append_char (pool, *str); | ||
69 | str++; | ||
70 | if (*str == '!') | ||
71 | { | ||
72 | mu_opool_append_char (pool, '^'); | ||
73 | str++; | ||
74 | } | ||
75 | while (str < cur) | ||
76 | { | ||
77 | if (*str == '[') | ||
78 | mu_opool_append_char (pool, '\\'); | ||
79 | |||
80 | mu_opool_append_char (pool, *str); | ||
81 | str++; | ||
82 | } | ||
83 | mu_opool_append_char (pool, ']'); | ||
84 | *endp = cur + 1; | ||
85 | } | ||
86 | else | ||
87 | { | ||
88 | mu_opool_append_char (pool, '\\'); | ||
89 | mu_opool_append_char (pool, *str); | ||
90 | str++; | ||
91 | *endp = str; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | int | ||
96 | mu_glob_to_regex_opool (char const *pattern, mu_opool_t pool, int flags) | ||
97 | { | ||
98 | unsigned char const *str = (unsigned char const *) pattern; | ||
99 | mu_nonlocal_jmp_t jmp; | ||
100 | |||
101 | if (!(flags & MU_GLOBF_SUB)) | ||
102 | flags |= MU_GLOBF_COLLAPSE; | ||
103 | |||
104 | mu_opool_setup_nonlocal_jump (pool, jmp); | ||
105 | |||
106 | while (*str) | ||
107 | { | ||
108 | int c = *str++; | ||
109 | |||
110 | if (c < 0x80) | ||
111 | { | ||
112 | switch (c) | ||
113 | { | ||
114 | case '\\': | ||
115 | mu_opool_append_char (pool, '\\'); | ||
116 | if (*str && strchr ("?*[", *str)) | ||
117 | { | ||
118 | mu_opool_append_char (pool, *str); | ||
119 | str++; | ||
120 | } | ||
121 | else | ||
122 | mu_opool_append_char (pool, '\\'); | ||
123 | break; | ||
124 | |||
125 | case '?': | ||
126 | if (flags & MU_GLOBF_SUB) | ||
127 | mu_opool_append_char (pool, '('); | ||
128 | mu_opool_append_char (pool, '.'); | ||
129 | if (flags & MU_GLOBF_SUB) | ||
130 | mu_opool_append_char (pool, ')'); | ||
131 | break; | ||
132 | |||
133 | case '*': | ||
134 | if (flags & MU_GLOBF_COLLAPSE) | ||
135 | { | ||
136 | while (*str == '*') | ||
137 | str++; | ||
138 | } | ||
139 | |||
140 | if (flags & MU_GLOBF_SUB) | ||
141 | { | ||
142 | while (*str == '*') | ||
143 | { | ||
144 | mu_opool_append (pool, "()", 2); | ||
145 | str++; | ||
146 | } | ||
147 | mu_opool_append_char (pool, '('); | ||
148 | mu_opool_append (pool, ".*", 2); | ||
149 | mu_opool_append_char (pool, ')'); | ||
150 | } | ||
151 | else | ||
152 | mu_opool_append (pool, ".*", 2); | ||
153 | break; | ||
154 | |||
155 | case '[': | ||
156 | parse_character_class (str - 1, pool, &str); | ||
157 | break; | ||
158 | |||
159 | case '(': | ||
160 | case ')': | ||
161 | case '{': | ||
162 | case '}': | ||
163 | case '^': | ||
164 | case '$': | ||
165 | case ']': | ||
166 | case '|': | ||
167 | case '.': | ||
168 | mu_opool_append_char (pool, '\\'); | ||
169 | mu_opool_append_char (pool, c); | ||
170 | break; | ||
171 | |||
172 | default: | ||
173 | mu_opool_append_char (pool, c); | ||
174 | } | ||
175 | } | ||
176 | else | ||
177 | { | ||
178 | mu_opool_append_char (pool, c); | ||
179 | if (c >= 0xc2) | ||
180 | { | ||
181 | size_t len; | ||
182 | |||
183 | if (c < 0xe0) | ||
184 | len = 1; | ||
185 | else if (c < 0xf0) | ||
186 | len = 2; | ||
187 | else if (c < 0xf8) | ||
188 | len = 3; | ||
189 | else | ||
190 | /* Invalid UTF-8 sequence; skip. */ | ||
191 | continue; | ||
192 | |||
193 | for (; len-- && *str; str++) | ||
194 | mu_opool_append_char (pool, *str); | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | mu_opool_clrjmp (pool); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | int | ||
203 | mu_glob_to_regex (char **rxstr, char const *pattern, int flags) | ||
204 | { | ||
205 | mu_opool_t pool; | ||
206 | int rc; | ||
207 | mu_nonlocal_jmp_t jmp; | ||
208 | |||
209 | rc = mu_opool_create (&pool, MU_OPOOL_DEFAULT); | ||
210 | if (rc) | ||
211 | return rc; | ||
212 | mu_opool_setup_nonlocal_jump (pool, jmp); | ||
213 | mu_opool_append_char (pool, '^'); | ||
214 | rc = mu_glob_to_regex_opool (pattern, pool, flags); | ||
215 | if (rc == 0) | ||
216 | { | ||
217 | mu_opool_append_char (pool, '$'); | ||
218 | mu_opool_append_char (pool, 0); | ||
219 | *rxstr = mu_opool_detach (pool, NULL); | ||
220 | } | ||
221 | mu_opool_clrjmp (pool); | ||
222 | mu_opool_destroy (&pool); | ||
223 | return rc; | ||
224 | } | ||
225 | |||
226 | int | ||
227 | mu_glob_compile (regex_t *rx, char const *pattern, int flags) | ||
228 | { | ||
229 | char *str; | ||
230 | int rc; | ||
231 | int rxflags; | ||
232 | |||
233 | rc = mu_glob_to_regex (&str, pattern, flags); | ||
234 | if (rc) | ||
235 | return rc; | ||
236 | |||
237 | rxflags = REG_EXTENDED; | ||
238 | if (flags & MU_GLOBF_ICASE) | ||
239 | rxflags |= REG_ICASE; | ||
240 | if (!(flags & MU_GLOBF_SUB)) | ||
241 | rxflags |= REG_NOSUB; | ||
242 | |||
243 | rc = regcomp (rx, str, rxflags); | ||
244 | if (rc) | ||
245 | { | ||
246 | size_t size = regerror (rc, rx, NULL, 0); | ||
247 | char *errbuf = malloc (size + 1); | ||
248 | if (errbuf) | ||
249 | { | ||
250 | regerror (rc, rx, errbuf, size); | ||
251 | mu_error ("INTERNAL ERROR: can't compile regular expression \"%s\": %s", | ||
252 | str, mu_strerror (rc)); | ||
253 | } | ||
254 | else | ||
255 | mu_error ("INTERNAL ERROR: can't compile regular expression \"%s\"", | ||
256 | str); | ||
257 | mu_error ("INTERNAL ERROR: expression compiled from globbing pattern: %s", | ||
258 | pattern); | ||
259 | free (errbuf); | ||
260 | } | ||
261 | free (str); | ||
262 | |||
263 | return rc; | ||
264 | } |
... | @@ -53,6 +53,7 @@ struct _mu_opool | ... | @@ -53,6 +53,7 @@ struct _mu_opool |
53 | int flags; /* Flag bits */ | 53 | int flags; /* Flag bits */ |
54 | size_t bucket_size; /* Default bucket size */ | 54 | size_t bucket_size; /* Default bucket size */ |
55 | size_t itr_count; /* Number of iterators created for this pool */ | 55 | size_t itr_count; /* Number of iterators created for this pool */ |
56 | mu_nonlocal_jmp_t *jmp; /* Buffer for non-local exit */ | ||
56 | union mu_opool_bucket *bkt_head, *bkt_tail; | 57 | union mu_opool_bucket *bkt_head, *bkt_tail; |
57 | union mu_opool_bucket *bkt_fini; /* List of finished objects */ | 58 | union mu_opool_bucket *bkt_fini; /* List of finished objects */ |
58 | }; | 59 | }; |
... | @@ -65,6 +66,8 @@ alloc_bucket (struct _mu_opool *opool, size_t size) | ... | @@ -65,6 +66,8 @@ alloc_bucket (struct _mu_opool *opool, size_t size) |
65 | { | 66 | { |
66 | if (opool->flags & MU_OPOOL_ENOMEMABRT) | 67 | if (opool->flags & MU_OPOOL_ENOMEMABRT) |
67 | mu_alloc_die (); | 68 | mu_alloc_die (); |
69 | if (opool->jmp) | ||
70 | longjmp (opool->jmp->buf, ENOMEM); | ||
68 | } | 71 | } |
69 | else | 72 | else |
70 | { | 73 | { |
... | @@ -122,10 +125,30 @@ mu_opool_create (mu_opool_t *pret, int flags) | ... | @@ -122,10 +125,30 @@ mu_opool_create (mu_opool_t *pret, int flags) |
122 | x->bucket_size = MU_OPOOL_BUCKET_SIZE; | 125 | x->bucket_size = MU_OPOOL_BUCKET_SIZE; |
123 | x->itr_count = 0; | 126 | x->itr_count = 0; |
124 | x->bkt_head = x->bkt_tail = x->bkt_fini = NULL; | 127 | x->bkt_head = x->bkt_tail = x->bkt_fini = NULL; |
128 | x->jmp = NULL; | ||
125 | *pret = x; | 129 | *pret = x; |
126 | return 0; | 130 | return 0; |
127 | } | 131 | } |
128 | 132 | ||
133 | void | ||
134 | mu_opool_setjmp (mu_opool_t opool, mu_nonlocal_jmp_t *jmp) | ||
135 | { | ||
136 | if (jmp) | ||
137 | { | ||
138 | jmp->next = opool->jmp; | ||
139 | opool->jmp = jmp; | ||
140 | } | ||
141 | else | ||
142 | mu_opool_clrjmp (opool); | ||
143 | } | ||
144 | |||
145 | void | ||
146 | mu_opool_clrjmp (mu_opool_t opool) | ||
147 | { | ||
148 | if (opool->jmp) | ||
149 | opool->jmp = opool->jmp->next; | ||
150 | } | ||
151 | |||
129 | int | 152 | int |
130 | mu_opool_set_bucket_size (mu_opool_t opool, size_t size) | 153 | mu_opool_set_bucket_size (mu_opool_t opool, size_t size) |
131 | { | 154 | { | ... | ... |
... | @@ -49,6 +49,7 @@ noinst_PROGRAMS = \ | ... | @@ -49,6 +49,7 @@ noinst_PROGRAMS = \ |
49 | fsaf\ | 49 | fsaf\ |
50 | fsaftomod\ | 50 | fsaftomod\ |
51 | fsfolder\ | 51 | fsfolder\ |
52 | globtest\ | ||
52 | imapio\ | 53 | imapio\ |
53 | listop\ | 54 | listop\ |
54 | mailcap\ | 55 | mailcap\ |
... | @@ -94,6 +95,7 @@ TESTSUITE_AT = \ | ... | @@ -94,6 +95,7 @@ TESTSUITE_AT = \ |
94 | fsfolder02.at\ | 95 | fsfolder02.at\ |
95 | hdrflt.at\ | 96 | hdrflt.at\ |
96 | htmlent.at\ | 97 | htmlent.at\ |
98 | globtest.at\ | ||
97 | imapio.at\ | 99 | imapio.at\ |
98 | inline-comment.at\ | 100 | inline-comment.at\ |
99 | linecon.at\ | 101 | linecon.at\ | ... | ... |
libmailutils/tests/globtest.at
0 → 100644
1 | # This file is part of GNU Mailutils. -*- Autotest -*- | ||
2 | # Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | # | ||
4 | # GNU Mailutils is free software; you can redistribute it and/or | ||
5 | # modify it under the terms of the GNU General Public License as | ||
6 | # published by the Free Software Foundation; either version 3, or (at | ||
7 | # your option) any later version. | ||
8 | # | ||
9 | # GNU Mailutils is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | # General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License | ||
15 | # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. | ||
16 | |||
17 | AT_BANNER([Globbing patterns]) | ||
18 | |||
19 | dnl ------------------------------------------------------------------ | ||
20 | dnl GLOBCOMP (pattern, regex, [FLAGS]) | ||
21 | m4_pushdef([GLOBCOMP], | ||
22 | [AT_SETUP([[$1][]m4_foreach([flag],[m4_shift(m4_shift($@))],[ flag])]) | ||
23 | AT_KEYWORDS([glob]) | ||
24 | AT_CHECK([globtest m4_foreach([flag],[m4_shift(m4_shift($@))],[dnl | ||
25 | m4_if(flag,[sub],[ -s],[icase],[ -i],[collapse],[ -c])]) '[$1]'],[0],[[^$2$] | ||
26 | ]) | ||
27 | AT_CLEANUP | ||
28 | ]) | ||
29 | dnl ------------------------------------------------------------------ | ||
30 | |||
31 | GLOBCOMP(abab, abab) | ||
32 | GLOBCOMP(a*c, a.*c) | ||
33 | GLOBCOMP(a*c?d, a(.*)c(.)d, sub) | ||
34 | GLOBCOMP(a***c, a.*c) | ||
35 | GLOBCOMP(a***c, a()()(.*)c, sub) | ||
36 | GLOBCOMP(a***c, a(.*)c, sub, collapse) | ||
37 | GLOBCOMP([{$|a$$], [\{\$\|a\$\$]) | ||
38 | GLOBCOMP([a[0-9A-Z]c], [a[0-9A-Z]c]) | ||
39 | GLOBCOMP([a[!a-z]c], [a[^a-z]c]) | ||
40 | GLOBCOMP([a[!]z@:>@], [a[^]z@:>@]) | ||
41 | GLOBCOMP([a@<:@cde], [a\@<:@cde]) | ||
42 | GLOBCOMP([a[@<:@ba]], [a[\@<:@ba]]) | ||
43 | GLOBCOMP([*.c], [.*\.c]) | ||
44 | GLOBCOMP([a\],[a\\]) | ||
45 | |||
46 | m4_popdef([GLOBCOMP]) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
libmailutils/tests/globtest.c
0 → 100644
1 | #include <config.h> | ||
2 | #include <mailutils/mailutils.h> | ||
3 | |||
4 | /* globtest PATTERN [WORD...] | ||
5 | */ | ||
6 | int | ||
7 | main (int argc, char **argv) | ||
8 | { | ||
9 | char *pattern = NULL; | ||
10 | int flags = 0; | ||
11 | int rc; | ||
12 | int i; | ||
13 | |||
14 | mu_set_program_name (argv[0]); | ||
15 | for (i = 1; i < argc; i++) | ||
16 | { | ||
17 | char *a = argv[i]; | ||
18 | if (strcmp (a, "-i") == 0) | ||
19 | flags |= MU_GLOBF_ICASE; | ||
20 | else if (strcmp (a, "-s") == 0) | ||
21 | flags |= MU_GLOBF_SUB; | ||
22 | else if (strcmp (a, "-c") == 0) | ||
23 | flags |= MU_GLOBF_COLLAPSE; | ||
24 | else if (strcmp (a, "--") == 0) | ||
25 | { | ||
26 | i++; | ||
27 | break; | ||
28 | } | ||
29 | else if (*a != '-') | ||
30 | break; | ||
31 | else | ||
32 | { | ||
33 | mu_error ("unknown option %s", a); | ||
34 | return 1; | ||
35 | } | ||
36 | } | ||
37 | |||
38 | if (i == argc) | ||
39 | { | ||
40 | mu_printf ("usage: %s [-ics] PATTERN [WORD...]\n", mu_program_name); | ||
41 | return 1; | ||
42 | } | ||
43 | |||
44 | pattern = argv[i++]; | ||
45 | |||
46 | if (i == argc) | ||
47 | { | ||
48 | char *regstr; | ||
49 | |||
50 | rc = mu_glob_to_regex (®str, pattern, flags); | ||
51 | if (rc) | ||
52 | { | ||
53 | mu_error ("convert: %s", mu_strerror (rc)); | ||
54 | return 1; | ||
55 | } | ||
56 | mu_printf ("%s\n", regstr); | ||
57 | free (regstr); | ||
58 | } | ||
59 | else | ||
60 | { | ||
61 | regex_t regex; | ||
62 | size_t nmatch = 0; | ||
63 | regmatch_t *matches = NULL; | ||
64 | |||
65 | rc = mu_glob_compile (®ex, pattern, flags); | ||
66 | if (rc) | ||
67 | { | ||
68 | mu_error ("compile: %s", mu_strerror (rc)); | ||
69 | return 1; | ||
70 | } | ||
71 | |||
72 | if (flags & MU_GLOBF_SUB) | ||
73 | { | ||
74 | nmatch = regex.re_nsub + 1; | ||
75 | matches = mu_calloc (nmatch, sizeof matches[0]); | ||
76 | } | ||
77 | |||
78 | for (; i < argc; i++) | ||
79 | { | ||
80 | char *a = argv[i]; | ||
81 | rc = regexec (®ex, a, nmatch, matches, 0); | ||
82 | mu_printf ("%s: %s\n", a, rc == 0 ? "OK" : "NO"); | ||
83 | if (flags & MU_GLOBF_SUB) | ||
84 | { | ||
85 | size_t j; | ||
86 | |||
87 | for (j = 0; j < nmatch; j++) | ||
88 | printf ("%02d: %.*s\n", j, | ||
89 | matches[j].rm_eo - matches[j].rm_so, | ||
90 | a + matches[j].rm_so); | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | return 0; | ||
95 | } |
-
Please register or sign in to post a comment