Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ add_subdirectory(crc)
add_subdirectory(crypto)
add_subdirectory(fifo)
add_subdirectory(fsm)
add_subdirectory(glob)
add_subdirectory(guards)
add_subdirectory(hashmap)
add_subdirectory(hashtbl)
Expand Down
2 changes: 2 additions & 0 deletions examples/glob/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-or-later
/glob
21 changes: 21 additions & 0 deletions examples/glob/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright(c) 2023 John Sanpe <sanpeqf@gmail.com>
#

add_executable(glob utils.c)
target_link_libraries(glob bfdev)

if(${CMAKE_PROJECT_NAME} STREQUAL "bfdev")
install(FILES
utils.c
DESTINATION
${CMAKE_INSTALL_DOCDIR}/examples/glob
)

install(TARGETS
glob
DESTINATION
${CMAKE_INSTALL_DOCDIR}/bin
)
endif()
31 changes: 31 additions & 0 deletions examples/glob/utils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright(c) 2025 John Sanpe <sanpeqf@gmail.com>
*/

#define MODULE_NAME "bfdev-glob"
#define bfdev_log_fmt(fmt) MODULE_NAME ": " fmt

#include <bfdev/log.h>
#include <bfdev/glob.h>

int
main(int argc, const char *argv[])
{
unsigned int count;
const char *patten;
bool result;

if (argc < 2) {
bfdev_log_alert("Usage: %s patten string ...\n", argv[0]);
return 1;
}

patten = argv[1];
for (count = 2; count < argc; ++count) {
result = bfdev_glob(patten, argv[count]);
bfdev_log_info("matching %s: %s\n", argv[count], result ? "yes" : "no");
}

return 0;
}
20 changes: 20 additions & 0 deletions include/bfdev/glob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
* Copyright(c) 2025 John Sanpe <sanpeqf@gmail.com>
*/

#ifndef _BFDEV_GLOB_H_
#define _BFDEV_GLOB_H_

#include <bfdev/config.h>
#include <bfdev/types.h>
#include <bfdev/stddef.h>

BFDEV_BEGIN_DECLS

extern bool
bfdev_glob(const char *patten, const char *string);

BFDEV_END_DECLS

