From ccc33a57176fb21e8dad21c910c687b9809f72bd Mon Sep 17 00:00:00 2001 From: Shlomi Fish Date: Sat, 23 Jun 2012 19:01:33 +0300 Subject: [PATCH] Enhance "make xconfig". Signed-off-by: Shlomi Fish This is a modified version of this patch: http://www.shlomifish.org/open-source/projects/linux-kernel/xconfig-search/ with the main changes being that most of the warnings and errors reported by checkpatch.pl were fixed. This patch adds substring, keyword search and regular expression search to the "make xconfig" Edit -> Find dialog in the kernel - all with case-sensitive vs. case-insensitive options. --- scripts/kconfig/Makefile | 12 +-- scripts/kconfig/keywords_search.c | 173 ++++++++++++++++++++++++++++++++++++ scripts/kconfig/keywords_search.h | 36 ++++++++ scripts/kconfig/lkc.h | 14 +++ scripts/kconfig/qconf.cc | 44 +++++++++- scripts/kconfig/qconf.h | 10 +++ scripts/kconfig/symbol.c | 179 ++++++++++++++++++++++++++++++++++++-- 7 files changed, 455 insertions(+), 13 deletions(-) create mode 100644 scripts/kconfig/keywords_search.c create mode 100644 scripts/kconfig/keywords_search.h diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 7966265..bae941d 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -140,13 +140,13 @@ HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) \ lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o -conf-objs := conf.o zconf.tab.o -mconf-objs := mconf.o zconf.tab.o $(lxdialog) -nconf-objs := nconf.o zconf.tab.o nconf.gui.o -kxgettext-objs := kxgettext.o zconf.tab.o +conf-objs := conf.o zconf.tab.o keywords_search.o +mconf-objs := mconf.o zconf.tab.o keywords_search.o $(lxdialog) +nconf-objs := nconf.o zconf.tab.o keywords_search.o nconf.gui.o +kxgettext-objs := kxgettext.o zconf.tab.o keywords_search.o qconf-cxxobjs := qconf.o -qconf-objs := zconf.tab.o -gconf-objs := gconf.o zconf.tab.o +qconf-objs := zconf.tab.o keywords_search.o +gconf-objs := gconf.o zconf.tab.o keywords_search.o hostprogs-y := conf diff --git a/scripts/kconfig/keywords_search.c b/scripts/kconfig/keywords_search.c new file mode 100644 index 0000000..63bb971 --- /dev/null +++ b/scripts/kconfig/keywords_search.c @@ -0,0 +1,173 @@ +/* + * Keywords' Search Micro-Library. + * + * Copyrighted by Shlomi Fish, 2007. + * + * Licensed under the MIT/X11 License: + * + * http://www.opensource.org/licenses/mit-license.php + * + * */ +#include "keywords_search.h" +#include +#include +#include + +#define is_word_char(c) (isalpha(c) || isdigit(c)) + +static int add_word(keywords_search_handle_t *h, const char **p) +{ + const char *start; + keywords_search_keyword_t *word; + + start = *p; + while (is_word_char(**p)) + (*p)++; + + if (h->num_words == h->max_num_words) { + void *new_ptr; + h->max_num_words += 16; + new_ptr = realloc(h->words, + sizeof(h->words[0]) * h->max_num_words + ); + if (new_ptr == NULL) + return 0; + h->words = new_ptr; + } + word = &(h->words[h->num_words++]); + word->found = 0; + word->word = + malloc(sizeof(word->word[0]) * + (word->word_len = (*p)-start)+1 + ); + if (!word->word) + return 0; + memcpy(word->word, start, word->word_len); + word->word[word->word_len] = '\0'; + + return 1; +} + +keywords_search_handle_t *keywords_search_compile(const char *pattern) +{ + int i; + keywords_search_handle_t *ret = NULL; + const char *char_ptr; + + ret = malloc(sizeof(*ret)); + if (!ret) + return NULL; + + ret->num_words = 0; + ret->max_num_words = 0; + ret->num_found_words = 0; + ret->words = NULL; + + char_ptr = pattern; + + while (*char_ptr) { + if (is_word_char(*char_ptr)) { + if (!add_word(ret, &char_ptr)) + goto cleanup_words; + } else + char_ptr++; + } + + return ret; + +cleanup_words: + for (i = 0; i < ret->num_words; i++) { + free(ret->words[i].word); + ret->words[i].word = NULL; + } + free(ret->words); + ret->words = NULL; + + free(ret); + ret = NULL; + + return NULL; +} + +static int +cmp_word(keywords_search_handle_t *handle, keywords_search_keyword_t *word, + const char *start, const int len) +{ + if (word->found || (word->word_len != len)) + return 0; + + if (handle->case_sensitivity) + return !strncmp(word->word, start, len); + else + return !strncasecmp(word->word, start, len); +} + +static int +find_word(keywords_search_handle_t *handle, const char *start, const int len) +{ + int i; + keywords_search_keyword_t *word; + + word = handle->words; + for (i = 0; i < handle->num_words; i++, word++) + if (cmp_word(handle, word, start, len)) { + word->found = 1; + if (++handle->num_found_words == handle->num_words) + return 1; + } + return 0; +} + +int keywords_search_matches(keywords_search_handle_t *handle, + const char *string, int case_sensitivity) +{ + const char *char_ptr, *start; + + char_ptr = string; + + handle->case_sensitivity = case_sensitivity; + + if (handle->num_found_words == handle->num_words) + return 1; + + /* Handle the NULL string gracefully. */ + if (!string) + return 0; + + while (*char_ptr) + if (is_word_char(*char_ptr)) { + int len; + + start = char_ptr; + while (is_word_char(*char_ptr)) + char_ptr++; + + len = char_ptr - start; + + if (find_word(handle, start, len)) + return 1; + } else + char_ptr++; + + return 0; +} + +void keywords_search_reset(keywords_search_handle_t *handle) +{ + int i; + + /* Reset the state of the handle to no words found. */ + handle->num_found_words = 0; + for (i = 0; i < handle->num_words; i++) + handle->words[i].found = 0; +} + +void keywords_search_free(keywords_search_handle_t *handle) +{ + int i; + + for (i = 0; i < handle->num_words; i++) + free(handle->words[i].word); + free(handle->words); + free(handle); +} diff --git a/scripts/kconfig/keywords_search.h b/scripts/kconfig/keywords_search.h new file mode 100644 index 0000000..962d5e0 --- /dev/null +++ b/scripts/kconfig/keywords_search.h @@ -0,0 +1,36 @@ +/* + * Keywords' Search Micro-Library. + * + * Copyrighted by Shlomi Fish, 2007. + * + * Licensed under the MIT/X11 License: + * + * http://www.opensource.org/licenses/mit-license.php + * + * */ +#ifndef KEYWORDS_SEARCH_H +#define KEYWORDS_SEARCH_H + +typedef struct { + int found; + char *word; + int word_len; +} keywords_search_keyword_t; + +typedef struct { + int num_words; + int max_num_words; + int num_found_words; + keywords_search_keyword_t *words; + /* The case sensitivity flag - valid only for a search session. */ + int case_sensitivity; +} keywords_search_handle_t; + +keywords_search_handle_t *keywords_search_compile(const char *pattern); +int keywords_search_matches(keywords_search_handle_t *handle, + const char *string, int case_sensitivity); +void keywords_search_reset(keywords_search_handle_t *handle); +void keywords_search_free(keywords_search_handle_t *handle); + +#endif /* KEYWORDS_SEARCH_H */ + diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index c18f2bd..1a3209f 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -147,6 +147,20 @@ struct property *prop_alloc(enum prop_type type, struct symbol *sym); struct symbol *prop_get_symbol(struct property *prop); struct property *sym_get_env_prop(struct symbol *sym); +typedef int (*sym_search_filter_t)(struct symbol *sym, void *context); +struct symbol **sym_generic_search(sym_search_filter_t filter, void *context); +struct symbol **sym_pattern_search(const char *pattern, unsigned int flags); + +enum SYM_PATTERN_SEARCH_FLAGS { + SYM_PATTERN_SEARCH_REGEX = 0x0, + SYM_PATTERN_SEARCH_SUBSTRING = 0x1, + SYM_PATTERN_SEARCH_KEYWORDS_AND = 0x2, + SYM_PATTERN_SEARCH_TYPE_MASK = 0xF, + SYM_PATTERN_CASE_SENSITIVE = 0x10, + SYM_PATTERN_CASE_INSENSITIVE = 0x00, + SYM_PATTERN_CASE_MASK = 0x10, +}; + static inline tristate sym_get_tristate_value(struct symbol *sym) { return sym->curr.tri; diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index df274fe..e3daa9b 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -1196,6 +1197,36 @@ ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *nam layout2->addWidget(searchButton); layout1->addLayout(layout2); + // Initialize the "Search Type" button group. + searchType = new Q3VButtonGroup("Search Type", this, "searchType"); + + searchType->insert(new QRadioButton("Substring Match", searchType), + SYM_PATTERN_SEARCH_SUBSTRING + ); + + searchType->insert(new QRadioButton("Keywords", searchType), + SYM_PATTERN_SEARCH_KEYWORDS_AND + ); + + searchType->insert(new QRadioButton("Regular Expression", searchType), + SYM_PATTERN_SEARCH_REGEX + ); + + searchType->setButton(SYM_PATTERN_SEARCH_SUBSTRING); + layout1->addWidget(searchType); + + // Initialize the "Options" button group. + options = new Q3VButtonGroup("Options", this, "Options"); + + caseSensitivity = new QCheckBox( + "Case sensitive", options, "Case sensitive" + ); + + caseSensitivity->setChecked(FALSE); + + layout1->addWidget(options); + + // Initialize the ConfigView and ConfigInfoView split = new QSplitter(this); split->setOrientation(Qt::Vertical); list = new ConfigView(split, name); @@ -1242,6 +1273,17 @@ void ConfigSearchWindow::saveSettings(void) } } +unsigned int ConfigSearchWindow::getSearchFlags() +{ + return (searchType->selectedId() + | + (caseSensitivity->isChecked() + ? SYM_PATTERN_CASE_SENSITIVE + : SYM_PATTERN_CASE_INSENSITIVE + ) + ); +} + void ConfigSearchWindow::search(void) { struct symbol **p; @@ -1252,7 +1294,7 @@ void ConfigSearchWindow::search(void) list->list->clear(); info->clear(); - result = sym_re_search(editField->text().latin1()); + result = sym_pattern_search(editField->text(), getSearchFlags()); if (!result) return; for (p = result; *p; p++) { diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 3715b3e..b6765c7 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -5,9 +5,12 @@ #if QT_VERSION < 0x040000 #include +#include #else #include +#include #endif +#include #include #if QT_VERSION < 0x040000 @@ -22,6 +25,7 @@ #define Q3ToolBar QToolBar #define Q3ListViewItemIterator QListViewItemIterator #define Q3FileDialog QFileDialog +#define Q3VButtonGroup QVButtonGroup #endif class ConfigView; @@ -278,6 +282,7 @@ protected: bool _showDebug; }; +enum SEARCH_TYPE { SUBSTRING, KEYWORDS, REGEX }; class ConfigSearchWindow : public QDialog { Q_OBJECT typedef class QDialog Parent; @@ -291,11 +296,15 @@ public slots: protected: QLineEdit* editField; QPushButton* searchButton; + Q3VButtonGroup *searchType, *options; + QCheckBox *caseSensitivity; QSplitter* split; ConfigView* list; ConfigInfoView* info; struct symbol **result; + + unsigned int getSearchFlags(); }; class ConfigMainWindow : public Q3MainWindow { @@ -335,3 +344,4 @@ protected: QSplitter* split1; QSplitter* split2; }; + diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 22a3c40..6e5d683 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -9,6 +9,7 @@ #include #include +#include "keywords_search.h" #include "lkc.h" struct symbol symbol_yes = { @@ -943,23 +944,190 @@ const char *sym_escape_string_value(const char *in) return res; } +static int re_search_filter(struct symbol *sym, void *re_void) +{ + return (regexec((regex_t *)re_void, sym->name, 0, NULL, 0) == 0); +} + + +/* The old function that only searches through sym->name. */ struct symbol **sym_re_search(const char *pattern) { - struct symbol *sym, **sym_arr = NULL; - int i, cnt, size; regex_t re; + struct symbol **results; - cnt = size = 0; - /* Skip if empty */ if (strlen(pattern) == 0) return NULL; if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE)) return NULL; + results = sym_generic_search(re_search_filter, (void *)&re); + + regfree(&re); + + return results; +} + +typedef struct substring_search_context_struct { + const char *substring; + char *(*find_func)(const char *, const char *); +} substring_search_context_t; + +#define MATCH_SYMBOL() \ + (MATCH(sym->name) || \ + (sym->prop && \ + ((sym->prop->text && MATCH(sym->prop->text)) || \ + (sym->prop->menu && sym->prop->menu->help && \ + MATCH(sym->prop->menu->help))) \ + )); + +static int substring_filter(struct symbol *sym, void *context_void) +{ + substring_search_context_t *context; + context = (substring_search_context_t *)context_void; + +#define MATCH(string) \ + (context->find_func((string), context->substring)) + + return MATCH_SYMBOL(); +#undef MATCH +} + +/* + * A not-so-efficient (but should be good enough) implementation of the + * GNU strcasestr extension. This is done because strcasestr is not portable + * and the compiler yells at us for using it. + * */ +static char *my_strcasestr(const char *haystack, const char *needle) +{ + size_t needle_len; + char *start_from; + + needle_len = strlen(needle); + start_from = (char *)haystack; + + while (*start_from) { + if (!strncasecmp(start_from, needle, needle_len)) + return start_from; + + start_from++; + } + + return NULL; +} + +static struct symbol **substring_search(const char *substring, int case_sense) +{ + substring_search_context_t context; + + context.substring = substring; + context.find_func = case_sense ? strstr: my_strcasestr; + + return sym_generic_search(substring_filter, (void *)&context); +} + +typedef struct { + keywords_search_handle_t *h; + int case_sense; +} keywords_search_context_t; + +static int keywords_filter(struct symbol *sym, void *context_void) +{ + keywords_search_context_t *context; + context = (keywords_search_context_t *)context_void; + + keywords_search_reset(context->h); + +#define MATCH(string) \ + (keywords_search_matches(context->h, string, context->case_sense)) + + return MATCH_SYMBOL(); +#undef MATCH +} + +static struct symbol **keywords_search(const char *query, int case_sense) +{ + struct symbol **results; + keywords_search_context_t context; + + context.h = keywords_search_compile(query); + if (!context.h) + return NULL; + context.case_sense = case_sense; + + results = sym_generic_search(keywords_filter, (void *)&context); + keywords_search_free(context.h); + + return results; +} + + +static int re_search2_filter(struct symbol *sym, void *re_void) +{ + regex_t *re = (regex_t *)re_void; + +#define MATCH(string) \ + (regexec(re, (string), 0, NULL, 0) == 0) + + return MATCH_SYMBOL(); +#undef MATCH +} + +static struct symbol **sym_re_search2(const char *pattern, int case_sense) +{ + regex_t re; + struct symbol **results; + int rc_flags; + + rc_flags = case_sense ? 0: REG_ICASE; + + if (strlen(pattern) == 0) + return NULL; + if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|rc_flags)) + return NULL; + + results = sym_generic_search(re_search2_filter, (void *)&re); + + regfree(&re); + + return results; +} + + +#undef MATCH_SYMBOL + +#define is_case_sensitive(flags) \ + (((flags) & SYM_PATTERN_CASE_MASK) == SYM_PATTERN_CASE_SENSITIVE) + +struct symbol **sym_pattern_search(const char *pattern, unsigned int flags) +{ + unsigned int type; + int case_sense; + + type = (flags & SYM_PATTERN_SEARCH_TYPE_MASK); + case_sense = is_case_sensitive(flags); + + if (type == SYM_PATTERN_SEARCH_REGEX) + return sym_re_search2(pattern, case_sense); + else if (type == SYM_PATTERN_SEARCH_SUBSTRING) + return substring_search(pattern, case_sense); + else if (type == SYM_PATTERN_SEARCH_KEYWORDS_AND) + return keywords_search(pattern, case_sense); + else + return NULL; +} + +struct symbol **sym_generic_search(sym_search_filter_t filter, void *context) +{ + struct symbol *sym, **sym_arr = NULL; + int i, cnt, size; + + cnt = size = 0; + for_all_symbols(i, sym) { if (sym->flags & SYMBOL_CONST || !sym->name) continue; - if (regexec(&re, sym->name, 0, NULL, 0)) + if (!filter(sym, context)) continue; if (cnt + 1 >= size) { void *tmp = sym_arr; @@ -975,7 +1143,6 @@ struct symbol **sym_re_search(const char *pattern) } if (sym_arr) sym_arr[cnt] = NULL; - regfree(&re); return sym_arr; } -- 1.7.11