Coverage Report

Created: 2026-08-31 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/augeas/src/regexp.c
Line
Count
Source
1
/*
2
 * regexp.c:
3
 *
4
 * Copyright (C) 2007-2016 David Lutterkort
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but 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 library; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19
 *
20
 * Author: David Lutterkort <dlutter@redhat.com>
21
 */
22
23
#include <config.h>
24
#include <regex.h>
25
26
#include "internal.h"
27
#include "syntax.h"
28
#include "memory.h"
29
#include "errcode.h"
30
31
static const struct string empty_pattern_string = {
32
    .ref = REF_MAX, .str = (char *) "()"
33
};
34
35
static const struct string *const empty_pattern = &empty_pattern_string;
36
37
0
char *regexp_escape(const struct regexp *r) {
38
0
    char *pat = NULL;
39
40
0
    if (r == NULL)
41
0
        return strdup("");
42
43
#if !HAVE_USELOCALE
44
    char *nre = NULL;
45
    int ret;
46
    size_t nre_len;
47
48
    /* Use a range with from > to to force conversion of ranges into
49
     * short form */
50
    ret = fa_restrict_alphabet(r->pattern->str, strlen(r->pattern->str),
51
                               &nre, &nre_len, 2, 1);
52
    if (ret == 0) {
53
        pat = escape(nre, nre_len, RX_ESCAPES);
54
        free(nre);
55
    }
56
#endif
57
58
0
    if (pat == NULL) {
59
        /* simplify the regexp by removing some artifacts of reserving
60
           chanaracters for internal purposes */
61
0
        if (index(r->pattern->str, RESERVED_FROM_CH)) {
62
0
            char *s = strdup(r->pattern->str);
63
0
            char *t = s;
64
0
            for (char *p = s; *p; p++) {
65
0
                if (STREQLEN(p, RESERVED_RANGE_RX, strlen(RESERVED_RANGE_RX))) {
66
                    /* Completely eliminate mentions of the reserved range */
67
0
                    p += strlen(RESERVED_RANGE_RX);
68
0
                } else if (STREQLEN(p,
69
0
                                    RESERVED_DOT_RX, strlen(RESERVED_DOT_RX))) {
70
                    /* Replace what amounts to a '.' by one */
71
0
                    p += strlen(RESERVED_DOT_RX);
72
0
                    *t++ = '.';
73
0
                }
74
0
                *t++ = *p;
75
0
            }
76
0
            *t = '\0';
77
0
            pat = escape(s, -1, RX_ESCAPES);
78
0
            free(s);
79
0
        } else {
80
0
            pat = escape(r->pattern->str, -1, RX_ESCAPES);
81
0
        }
82
0
    }
83
84
0
    if (pat == NULL)
85
0
        return NULL;
86
87
    /* Remove unneeded '()' from pat */
88
0
    for (int changed = 1; changed;) {
89
0
        changed = 0;
90
0
        for (char *p = pat; *p != '\0'; p++) {
91
0
            if (*p == '(' && p[1] == ')') {
92
0
                memmove(p, p+2, strlen(p+2)+1);
93
0
                changed = 1;
94
0
            }
95
0
        }
96
0
    }
97
98
0
    if (pat[0] == '(' && pat[strlen(pat)-1] == ')') {
99
0
        int level = 1;
100
0
        for (int i=1; i < strlen(pat)-1; i++) {
101
0
            if (pat[i] == '(')
102
0
                level += 1;
103
0
            if (pat[i] == ')')
104
0
                level -= 1;
105
0
            if (level == 0)
106
0
                break;
107
0
        }
108
0
        if (level == 1) {
109
0
            memmove(pat, pat+1, strlen(pat+1)+1);
110
0
            pat[strlen(pat)-1] = '\0';
111
0
        }
112
0
    }
113
114
0
    return pat;
115
0
}
116
117
0
void print_regexp(FILE *out, struct regexp *r) {
118
0
    if (r == NULL) {
119
0
        fprintf(out, "<NULL>");
120
0
        return;
121
0
    }
122
123
0
    fputc('/', out);
124
0
    if (r->pattern == NULL)
125
0
        fprintf(out, "%p", r);
126
0
    else {
127
0
        char *rx;
128
0
        size_t rx_len;
129
0
        fa_restrict_alphabet(r->pattern->str, strlen(r->pattern->str),
130
0
                             &rx, &rx_len, 2, 1);
131
0
        print_chars(out, rx, rx_len);
132
0
        FREE(rx);
133
0
    }
134
0
    fputc('/', out);
135
0
    if (r->nocase)
136
0
        fputc('i', out);
137
0
}
138
139
struct regexp *
140
118
make_regexp_unescape(struct info *info, const char *pat, int nocase) {
141
118
    char *p = unescape(pat, strlen(pat), NULL);
142
143
118
    if (p == NULL)
144
0
        return NULL;
145
118
    return make_regexp(info, p, nocase);
146
118
}
147
148
131
struct regexp *make_regexp(struct info *info, char *pat, int nocase) {
149
131
    struct regexp *regexp;
150
151
131
    make_ref(regexp);
152
131
    regexp->info = ref(info);
153
154
131
    make_ref(regexp->pattern);
155
131
    regexp->pattern->str = pat;
156
131
    regexp->nocase = nocase;
157
131
    return regexp;
158
131
}
159
160
/* Take a POSIX glob and turn it into a regexp. The regexp is constructed
161
 * by doing the following translations of characters in the string:
162
 *  * -> [^/]*
163
 *  ? -> [^/]
164
 *  leave characters escaped with a backslash alone
165
 *  escape any of ".|{}()+^$" with a backslash
166
 *
167
 * Note that that ignores some of the finer points of globs, like
168
 * complementation.
169
 */
