/* * Copyright 2008-2013 Various Authors * Copyright 2005 Timo Hirvonen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "glob.h" #include "uchar.h" #include "list.h" #include "xmalloc.h" #include "debug.h" #include struct glob_item { struct list_head node; enum { GLOB_STAR, GLOB_QMARK, GLOB_TEXT } type; char text[]; }; /* simplification: * * ??*? => ???* * *?* => ?* * *? => ?* * ... */ static void simplify(struct list_head *head) { struct list_head *item; item = head->next; while (item != head) { struct list_head *i, *next; int qcount = 0; int scount = 0; i = item; do { struct glob_item *gi; gi = container_of(i, struct glob_item, node); if (gi->type == GLOB_STAR) { scount++; } else if (gi->type == GLOB_QMARK) { qcount++; } else { i = i->next; break; } i = i->next; } while (i != head); next = i; if (scount) { /* move all qmarks to front and * if there are >1 stars remove all but the last */ struct list_head *insert_after = item->prev; i = item; while (qcount) { struct glob_item *gi; gi = container_of(i, struct glob_item, node); i = i->next; if (gi->type == GLOB_QMARK) { list_del(&gi->node); list_add(&gi->node, insert_after); qcount--; } } i = item; while (scount > 1) { struct glob_item *gi; gi = container_of(i, struct glob_item, node); i = i->next; if (gi->type == GLOB_STAR) { list_del(&gi->node); free(gi); scount--; } } } item = next; } } void glob_compile(struct list_head *head, const char *pattern) { int i = 0; list_init(head); while (pattern[i]) { struct glob_item *item; if (pattern[i] == '*') { item = xnew(struct glob_item, 1); item->type = GLOB_STAR; i++; } else if (pattern[i] == '?') { item = xnew(struct glob_item, 1); item->type = GLOB_QMARK; i++; } else { int start, len, j; char *str; start = i; len = 0; while (pattern[i]) { if (pattern[i] == '\\') { i++; len++; if (pattern[i]) i++; } else if (pattern[i] == '*') { break; } else if (pattern[i] == '?') { break; } else { i++; len++; } } item = xmalloc(sizeof(struct glob_item) + len + 1); item->type = GLOB_TEXT; str = item->text; i = start; j = 0; while (j < len) { if (pattern[i] == '\\') { i++; if (pattern[i]) { str[j++] = pattern[i++]; } else { str[j++] = '\\'; } } else { str[j++] = pattern[i++]; } } str[j] = 0; } list_add_tail(&item->node, head); } simplify(head); } void glob_free(struct list_head *head) { struct list_head *item = head->next; while (item != head) { struct glob_item *gi; struct list_head *next = item->next; gi = container_of(item, struct glob_item, node); free(gi); item = next; } } static int do_glob_match(struct list_head *head, struct list_head *first, const char *text) { struct list_head *item = first; while (item != head) { struct glob_item *gitem; gitem = container_of(item, struct glob_item, node); if (gitem->type == GLOB_TEXT) { int len = u_strlen(gitem->text); if (!u_strncase_equal_base(gitem->text, text, len)) return 0; text += strlen(gitem->text); } else if (gitem->type == GLOB_QMARK) { uchar u; int idx = 0; u = u_get_char(text, &idx); if (u == 0) return 0; text += idx; } else if (gitem->type == GLOB_STAR) { /* after star there MUST be normal text (or nothing), * question marks have been moved before this star and * other stars have been stripped (see simplify) */ struct list_head *next; struct glob_item *next_gi; const char *t; int tlen; next = item->next; if (next == head) { /* this star was the last item => matched */ return 1; } next_gi = container_of(next, struct glob_item, node); BUG_ON(next_gi->type != GLOB_TEXT); t = next_gi->text; tlen = strlen(t); while (1) { const char *pos; pos = u_strcasestr_base(text, t); if (pos == NULL) return 0; if (do_glob_match(head, next->next, pos + tlen)) return 1; text = pos + 1; } } item = item->next; } return text[0] == 0; } int glob_match(struct list_head *head, const char *text) { return do_glob_match(head, head->next, text); }