push
This commit is contained in:
245
glob.c
Normal file
245
glob.c
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glob.h"
|
||||
#include "uchar.h"
|
||||
#include "list.h"
|
||||
#include "xmalloc.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user