170
0
struct regexp *make_regexp_from_glob(struct info *info, const char *glob) {
171
0
    static const char *const star = "[^/]*";
172
0
    static const char *const qmark = "[^/]";
173
0
    static const char *const special = ".|{}()+^$";
174
0
    int newlen = strlen(glob);
175
0
    char *pat = NULL;
176
177
0
    for (const char *s = glob; *s; s++) {
178
0
        if (*s == '\\' && *(s+1))
179
0
            s += 1;
180
0
        else if (*s == '*')
181
0
            newlen += strlen(star)-1;
182
0
        else if (*s == '?')
183
0
            newlen += strlen(qmark)-1;
184
0
        else if (strchr(special, *s) != NULL)
185
0
            newlen += 1;
186
0
    }
187
188
0
    if (ALLOC_N(pat, newlen + 1) < 0)
189
0
        return NULL;
190
191
0
    char *t = pat;
192
0
    for (const char *s = glob; *s; s++) {
193
0
        if (*s == '\\' && *(s+1)) {
194
0
            *t++ = *s++;
195
0
            *t++ = *s;
196
0
        } else if (*s == '*') {
197
0
            t = stpcpy(t, star);
198
0
        } else if (*s == '?') {
199
0
            t = stpcpy(t, qmark);
200
0
        } else if (strchr(special, *s) != NULL) {
201
0
            *t++ = '\\';
202
0
            *t++ = *s;
203
0
        } else {
204
0
            *t++ = *s;
205
0
        }
206
0
    }
207
208
0
    return make_regexp(info, pat, 0);
209
0
}
210
211
131
void free_regexp(struct regexp *regexp) {
212
131
    if (regexp == NULL)
213
0
        return;
214
131
    assert(regexp->ref == 0);
215
131
    unref(regexp->info, info);
216
131
    unref(regexp->pattern, string);
217
131
    if (regexp->re != NULL) {
218
98
        regfree(regexp->re);
219
98
        free(regexp->re);
220
98
    }
221
131
    free(regexp);
222
131
}
223
224
0
int regexp_is_empty_pattern(struct regexp *r) {
225
0
    for (char *s = r->pattern->str; *s; s++) {
226
0
        if (*s != '(' && *s != ')')
227
0
            return 0;
228
0
    }
229
0
    return 1;
230
0
}
231
232
0
struct regexp *make_regexp_literal(struct info *info, const char *text) {
233
0
    char *pattern, *p;
234
235
    /* Escape special characters in text since it should be taken
236
       literally */
237
0
    if (ALLOC_N(pattern, 2*strlen(text)+1) < 0)
238
0
        return NULL;
239
0
    p = pattern;
240
0
    for (const char *t = text; *t != '\0'; t++) {
241
0
        if ((*t == '\\') && t[1]) {
242
0
            *p++ = *t++;
243
0
            *p++ = *t;
244
0
        } else if (strchr(".|{}[]()+*?", *t) != NULL) {
245
0
            *p++ = '\\';
246
0
            *p++ = *t;
247
0
        } else {
248
0
            *p++ = *t;
249
0
        }
250
0
    }
251
0
    return make_regexp(info, pattern, 0);
252
0
}
253
254
struct regexp *
255
0
regexp_union(struct info *info, struct regexp *r1, struct regexp *r2) {
256
0
    struct regexp *r[2];
257
258
0
    r[0] = r1;
259
0
    r[1] = r2;
260
0
    return regexp_union_n(info, 2, r);
261
0
}
262
263
0
char *regexp_expand_nocase(struct regexp *r) {
264
0
    const char *p = r->pattern->str, *t;
265
0
    char *s = NULL;
266
0
    size_t len;
267
0
    int ret;
268
0
    int psub = 0, rsub = 0;
269
270
0
    if (! r->nocase)
271
0
        return strdup(p);
272
273
0
    ret = fa_expand_nocase(p, strlen(p), &s, &len);
274
0
    ERR_NOMEM(ret == REG_ESPACE, r->info);
275
0
    BUG_ON(ret != REG_NOERROR, r->info, NULL);
276
277
    /* Make sure that r->pattern->str and ret have the same number
278
     * of parentheses/groups, since our parser critically depends
279
     * on the fact that the regexp for a union/concat and those
280
     * of its children have groups that are in direct relation */
281
0
    for (t = p; *t; t++) if (*t == '(') psub += 1;
282
0
    for (t = s; *t; t++) if (*t == '(') rsub += 1;
283
0
    BUG_ON(psub < rsub, r->info, NULL);
284
0
    psub -= rsub;
285
0
    if (psub > 0) {
286
0
        char *adjusted = NULL, *a;
287
0
        if (ALLOC_N(adjusted, strlen(s) + 2*psub + 1) < 0)
288
0
            ERR_NOMEM(true, r->info);
289
0
        a = adjusted;
290
0
        for (int i=0; i < psub; i++) *a++ = '(';
291
0
        a = stpcpy(a, s);
292
0
        for (int i=0; i < psub; i++) *a++ = ')';
293
0
        free(s);
294
0
        s = adjusted;
295
0
    }
296
0
 error:
297
0
    return s;
298
0
}
299
300
static char *append_expanded(struct regexp *r, char **pat, char *p,
301
0
                             size_t *len) {
302
0
    char *expanded = NULL;
303
0
    size_t ofs = p - *pat;
304
0
    int ret;
305
306
0
    expanded = regexp_expand_nocase(r);
307
0
    ERR_BAIL(r->info);
308
309
0
    *len += strlen(expanded) - strlen(r->pattern->str);
310
311
0
    ret = REALLOC_N(*pat, *len);
312
0
    ERR_NOMEM(ret < 0, r->info);
313
314
0
    p = stpcpy(*pat + ofs, expanded);
315
0
 error:
316
0
    FREE(expanded);
317
0
    return p;
318
0
}
319
320
struct regexp *
321
13
regexp_union_n(struct info *info, int n, struct regexp **r) {
322
13
    size_t len = 0;
323
13
    char *pat = NULL, *p, *expanded = NULL;
324
13
    int nnocase = 0, npresent = 0;
325
326
59
    for (int i=0; i < n; i++)
327
46
        if (r[i] != NULL) {
328
33
            len += strlen(r[i]->pattern->str) + strlen("()|");
329
33
            npresent += 1;
330
33
            if (r[i]->nocase)
331
0
                nnocase += 1;
332
33
        }
333
334
13
    bool mixedcase = nnocase > 0 && nnocase < npresent;
335
336
13
    if (len == 0)
337
0
        return NULL;
338
339
13
    if (ALLOC_N(pat, len) < 0)
340
0
        return NULL;
341
342
13
    p = pat;
343
13
    int added = 0;
344
59
    for (int i=0; i < n; i++) {
345
46
        if (r[i] == NULL)
346
13
            continue;
347
33
        if (added > 0)
348
20
            *p++ = '|';
349
33
        *p++ = '(';
350
33
        if (mixedcase && r[i]->nocase) {
351
0
            p = append_expanded(r[i], &pat, p, &len);
352
0
            ERR_BAIL(r[i]->info);
353
33
        } else {
354
33
            p = stpcpy(p, r[i]->pattern->str);
355
33
        }
356
33
        *p++ = ')';
357
33
        added += 1;
358
33
    }
359
13
    *p = '\0';
360
13
    return make_regexp(info, pat, nnocase == npresent);
361
0
 error:
362
0
    FREE(expanded);
363
0
    FREE(pat);
364
0
    return NULL;
365
13
}
366
367
struct regexp *
368
0
regexp_concat(struct info *info, struct regexp *r1, struct regexp *r2) {
369
0
    struct regexp *r[2];
370
371
0
    r[0] = r1;
372
0
    r[1] = r2;
373
0
    return regexp_concat_n(info, 2, r);
374
0
}
375
376
struct regexp *
377
0
regexp_concat_n(struct info *info, int n, struct regexp **r) {
378
0
    size_t len = 0;
379
0
    char *pat = NULL, *p, *expanded = NULL;
380
0
    int nnocase = 0, npresent = 0;
381
382
0
    for (int i=0; i < n; i++)
383
0
        if (r[i] != NULL) {
384
0
            len += strlen(r[i]->pattern->str) + strlen("()");
385
0
            npresent += 1;
386
0
            if (r[i]->nocase)
387
0
                nnocase += 1;
388
0
        }
389
390
0
    bool mixedcase = nnocase > 0 && nnocase < npresent;
391
392
0
    if (len == 0)
393
0
        return NULL;
394
395
0
    len += 1;
396
0
    if (ALLOC_N(pat, len) < 0)
397
0
        return NULL;
398
399
0
    p = pat;
400
0
    for (int i=0; i < n; i++) {
401
0
        if (r[i] == NULL)
402
0
            continue;
403
0
        *p++ = '(';
404
0
        if (mixedcase && r[i]->nocase) {
405
0
            p = append_expanded(r[i], &pat, p, &len);
406
0
            ERR_BAIL(r[i]->info);
407
0
        } else {
408
0
            p = stpcpy(p, r[i]->pattern->str);
409
0
        }
410
0
        *p++ = ')';
411
0
    }
412
0
    *p = '\0';
413
0
    return make_regexp(info, pat, nnocase == npresent);
414
0
 error:
415
0
    FREE(expanded);
416
0
    FREE(pat);
417
0
    return NULL;
418
0
}
419
420
0
static struct fa *regexp_to_fa(struct regexp *r) {
421
0
    const char *p = r->pattern->str;
422
0
    int ret;
423
0
    struct fa *fa = NULL;
424
425
0
    ret = fa_compile(p, strlen(p), &fa);
426
0
    ERR_NOMEM(ret == REG_ESPACE, r->info);
427
0
    BUG_ON(ret != REG_NOERROR, r->info, NULL);
428
429
0
    if (r->nocase) {
430
0
        ret = fa_nocase(fa);
431
0
        ERR_NOMEM(ret < 0, r->info);
432
0
    }
433
0
    return fa;
434
435
0
 error:
436
0
    fa_free(fa);
437
0
    return NULL;
438
0
}
439
440
struct regexp *
441
0
regexp_minus(struct info *info, struct regexp *r1, struct regexp *r2) {
442
0
    struct regexp *result = NULL;
443
0
    struct fa *fa = NULL, *fa1 = NULL, *fa2 = NULL;
444
0
    int r;
445
0
    char *s = NULL;
446
0
    size_t s_len;
447
448
0
    fa1 = regexp_to_fa(r1);
449
0
    ERR_BAIL(r1->info);
450
451
0
    fa2 = regexp_to_fa(r2);
452
0
    ERR_BAIL(r2->info);
453
454
0
    fa = fa_minus(fa1, fa2);
455
0
    if (fa == NULL)
456
0
        goto error;
457
458
0
    r = fa_as_regexp(fa, &s, &s_len);
459
0
    if (r < 0)
460
0
        goto error;
461
462
0
    if (s == NULL) {
463
        /* FA is the empty set, which we can't represent as a regexp */
464
0
        goto error;
465
0
    }
466
467
0
    if (regexp_c_locale(&s, NULL) < 0)
468
0
        goto error;
469
470
0
    result = make_regexp(info, s, fa_is_nocase(fa));
471
0
    s = NULL;
472
473
0
 done:
474
0
    fa_free(fa);
475
0
    fa_free(fa1);
476
0
    fa_free(fa2);
477
0
    free(s);
478
0
    return result;
479
0
 error:
480
0
    unref(result, regexp);
481
0
    goto done;
482
0
}
483
484
485
struct regexp *
486
0
regexp_iter(struct info *info, struct regexp *r, int min, int max) {
487
0
    const char *p;
488
0
    char *s;
489
0
    int ret = 0;
490
491
0
    if (r == NULL)
492
0
        return NULL;
493
494
0
    p = r->pattern->str;
495
0
    if ((min == 0 || min == 1) && max == -1) {
496
0
        char q = (min == 0) ? '*' : '+';
497
0
        ret = asprintf(&s, "(%s)%c", p, q);
498
0
    } else if (min == max) {
499
0
        ret = asprintf(&s, "(%s){%d}", p, min);
500
0
    } else {
501
0
        ret = asprintf(&s, "(%s){%d,%d}", p, min, max);
502
0
    }
503
0
    return (ret == -1) ? NULL : make_regexp(info, s, r->nocase);
504
0
}
505
506
struct regexp *
507
0
regexp_maybe(struct info *info, struct regexp *r) {
508
0
    const char *p;
509
0
    char *s;
510
0
    int ret;
511
512
0
    if (r == NULL)
513
0
        return NULL;
514
0
    p = r->pattern->str;
515
0
    ret = asprintf(&s, "(%s)?", p);
516
0
    return (ret == -1) ? NULL : make_regexp(info, s, r->nocase);
517
0
}
518
519
0
struct regexp *regexp_make_empty(struct info *info) {
520
0
    struct regexp *regexp;
521
522
0
    make_ref(regexp);
523
0
    if (regexp != NULL) {
524
0
        regexp->info = ref(info);
525
        /* Casting away the CONST for EMPTY_PATTERN is ok since it
526
           is protected against changes because REF == REF_MAX */
527
0
        regexp->pattern = (struct string *) empty_pattern;
528
0
        regexp->nocase = 0;
529
0
    }
530
0
    return regexp;
531
0
}
532
533
103
static int regexp_compile_internal(struct regexp *r, const char **c) {
534
    /* See the GNU regex manual or regex.h in gnulib for
535
     * an explanation of these flags. They are set so that the regex
536
     * matcher interprets regular expressions the same way that libfa
537
     * does
538
     */
539
103
    static const reg_syntax_t syntax =
540
103
        RE_CONTEXT_INDEP_OPS|RE_CONTEXT_INVALID_OPS|RE_DOT_NOT_NULL
541
103
        |RE_INTERVALS|RE_NO_BK_BRACES|RE_NO_BK_PARENS|RE_NO_BK_REFS
542
103
        |RE_NO_BK_VBAR|RE_NO_EMPTY_RANGES
543
103
        |RE_NO_POSIX_BACKTRACKING|RE_CONTEXT_INVALID_DUP|RE_NO_GNU_OPS;
544
103
    reg_syntax_t old_syntax = re_syntax_options;
545
546
103
    *c = NULL;
547
548
103
    if (r->re == NULL) {
549
98
        if (ALLOC(r->re) < 0)
550
0
            return -1;
551
98
    }
552
553
103
    re_syntax_options = syntax;
554
103
    if (r->nocase)
555
0
        re_syntax_options |= RE_ICASE;
556
103
    *c = re_compile_pattern(r->pattern->str, strlen(r->pattern->str), r->re);
557
103
    re_syntax_options = old_syntax;
558
559
103
    r->re->regs_allocated = REGS_REALLOCATE;
560
103
    if (*c != NULL)
561
10
        return -1;
562
93
    return 0;
563
103
}
564
565
98
int regexp_compile(struct regexp *r) {
566
98
    const char *c;
567
568
98
    return regexp_compile_internal(r, &c);
569
98
}
570
571
5
int regexp_check(struct regexp *r, const char **msg) {
572
5
    return regexp_compile_internal(r, msg);
573
5
}
574
575
int regexp_match(struct regexp *r,
576
                 const char *string, const int size,
577
57
                 const int start, struct re_registers *regs) {
578
57
    if (r->re == NULL) {
579
0
        if (regexp_compile(r) == -1)
580
0
            return -3;
581
0
    }
582
57
    return re_match(r->re, string, size, start, regs);
583
57
}
584
585
0
int regexp_matches_empty(struct regexp *r) {
586
0
    return regexp_match(r, "", 0, 0, NULL) == 0;
587
0
}
588
589
0
int regexp_nsub(struct regexp *r) {
590
0
    if (r->re == NULL)
591
0
        if (regexp_compile(r) == -1)
592
0
            return -1;
593
0
    return r->re->re_nsub;
594
0
}
595
596
0
void regexp_release(struct regexp *regexp) {
597
0
    if (regexp != NULL && regexp->re != NULL) {
598
0
        regfree(regexp->re);
599
        FREE(regexp->re);
600
0
    }
601
0
}
602
603
/*
604
 * Local variables:
605
 *  indent-tabs-mode: nil
606
 *  c-indent-level: 4
607
 *  c-basic-offset: 4
608
 *  tab-width: 4
609
 * End:
610
 */