#endif /* _BFDEV_GLOB_H_ */
1 change: 1 addition & 0 deletions src/build.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(BFDEV_SOURCE
${CMAKE_CURRENT_LIST_DIR}/errname.c
${CMAKE_CURRENT_LIST_DIR}/fifo.c
${CMAKE_CURRENT_LIST_DIR}/fsm.c
${CMAKE_CURRENT_LIST_DIR}/glob.c
${CMAKE_CURRENT_LIST_DIR}/hashmap.c
${CMAKE_CURRENT_LIST_DIR}/heap.c
${CMAKE_CURRENT_LIST_DIR}/ilist.c
Expand Down
83 changes: 83 additions & 0 deletions src/glob.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: LGPL-3.0-or-later */
/*
* Copyright(c) 2025 John Sanpe <sanpeqf@gmail.com>
*/

#include <base.h>
#include <bfdev/glob.h>
#include <export.h>

export bool
bfdev_glob(const char *patten, const char *string)
{
const char *class, *bpatten, *bstring;
char ptch, stch, tcha, tchb;
bool match, inverted;

bpatten = NULL;
bstring = NULL;

for (;;) {
ptch = *patten++;
stch = *string++;

switch (ptch) {
case '?':
if (stch == '\0')
return false;
break;

case '*':
if (*patten == '\0')
return true;
bpatten = patten;
bstring = --string;
break;

case '[':
match = false;
inverted = *patten == '!';
class = patten + inverted;
tcha = *class++;

do {
tchb = tcha;
if (tcha == '\0')
goto literal;

if (class[0] == '-' && class[1] != ']') {
tchb = class[1];
if (tchb == '\0')
goto literal;
class += 2;
}
match |= tcha <= stch && stch <= tchb;
} while ((tcha = *class++) != ']');

if (match == inverted)
goto backtrack;

patten = class;
break;

case '\\':
ptch = *patten++;
bfdev_fallthrough;

default: literal:
if (ptch == stch) {
if (ptch == '\0')
return true;
break;
}

backtrack:
if (stch == '\0' || !bpatten)
return false;

patten = bpatten;
string = ++bstring;
break;
}
}
}
1 change: 1 addition & 0 deletions testsuite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ include(testsuite.cmake)
add_subdirectory(array)
add_subdirectory(bitwalk)
add_subdirectory(fifo)
add_subdirectory(glob)
add_subdirectory(hlist)
add_subdirectory(list)
add_subdirectory(memalloc)
Expand Down
2 changes: 2 additions & 0 deletions testsuite/glob/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-or-later
/fifo-selftest
16 changes: 16 additions & 0 deletions testsuite/glob/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Copyright(c) 2024 John Sanpe <sanpeqf@gmail.com>
#

add_executable(glob-selftest selftest.c)
target_link_libraries(glob-selftest bfdev testsuite)
add_test(glob-selftest glob-selftest)

if(${CMAKE_PROJECT_NAME} STREQUAL "bfdev")
install(TARGETS
glob-selftest
DESTINATION
${CMAKE_INSTALL_DOCDIR}/testsuite
)
endif()
120 changes: 120 additions & 0 deletions testsuite/glob/selftest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* SPDX-License-Identifier GPL-2.0-or-later */
/*
* Copyright(c) 2022-2024 John Sanpe <sanpeqf@gmail.com>
*/

#define MODULE_NAME "bitwalk-selftest"
#define bfdev_log_fmt(fmt) MODULE_NAME ":" fmt

#include <bfdev/glob.h>
#include <bfdev/macro.h>
#include <bfdev/log.h>
#include <testsuite.h>

struct glob_test {
const char *glob;
const char *string;
bool result;
};

static const struct glob_test
test_glob[] = {
/* Some basic tests */
{"a", "a", true},
{"a", "b", false},
{"a", "aa", false},
{"a", "", false},
{"", "", true},
{"", "a", false},

/* Simple character class tests */
{"[a]", "a", true},
{"[a]", "b", false},
{"[!a]", "a", false},
{"[!a]", "b", true},
{"[ab]", "a", true},
{"[ab]", "b", true},
{"[ab]", "c", false},
{"[!ab]", "c", true},
{"[a-c]", "b", true},
{"[a-c]", "d", false},

/* Corner cases in character class parsing */
{"[a-c-e-g]", "-", true},
{"[a-c-e-g]", "d", false},
{"[a-c-e-g]", "f", true},
{"[]a-ceg-ik[]", "a", true},
{"[]a-ceg-ik[]", "]", true},
{"[]a-ceg-ik[]", "[", true},
{"[]a-ceg-ik[]", "h", true},
{"[]a-ceg-ik[]", "f", false},
{"[!]a-ceg-ik[]", "h", false},
{"[!]a-ceg-ik[]", "]", false},
{"[!]a-ceg-ik[]", "f", true},

/* Simple wild cards */
{"?", "a", true},
{"?", "aa", false},
{"??", "a", false},
{"?x?", "axb", true},
{"?x?", "abx", false},
{"?x?", "xab", false},

/* Asterisk wild cards (backtracking) */
{"*??", "a", false},
{"*??", "ab", true},
{"*??", "abc", true},
{"*??", "abcd", true},
{"??*", "a", false},
{"??*", "ab", true},
{"??*", "abc", true},
{"??*", "abcd", true},
{"?*?", "a", false},
{"?*?", "ab", true},
{"?*?", "abc", true},
{"?*?", "abcd", true},
{"*b", "b", true},
{"*b", "ab", true},
{"*b", "ba", false},
{"*b", "bb", true},
{"*b", "abb", true},
{"*b", "bab", true},
{"*bc", "abbc", true},
{"*bc", "bc", true},
{"*bc", "bbc", true},
{"*bc", "bcbc", true},

/* Multiple asterisks (complex backtracking) */
{"*ac*", "abacadaeafag", true},
{"*ac*ae*ag*", "abacadaeafag", true},
{"*a*b*[bc]*[ef]*g*", "abacadaeafag", true},
{"*a*b*[ef]*[cd]*g*", "abacadaeafag", false},
{"*abcd*", "abcabcabcabcdefg", true},
{"*ab*cd*", "abcabcabcabcdefg", true},
{"*abcd*abcdef*", "abcabcdabcdeabcdefg", true},
{"*abcd*", "abcabcabcabcefg", false},
{"*ab*cd*", "abcabcabcabcefg", false},
};

TESTSUITE(
"glob:selftest", NULL, NULL,
"glob selftest"
) {
unsigned int count;

for (count = 0; count < BFDEV_ARRAY_SIZE(test_glob); ++count) {
const struct glob_test *test = test_glob + count;
bool matched;

matched = bfdev_glob(test->glob, test->string);
bfdev_log_info("match '%s' with '%s': result %d\n",
test->string, test->glob, matched);

if (matched != test->result) {
bfdev_log_err("result should be %d\n", test->result);
return -BFDEV_EFAULT;
}
}

return -BFDEV_ENOERR;
}
Loading