# HG changeset patch # User Guido Berhoerster # Date 1502182024 -7200 # Node ID a07665727c19d411962c47bd42fb5af7cb6e2b0c # Parent 3380c8fd97766eac70314512686430ec2b72a4e9 Add status command Add status command to redisplay an error message of the previous command and unsaved changes. Add pwm_err function to display and save error messages. diff -r 3380c8fd9776 -r a07665727c19 cmd.c --- a/cmd.c Mon Aug 07 19:11:56 2017 +0200 +++ b/cmd.c Tue Aug 08 10:47:04 2017 +0200 @@ -82,6 +82,7 @@ CHARCLASS_GRAPH }; +static enum cmd_return cmd_status(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_info(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_list(struct pwm_ctx *, int, char *[]); static enum cmd_return cmd_create(struct pwm_ctx *, int, char *[]); @@ -151,6 +152,8 @@ }; static struct cmd cmds[] = { + { "t", "status", "status", "Redisplay an error message of the previous " + "command and unsaved changes", cmd_status }, { "i", "info", "info", "Show metadata information about the current file", cmd_info }, { "ls", "list", "list [field~regex ...]", "List entries", cmd_list }, @@ -214,6 +217,22 @@ } static enum cmd_return +cmd_status(struct pwm_ctx *ctx, int argc, char *argv[]) +{ + if (argc != 1) { + return (CMD_USAGE); + } + + if (ctx->errmsg != NULL) { + printf("%s\n", ctx->errmsg); + } + printf("There are%sunsaved changes\n", ctx->unsaved_changes ? " " : + " no "); + + return (CMD_STATUS); +} + +static enum cmd_return cmd_info(struct pwm_ctx *ctx, int argc, char *argv[]) { struct metadata *metadata; @@ -267,7 +286,7 @@ for (i = 1; i < argc; i++) { type = parse_arg(argv[i], field_namev, '~', &value); if (type == FIELD_UNKNOWN) { - fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + pwm_err(ctx, "bad field name \"%s\"", argv[i]); goto out; } if (value[0] == '\0') { @@ -291,7 +310,7 @@ repp = &url_re; break; default: - fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + pwm_err(ctx, "bad field name \"%s\"", argv[i]); goto out; } @@ -305,8 +324,7 @@ errbuf_size = regerror(errcode, *repp, "", 0); errbuf = xmalloc(errbuf_size); regerror(errcode, *repp, errbuf, errbuf_size); - fprintf(stderr, "bad regular expression \"%s\"\n", - errbuf); + pwm_err(ctx, "bad regular expression \"%s\"", errbuf); free(errbuf); free(*repp); @@ -383,8 +401,7 @@ for (i = 1; i < argc; i++) { type = parse_arg(argv[i], field_namev, '=', &value); if (type == FIELD_UNKNOWN) { - fprintf(stderr, "bad field assignment \"%s\"\n", - argv[i]); + pwm_err(ctx, "bad field assignment \"%s\"", argv[i]); } if (value[0] == '\0') { /* skip empty assignments */ @@ -410,7 +427,7 @@ record.url = value; break; default: - fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + pwm_err(ctx, "bad field name \"%s\"", argv[i]); return (CMD_ERR); } } @@ -434,15 +451,14 @@ } if (parse_id(argv[1], &id) != 0) { - fprintf(stderr, "invalid id %s\n", argv[1]); + pwm_err(ctx, "invalid id %s", argv[1]); return (CMD_ERR); } for (i = 2; i < argc; i++) { type = parse_arg(argv[i], field_namev, '=', &value); if (type == FIELD_UNKNOWN) { - fprintf(stderr, "bad field assignment \"%s\"\n", - argv[i]); + pwm_err(ctx, "bad field assignment \"%s\"", argv[i]); return (CMD_ERR); } if (value[0] == '\0') { @@ -469,7 +485,7 @@ record.url = value; break; default: - fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + pwm_err(ctx, "bad field name \"%s\"", argv[i]); return (CMD_ERR); } } @@ -510,8 +526,8 @@ x = strtol(value, &p, 10); if ((errno != 0) || (*value == '\0') || (*p != '\0') || (x > PWS3_MAX_PASSWORD_LEN) || (x <= 0)) { - fprintf(stderr, "invalid password length " - "\"%s\"\n", argv[i]); + pwm_err(ctx, "invalid password length \"%s\"", + argv[i]); goto out; } password_len = x; @@ -521,8 +537,8 @@ x = strtol(value, &p, 10); if ((errno != 0) || (*value == '\0') || (*p != ':') || (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) { - fprintf(stderr, "invalid minimum number of " - "characters \"%s\"\n", argv[i]); + pwm_err(ctx, "invalid minimum number of " + "characters \"%s\"", argv[i]); goto out; } chars_min = x; @@ -530,9 +546,8 @@ chars = ++p; while (*p != '\0') { if (!isascii(*p) || !isprint(*p)) { - fprintf(stderr, "invalid character in " - "character group \"%s\"\n", - argv[i]); + pwm_err(ctx, "invalid character in " + "character group \"%s\"", argv[i]); goto out; } p++; @@ -550,16 +565,16 @@ x = strtol(value, &p, 10); if ((errno != 0) || (*value == '\0') || (*p != ':') || (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) { - fprintf(stderr, "invalid minimum number of " - "characters \"%s\"\n", argv[i]); + pwm_err(ctx, "invalid minimum number of " + "characters \"%s\"", argv[i]); goto out; } chars_min = x; charclass = parse_arg(++p, charclass_namev, '\0', NULL); if (charclass < 0) { - fprintf(stderr, "unknown character class " - "\"%s\"\n", argv[i]); + pwm_err(ctx, "unknown character class \"%s\"", + argv[i]); goto out; } chars = charclass_values[charclass]; @@ -571,7 +586,7 @@ char_groupv_len++; break; default: - fprintf(stderr, "invalid argument \"%s\"\n", argv[i]); + pwm_err(ctx, "invalid argument \"%s\"", argv[i]); retval = CMD_USAGE; goto out; } @@ -579,8 +594,8 @@ for (j = 0; j < char_groupv_len; j++) { if (char_groupv[j].chars_min > password_len) { - fprintf(stderr, "invalid minimum number of " - "characters \"%zu:%s\"\n", char_groupv[j].chars_min, + pwm_err(ctx, "invalid minimum number of characters " + "\"%zu:%s\"", char_groupv[j].chars_min, char_groupv[j].chars); goto out; } @@ -596,15 +611,15 @@ if (pw_genrandom(char_groupv, char_groupv_len, password, password_len) != 0) { - fprintf(stderr, "failed to generate password that meets the " - "given constraints\n"); + pwm_err(ctx, "failed to generate password that meets the given " + "constraints"); goto out; } if (id != 0) { if (pwfile_modify_record(ctx, id, &(struct record){ .password = password }) != 0) { - fprintf(stderr, "record %u does not exist\n", id); + pwm_err(ctx, "record %u does not exist", id); goto out; } } else { @@ -629,12 +644,12 @@ } if (parse_id(argv[1], &id) != 0) { - fprintf(stderr, "invalid id %s\n", argv[1]); + pwm_err(ctx, "invalid id %s", argv[1]); return (CMD_ERR); } if (pwfile_remove_record(ctx, id) != 0) { - fprintf(stderr, "failed to remove record %u\n", id); + pwm_err(ctx, "failed to remove record %u", id); return (CMD_ERR); } @@ -736,7 +751,7 @@ } if (parse_id(argv[1], &id) != 0) { - fprintf(stderr, "invalid id %s\n", argv[1]); + pwm_err(ctx, "invalid id %s", argv[1]); return (CMD_ERR); } @@ -748,7 +763,7 @@ for (i = 2; i < argc; i++) { type = parse_arg(argv[i], field_namev, '\0', NULL); if (type < 0) { - fprintf(stderr, "bad field name \"%s\"\n", argv[i]); + pwm_err(ctx, "bad field name \"%s\"", argv[i]); return (CMD_ERR); } fields[type] = 1; @@ -756,7 +771,7 @@ record = pwfile_get_record(ctx, id); if (record == NULL) { - fprintf(stderr, "record %u does not exist\n", id); + pwm_err(ctx, "record %u does not exist", id); return (CMD_ERR); } print_record(record, fields, 1, stdout); @@ -780,13 +795,13 @@ } if (parse_id(argv[1], &id) != 0) { - fprintf(stderr, "invalid id %s\n", argv[1]); + pwm_err(ctx, "invalid id %s", argv[1]); return (CMD_ERR); } type = parse_arg(argv[2], field_namev, '\0', NULL); if (type < 0) { - fprintf(stderr, "bad field name \"%s\"\n", argv[2]); + pwm_err(ctx, "bad field name \"%s\"", argv[2]); return (CMD_ERR); } fields[type] = 1; @@ -799,7 +814,7 @@ record = pwfile_get_record(ctx, id); if (record == NULL) { - fprintf(stderr, "record %u does not exist\n", id); + pwm_err(ctx, "record %u does not exist", id); goto out; } @@ -824,7 +839,7 @@ } if (pwfile_create_group(ctx, argv[1]) != 0) { - fprintf(stderr, "group \"%s\" already exists\n", argv[1]); + pwm_err(ctx, "group \"%s\" already exists", argv[1]); return (CMD_ERR); } @@ -839,7 +854,7 @@ } if (pwfile_remove_group(ctx, argv[1]) != 0) { - fprintf(stderr, "there is no empty group \"%s\"\n", argv[1]); + pwm_err(ctx, "empty group \"%s\" does not exist", argv[1]); return (CMD_ERR); } @@ -858,10 +873,10 @@ } else if (argc == 2) { password_len = strlen(argv[1]); if (password_len == 0) { - fprintf(stderr, "password must not be empty\n"); + pwm_err(ctx, "password must not be empty"); return (CMD_ERR); } else if (password_len + 1 > sizeof (ctx->password)) { - fprintf(stderr, "password too long\n"); + pwm_err(ctx, "password too long"); return (CMD_ERR); } memcpy(ctx->password, argv[1], password_len + 1); @@ -873,7 +888,7 @@ } password_len = strlen(password_buf); if (password_len == 0) { - fprintf(stderr, "password must not be empty\n"); + pwm_err(ctx, "password must not be empty"); return (CMD_ERR); } if (readpassphrase("Confirm password: ", confirm_buf, @@ -882,7 +897,7 @@ err(1, "readpassphrase"); } if (strcmp(password_buf, confirm_buf) != 0) { - fprintf(stderr, "passwords do not match\n"); + pwm_err(ctx, "passwords do not match"); return (CMD_ERR); } memcpy(ctx->password, password_buf, password_len + 1); diff -r 3380c8fd9776 -r a07665727c19 cmd.h --- a/cmd.h Mon Aug 07 19:11:56 2017 +0200 +++ b/cmd.h Tue Aug 08 10:47:04 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Guido Berhoerster + * Copyright (C) 2017 Guido Berhoerster * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -28,6 +28,7 @@ enum cmd_return { CMD_OK, + CMD_STATUS, CMD_ERR, CMD_USAGE, CMD_QUIT diff -r 3380c8fd9776 -r a07665727c19 pwm.1.xml --- a/pwm.1.xml Mon Aug 07 19:11:56 2017 +0200 +++ b/pwm.1.xml Tue Aug 08 10:47:04 2017 +0200 @@ -34,7 +34,7 @@ guido+pwm@berhoerster.name - 7 August, 2017 + 8 August, 2017 pwm @@ -491,6 +491,20 @@ + Display status messages + + + status + + + t + + + Redisplay any error message from the previous command and + whether there are unsaved changes. + + + Write database diff -r 3380c8fd9776 -r a07665727c19 pwm.c --- a/pwm.c Mon Aug 07 19:11:56 2017 +0200 +++ b/pwm.c Tue Aug 08 10:47:04 2017 +0200 @@ -57,6 +57,24 @@ fprintf(stderr, "usage: %s [-P file] [filename]\n", getprogname()); } +void +pwm_err(struct pwm_ctx *ctx, char *fmt, ...) +{ + va_list args; + + free(ctx->errmsg); + + if (fmt != NULL) { + va_start(args, fmt); + xvasprintf(&ctx->errmsg, fmt, args); + va_end(args); + + fprintf(stderr, "%s\n", ctx->errmsg); + } else { + ctx->errmsg = NULL; + } +} + static int run_input_loop(struct pwm_ctx *ctx, int is_interactive) { @@ -125,7 +143,7 @@ /* find and execute the command */ cmd = cmd_match(argv[0]); if (cmd == NULL) { - fprintf(stderr, "unknown command: %s\n", argv[0]); + pwm_err(ctx, "unknown command: %s", argv[0]); if (is_interactive) { goto next; } else { @@ -134,6 +152,7 @@ } switch (cmd->cmd_func(ctx, argc, argv)) { case CMD_OK: + pwm_err(ctx, NULL); /* clear error */ break; case CMD_USAGE: fprintf(stderr, "usage: %s\n", cmd->usage); @@ -145,12 +164,9 @@ case CMD_QUIT: goto quit; } + ctx->prev_cmd = cmd->full_cmd; next: - if (cmd != NULL) { - ctx->prev_cmd = cmd->full_cmd; - } - for (i = 0; i < argc; i++) { free(argv[i]); } @@ -366,6 +382,7 @@ fclose(fp); } free(ctx.filename); + free(ctx.errmsg); free(pwm_dirname); exit(status); diff -r 3380c8fd9776 -r a07665727c19 pwm.h --- a/pwm.h Mon Aug 07 19:11:56 2017 +0200 +++ b/pwm.h Tue Aug 08 10:47:04 2017 +0200 @@ -28,6 +28,7 @@ struct pwm_ctx { const char *prev_cmd; + char *errmsg; char *filename; struct pws3_file *file; struct record_id_tree *record_id_tree; @@ -36,4 +37,6 @@ char password[PWS3_MAX_PASSWORD_LEN + 1]; }; +void pwm_err(struct pwm_ctx *, char *, ...); + #endif /* PWM_H */ diff -r 3380c8fd9776 -r a07665727c19 util.c --- a/util.c Mon Aug 07 19:11:56 2017 +0200 +++ b/util.c Tue Aug 08 10:47:04 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Guido Berhoerster + * Copyright (C) 2017 Guido Berhoerster * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -69,14 +69,22 @@ } char * +xvasprintf(char **strp, const char *fmt, va_list args) +{ + if (vasprintf(strp, fmt, args) < 0) { + err(1, "vasprintf"); + } + + return (*strp); +} + +char * xasprintf(char **strp, const char *fmt, ...) { va_list args; va_start(args, fmt); - if (vasprintf(strp, fmt, args) < 0) { - err(1, "vasprintf"); - } + xvasprintf(strp, fmt, args); va_end(args); return (*strp); diff -r 3380c8fd9776 -r a07665727c19 util.h --- a/util.h Mon Aug 07 19:11:56 2017 +0200 +++ b/util.h Tue Aug 08 10:47:04 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Guido Berhoerster + * Copyright (C) 2017 Guido Berhoerster * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -38,6 +38,7 @@ void * xmalloc(size_t); void * xrealloc(void *, size_t); char * xstrdup(const char *); +char * xvasprintf(char **, const char *, va_list); char * xasprintf(char **, const char *, ...); #endif /* UTIL_H */