# HG changeset patch # User Guido Berhoerster # Date 1501485621 -7200 # Node ID 85bce13237cfb2aadbb404b999a4b48b3241b71a # Parent 17fb30016e64fd8f527b50c7b9d4cbae81cf1777 Add filter expressions to list command Refactor field parsing so it can be used for parsing fields, field assignments and filter expressions. diff -r 17fb30016e64 -r 85bce13237cf cmd.c --- a/cmd.c Fri Jul 28 15:53:57 2017 +0200 +++ b/cmd.c Mon Jul 31 09:20:21 2017 +0200 @@ -31,6 +31,7 @@ #ifdef HAVE_READPASSPHRASE_H #include #endif /* READPASSPHRASE_H */ +#include #include #include #include @@ -94,7 +95,7 @@ static struct cmd cmds[] = { { "i", "info", "info", "Show metadata information about the current file", cmd_info }, - { "ls", "list", "list", "List entries", cmd_list }, + { "ls", "list", "list [field~regex ...]", "List entries", cmd_list }, { "c", "create", "create field=value ...", "Create entry", cmd_create }, { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify }, { "rm", "remove", "remove id", "Delete entry", cmd_remove }, @@ -114,56 +115,17 @@ }; static enum field_type -parse_field_name(const char *name) -{ - int i; - - for (i = 0; i < (int)COUNTOF(field_names); i++) { - if (strcmp(field_names[i], name) == 0) { - return (i); - } - } - - return (FIELD_UNKNOWN); -} - -static enum field_type -parse_field_assignment(char *arg, struct record *record) +parse_field(char *field_arg, int sep, char **valuep) { int i; size_t field_name_len; - char *value; for (i = 0; i < (int)COUNTOF(field_names); i++) { field_name_len = strlen(field_names[i]); - if ((strncmp(field_names[i], arg, field_name_len) == 0) && - (arg[field_name_len] == '=')){ - value = arg + field_name_len + 1; - if (*value == '\0') { - /* skip empty assignments */ - return (i); - } - switch (i) { - case FIELD_GROUP: - record->group = value; - break; - case FIELD_TITLE: - record->title = value; - break; - case FIELD_USERNAME: - record->username = value; - break; - case FIELD_PASSWORD: - record->password = value; - break; - case FIELD_NOTES: - record->notes = value; - break; - case FIELD_URL: - record->url = value; - break; - default: - return (FIELD_UNKNOWN); + if ((strncmp(field_names[i], field_arg, field_name_len) == 0) && + (field_arg[field_name_len] == sep)) { + if (valuep != NULL) { + *valuep = field_arg + field_name_len + 1; } return (i); } @@ -223,26 +185,125 @@ static enum cmd_return cmd_list(struct pwm_ctx *ctx, int argc, char *argv[]) { - union list_item **list; - size_t i; + int retval = CMD_ERR; + int i; + regex_t *group_re = NULL; + regex_t *title_re = NULL; + regex_t *username_re = NULL; + regex_t *notes_re = NULL; + regex_t *url_re = NULL; + enum field_type type; + char *value; + regex_t **repp; + int errcode; + char *errbuf; + size_t errbuf_size; + union list_item **list = NULL; + size_t j; + struct record *record; - if (argc != 1) { - return (CMD_USAGE); + for (i = 1; i < argc; i++) { + type = parse_field(argv[i], '~', &value); + if (type == FIELD_UNKNOWN) { + fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + goto out; + } + if (value[0] == '\0') { + /* skip empty expressions */ + continue; + } + switch (type) { + case FIELD_GROUP: + repp = &group_re; + break; + case FIELD_TITLE: + repp = &title_re; + break; + case FIELD_USERNAME: + repp = &username_re; + break; + case FIELD_NOTES: + repp = ¬es_re; + break; + case FIELD_URL: + repp = &url_re; + break; + default: + fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + goto out; + } + + if (*repp == NULL) { + *repp = xmalloc(sizeof (regex_t)); + } else { + regfree(*repp); + } + errcode = regcomp(*repp, value, REG_EXTENDED | REG_NOSUB); + if (errcode != 0) { + errbuf_size = regerror(errcode, *repp, "", 0); + errbuf = xmalloc(errbuf_size); + regerror(errcode, *repp, errbuf, errbuf_size); + fprintf(stderr, "bad regular expression \"%s\"\n", + errbuf); + free(errbuf); + + free(*repp); + *repp = NULL; + + goto out; + } } list = pwfile_create_list(ctx); - for (i = 0; list[i] != NULL; i++) { - if (list[i]->any.type == ITEM_TYPE_GROUP) { - printf("[%s]\n", list[i]->group.group); + for (j = 0; list[j] != NULL; j++) { + if (list[j]->any.type == ITEM_TYPE_GROUP) { + printf("[%s]\n", list[j]->group.group); } else { - printf("%4u %s\n", list[i]->record.id, - (list[i]->record.title != NULL) ? - list[i]->record.title : ""); + record = pwfile_get_record(ctx, list[j]->record.id); + if (((group_re == NULL) || (regexec(group_re, + record->group, 0, NULL, 0) == 0)) && + ((title_re == NULL) || (regexec(title_re, + record->title, 0, NULL, 0) == 0)) && + ((username_re == NULL) || (regexec(username_re, + record->username, 0, NULL, 0) == 0)) && + ((notes_re == NULL) || (regexec(notes_re, + record->notes, 0, NULL, 0) == 0)) && + ((url_re == NULL) || (regexec(url_re, + record->url, 0, NULL, 0) == 0))) { + printf("%4u %s\n", list[j]->record.id, + (list[j]->record.title != NULL) ? + list[j]->record.title : ""); + } + pwfile_destroy_record(record); } } + retval = CMD_OK; + +out: + if (group_re != NULL) { + regfree(group_re); + free(group_re); + } + if (title_re != NULL) { + regfree(title_re); + free(title_re); + } + if (username_re != NULL) { + regfree(username_re); + free(username_re); + } + if (notes_re != NULL) { + regfree(notes_re); + free(notes_re); + } + if (url_re != NULL) { + regfree(url_re); + free(url_re); + } + pwfile_destroy_list(list); - return (CMD_OK); + return (retval); } static enum cmd_return @@ -250,15 +311,44 @@ { int i; struct record record = { 0 }; + enum field_type type; + char *value; if (argc < 2) { return (CMD_USAGE); } for (i = 1; i < argc; i++) { - if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) { + type = parse_field(argv[i], '=', &value); + if (type == FIELD_UNKNOWN) { fprintf(stderr, "bad field assignment \"%s\"\n", argv[i]); + } + if (value[0] == '\0') { + /* skip empty assignments */ + continue; + } + switch (type) { + case FIELD_GROUP: + record.group = value; + break; + case FIELD_TITLE: + record.title = value; + break; + case FIELD_USERNAME: + record.username = value; + break; + case FIELD_PASSWORD: + record.password = value; + break; + case FIELD_NOTES: + record.notes = value; + break; + case FIELD_URL: + record.url = value; + break; + default: + fprintf(stderr, "bad field name \"%s\"\n", argv[i]); return (CMD_ERR); } } @@ -273,7 +363,9 @@ { unsigned int id; int i; - struct record record = { 0 }; + struct record record = { 0 }; + enum field_type type; + char *value; if (argc < 2) { return (CMD_USAGE); @@ -285,11 +377,39 @@ } for (i = 2; i < argc; i++) { - if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) { + type = parse_field(argv[i], '=', &value); + if (type == FIELD_UNKNOWN) { fprintf(stderr, "bad field assignment \"%s\"\n", argv[i]); return (CMD_ERR); } + if (value[0] == '\0') { + /* skip empty assignments */ + continue; + } + switch (type) { + case FIELD_GROUP: + record.group = value; + break; + case FIELD_TITLE: + record.title = value; + break; + case FIELD_USERNAME: + record.username = value; + break; + case FIELD_PASSWORD: + record.password = value; + break; + case FIELD_NOTES: + record.notes = value; + break; + case FIELD_URL: + record.url = value; + break; + default: + fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + return (CMD_ERR); + } } pwfile_modify_record(ctx, id, &record); @@ -410,7 +530,7 @@ } for (i = 2; i < argc; i++) { - type = parse_field_name(argv[i]); + type = parse_field(argv[i], '\0', NULL); if (type < 0) { fprintf(stderr, "bad field name \"%s\"\n", argv[i]); return (CMD_ERR); @@ -448,7 +568,7 @@ return (CMD_ERR); } - type = parse_field_name(argv[2]); + type = parse_field(argv[2], '\0', NULL); if (type < 0) { fprintf(stderr, "bad field name \"%s\"\n", argv[2]); return (CMD_ERR); diff -r 17fb30016e64 -r 85bce13237cf pwm.1.xml --- a/pwm.1.xml Fri Jul 28 15:53:57 2017 +0200 +++ b/pwm.1.xml Mon Jul 31 09:20:21 2017 +0200 @@ -34,7 +34,7 @@ guido+pwm@berhoerster.name - 28 July, 2017 + 31 July, 2017 pwm @@ -201,12 +201,21 @@ list + + field~regex + ls + + field~regex + - List all password database entries. + List password database entries. If one or more filter + expressions are specified, limit the displayed entries to those + whose field content matches the extended + regular expression regex. @@ -529,6 +538,8 @@ 1, locale 5, + regex + 5,