projects/pwm

changeset 16:a07665727c19

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.
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Tue Aug 08 10:47:04 2017 +0200 (2017-08-08)
parents 3380c8fd9776
children a08ef0674d8e
files cmd.c cmd.h pwm.1.xml pwm.c pwm.h util.c util.h
line diff
     1.1 --- a/cmd.c	Mon Aug 07 19:11:56 2017 +0200
     1.2 +++ b/cmd.c	Tue Aug 08 10:47:04 2017 +0200
     1.3 @@ -82,6 +82,7 @@
     1.4  	CHARCLASS_GRAPH
     1.5  };
     1.6  
     1.7 +static enum cmd_return	cmd_status(struct pwm_ctx *, int, char *[]);
     1.8  static enum cmd_return	cmd_info(struct pwm_ctx *, int, char *[]);
     1.9  static enum cmd_return	cmd_list(struct pwm_ctx *, int, char *[]);
    1.10  static enum cmd_return	cmd_create(struct pwm_ctx *, int, char *[]);
    1.11 @@ -151,6 +152,8 @@
    1.12  };
    1.13  
    1.14  static struct cmd cmds[] = {
    1.15 +    { "t", "status", "status", "Redisplay an error message of the previous "
    1.16 +    "command and unsaved changes", cmd_status },
    1.17      { "i", "info", "info", "Show metadata information about the current file",
    1.18      cmd_info },
    1.19      { "ls", "list", "list [field~regex ...]", "List entries", cmd_list },
    1.20 @@ -214,6 +217,22 @@
    1.21  }
    1.22  
    1.23  static enum cmd_return
    1.24 +cmd_status(struct pwm_ctx *ctx, int argc, char *argv[])
    1.25 +{
    1.26 +	if (argc != 1) {
    1.27 +		return (CMD_USAGE);
    1.28 +	}
    1.29 +
    1.30 +	if (ctx->errmsg != NULL) {
    1.31 +		printf("%s\n", ctx->errmsg);
    1.32 +	}
    1.33 +	printf("There are%sunsaved changes\n", ctx->unsaved_changes ? " " :
    1.34 +	    " no ");
    1.35 +
    1.36 +	return (CMD_STATUS);
    1.37 +}
    1.38 +
    1.39 +static enum cmd_return
    1.40  cmd_info(struct pwm_ctx *ctx, int argc, char *argv[])
    1.41  {
    1.42  	struct metadata	*metadata;
    1.43 @@ -267,7 +286,7 @@
    1.44  	for (i = 1; i < argc; i++) {
    1.45  		type = parse_arg(argv[i], field_namev, '~', &value);
    1.46  		if (type == FIELD_UNKNOWN) {
    1.47 -			fprintf(stderr, "bad field name \"%s\"\n", argv[i]);
    1.48 +			pwm_err(ctx, "bad field name \"%s\"", argv[i]);
    1.49  			goto out;
    1.50  		}
    1.51  		if (value[0] == '\0') {
    1.52 @@ -291,7 +310,7 @@
    1.53  			repp = &url_re;
    1.54  			break;
    1.55  		default:
    1.56 -			fprintf(stderr, "bad field name \"%s\"\n", argv[i]);
    1.57 +			pwm_err(ctx, "bad field name \"%s\"", argv[i]);
    1.58  			goto out;
    1.59  		}
    1.60  
    1.61 @@ -305,8 +324,7 @@
    1.62  			errbuf_size = regerror(errcode, *repp, "", 0);
    1.63  			errbuf = xmalloc(errbuf_size);
    1.64  			regerror(errcode, *repp, errbuf, errbuf_size);
    1.65 -			fprintf(stderr, "bad regular expression \"%s\"\n",
    1.66 -			    errbuf);
    1.67 +			pwm_err(ctx, "bad regular expression \"%s\"", errbuf);
    1.68  			free(errbuf);
    1.69  
    1.70  			free(*repp);
    1.71 @@ -383,8 +401,7 @@
    1.72  	for (i = 1; i < argc; i++) {
    1.73  		type = parse_arg(argv[i], field_namev, '=', &value);
    1.74  		if (type == FIELD_UNKNOWN) {
    1.75 -			fprintf(stderr, "bad field assignment \"%s\"\n",
    1.76 -			    argv[i]);
    1.77 +			pwm_err(ctx, "bad field assignment \"%s\"", argv[i]);
    1.78  		}
    1.79  		if (value[0] == '\0') {
    1.80  			/* skip empty assignments */
    1.81 @@ -410,7 +427,7 @@
    1.82  			record.url = value;
    1.83  			break;
    1.84  		default:
    1.85 -			fprintf(stderr, "bad field name \"%s\"\n", argv[i]);
    1.86 +			pwm_err(ctx, "bad field name \"%s\"", argv[i]);
    1.87  			return (CMD_ERR);
    1.88  		}
    1.89  	}
    1.90 @@ -434,15 +451,14 @@
    1.91  	}
    1.92  
    1.93  	if (parse_id(argv[1], &id) != 0) {
    1.94 -		fprintf(stderr, "invalid id %s\n", argv[1]);
    1.95 +		pwm_err(ctx, "invalid id %s", argv[1]);
    1.96  		return (CMD_ERR);
    1.97  	}
    1.98  
    1.99  	for (i = 2; i < argc; i++) {
   1.100  		type = parse_arg(argv[i], field_namev, '=', &value);
   1.101  		if (type == FIELD_UNKNOWN) {
   1.102 -			fprintf(stderr, "bad field assignment \"%s\"\n",
   1.103 -			    argv[i]);
   1.104 +			pwm_err(ctx, "bad field assignment \"%s\"", argv[i]);
   1.105  			return (CMD_ERR);
   1.106  		}
   1.107  		if (value[0] == '\0') {
   1.108 @@ -469,7 +485,7 @@
   1.109  			record.url = value;
   1.110  			break;
   1.111  		default:
   1.112 -			fprintf(stderr, "bad field name \"%s\"\n", argv[i]);
   1.113 +			pwm_err(ctx, "bad field name \"%s\"", argv[i]);
   1.114  			return (CMD_ERR);
   1.115  		}
   1.116  	}
   1.117 @@ -510,8 +526,8 @@
   1.118  			x = strtol(value, &p, 10);
   1.119  			if ((errno != 0) || (*value == '\0') || (*p != '\0') ||
   1.120  			    (x > PWS3_MAX_PASSWORD_LEN) || (x <= 0)) {
   1.121 -				fprintf(stderr, "invalid password length "
   1.122 -				    "\"%s\"\n", argv[i]);
   1.123 +				pwm_err(ctx, "invalid password length \"%s\"",
   1.124 +				    argv[i]);
   1.125  				goto out;
   1.126  			}
   1.127  			password_len = x;
   1.128 @@ -521,8 +537,8 @@
   1.129  			x = strtol(value, &p, 10);
   1.130  			if ((errno != 0) || (*value == '\0') || (*p != ':') ||
   1.131  			    (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) {
   1.132 -				fprintf(stderr, "invalid minimum number of "
   1.133 -				    "characters \"%s\"\n", argv[i]);
   1.134 +				pwm_err(ctx, "invalid minimum number of "
   1.135 +				    "characters \"%s\"", argv[i]);
   1.136  				goto out;
   1.137  			}
   1.138  			chars_min = x;
   1.139 @@ -530,9 +546,8 @@
   1.140  			chars = ++p;
   1.141  			while (*p != '\0') {
   1.142  				if (!isascii(*p) || !isprint(*p)) {
   1.143 -					fprintf(stderr, "invalid character in "
   1.144 -					    "character group \"%s\"\n",
   1.145 -					    argv[i]);
   1.146 +					pwm_err(ctx, "invalid character in "
   1.147 +					    "character group \"%s\"", argv[i]);
   1.148  					goto out;
   1.149  				}
   1.150  				p++;
   1.151 @@ -550,16 +565,16 @@
   1.152  			x = strtol(value, &p, 10);
   1.153  			if ((errno != 0) || (*value == '\0') || (*p != ':') ||
   1.154  			    (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) {
   1.155 -				fprintf(stderr, "invalid minimum number of "
   1.156 -				    "characters \"%s\"\n", argv[i]);
   1.157 +				pwm_err(ctx, "invalid minimum number of "
   1.158 +				    "characters \"%s\"", argv[i]);
   1.159  				goto out;
   1.160  			}
   1.161  			chars_min = x;
   1.162  
   1.163  			charclass = parse_arg(++p, charclass_namev, '\0', NULL);
   1.164  			if (charclass < 0) {
   1.165 -				fprintf(stderr, "unknown character class "
   1.166 -				    "\"%s\"\n", argv[i]);
   1.167 +				pwm_err(ctx, "unknown character class \"%s\"",
   1.168 +				    argv[i]);
   1.169  				goto out;
   1.170  			}
   1.171  			chars = charclass_values[charclass];
   1.172 @@ -571,7 +586,7 @@
   1.173  			char_groupv_len++;
   1.174  			break;
   1.175  		default:
   1.176 -			fprintf(stderr, "invalid argument \"%s\"\n", argv[i]);
   1.177 +			pwm_err(ctx, "invalid argument \"%s\"", argv[i]);
   1.178  			retval = CMD_USAGE;
   1.179  			goto out;
   1.180  		}
   1.181 @@ -579,8 +594,8 @@
   1.182  
   1.183  	for (j = 0; j < char_groupv_len; j++) {
   1.184  		if (char_groupv[j].chars_min > password_len) {
   1.185 -			fprintf(stderr, "invalid minimum number of "
   1.186 -			    "characters \"%zu:%s\"\n", char_groupv[j].chars_min,
   1.187 +			pwm_err(ctx, "invalid minimum number of characters "
   1.188 +			    "\"%zu:%s\"", char_groupv[j].chars_min,
   1.189  			    char_groupv[j].chars);
   1.190  			goto out;
   1.191  		}
   1.192 @@ -596,15 +611,15 @@
   1.193  
   1.194  	if (pw_genrandom(char_groupv, char_groupv_len, password,
   1.195  	    password_len) != 0) {
   1.196 -		fprintf(stderr, "failed to generate password that meets the "
   1.197 -		    "given constraints\n");
   1.198 +		pwm_err(ctx, "failed to generate password that meets the given "
   1.199 +		    "constraints");
   1.200  		goto out;
   1.201  	}
   1.202  
   1.203  	if (id != 0) {
   1.204  		if (pwfile_modify_record(ctx, id,
   1.205  		    &(struct record){ .password = password }) != 0) {
   1.206 -			fprintf(stderr, "record %u does not exist\n", id);
   1.207 +			pwm_err(ctx, "record %u does not exist", id);
   1.208  			goto out;
   1.209  		}
   1.210  	} else {
   1.211 @@ -629,12 +644,12 @@
   1.212  	}
   1.213  
   1.214  	if (parse_id(argv[1], &id) != 0) {
   1.215 -		fprintf(stderr, "invalid id %s\n", argv[1]);
   1.216 +		pwm_err(ctx, "invalid id %s", argv[1]);
   1.217  		return (CMD_ERR);
   1.218  	}
   1.219  
   1.220  	if (pwfile_remove_record(ctx, id) != 0) {
   1.221 -		fprintf(stderr, "failed to remove record %u\n", id);
   1.222 +		pwm_err(ctx, "failed to remove record %u", id);
   1.223  		return (CMD_ERR);
   1.224  	}
   1.225  
   1.226 @@ -736,7 +751,7 @@
   1.227  	}
   1.228  
   1.229  	if (parse_id(argv[1], &id) != 0) {
   1.230 -		fprintf(stderr, "invalid id %s\n", argv[1]);
   1.231 +		pwm_err(ctx, "invalid id %s", argv[1]);
   1.232  		return (CMD_ERR);
   1.233  	}
   1.234  
   1.235 @@ -748,7 +763,7 @@
   1.236  	for (i = 2; i < argc; i++) {
   1.237  		type = parse_arg(argv[i], field_namev, '\0', NULL);
   1.238  		if (type < 0) {
   1.239 -			fprintf(stderr, "bad field name \"%s\"\n", argv[i]);
   1.240 +			pwm_err(ctx, "bad field name \"%s\"", argv[i]);
   1.241  			return (CMD_ERR);
   1.242  		}
   1.243  		fields[type] = 1;
   1.244 @@ -756,7 +771,7 @@
   1.245  
   1.246  	record = pwfile_get_record(ctx, id);
   1.247  	if (record == NULL) {
   1.248 -		fprintf(stderr, "record %u does not exist\n", id);
   1.249 +		pwm_err(ctx, "record %u does not exist", id);
   1.250  		return (CMD_ERR);
   1.251  	}
   1.252  	print_record(record, fields, 1, stdout);
   1.253 @@ -780,13 +795,13 @@
   1.254  	}
   1.255  
   1.256  	if (parse_id(argv[1], &id) != 0) {
   1.257 -		fprintf(stderr, "invalid id %s\n", argv[1]);
   1.258 +		pwm_err(ctx, "invalid id %s", argv[1]);
   1.259  		return (CMD_ERR);
   1.260  	}
   1.261  
   1.262  	type = parse_arg(argv[2], field_namev, '\0', NULL);
   1.263  	if (type < 0) {
   1.264 -		fprintf(stderr, "bad field name \"%s\"\n", argv[2]);
   1.265 +		pwm_err(ctx, "bad field name \"%s\"", argv[2]);
   1.266  		return (CMD_ERR);
   1.267  	}
   1.268  	fields[type] = 1;
   1.269 @@ -799,7 +814,7 @@
   1.270  
   1.271  	record = pwfile_get_record(ctx, id);
   1.272  	if (record == NULL) {
   1.273 -		fprintf(stderr, "record %u does not exist\n", id);
   1.274 +		pwm_err(ctx, "record %u does not exist", id);
   1.275  		goto out;
   1.276  	}
   1.277  
   1.278 @@ -824,7 +839,7 @@
   1.279  	}
   1.280  
   1.281  	if (pwfile_create_group(ctx, argv[1]) != 0) {
   1.282 -		fprintf(stderr, "group \"%s\" already exists\n", argv[1]);
   1.283 +		pwm_err(ctx, "group \"%s\" already exists", argv[1]);
   1.284  		return (CMD_ERR);
   1.285  	}
   1.286  
   1.287 @@ -839,7 +854,7 @@
   1.288  	}
   1.289  
   1.290  	if (pwfile_remove_group(ctx, argv[1]) != 0) {
   1.291 -		fprintf(stderr, "there is no empty group \"%s\"\n", argv[1]);
   1.292 +		pwm_err(ctx, "empty group \"%s\" does not exist", argv[1]);
   1.293  		return (CMD_ERR);
   1.294  	}
   1.295  
   1.296 @@ -858,10 +873,10 @@
   1.297  	} else if (argc == 2) {
   1.298  		password_len = strlen(argv[1]);
   1.299  		if (password_len == 0) {
   1.300 -			fprintf(stderr, "password must not be empty\n");
   1.301 +			pwm_err(ctx, "password must not be empty");
   1.302  			return (CMD_ERR);
   1.303  		} else if (password_len + 1 > sizeof (ctx->password)) {
   1.304 -			fprintf(stderr, "password too long\n");
   1.305 +			pwm_err(ctx, "password too long");
   1.306  			return (CMD_ERR);
   1.307  		}
   1.308  		memcpy(ctx->password, argv[1], password_len + 1);
   1.309 @@ -873,7 +888,7 @@
   1.310  		}
   1.311  		password_len = strlen(password_buf);
   1.312  		if (password_len == 0) {
   1.313 -			fprintf(stderr, "password must not be empty\n");
   1.314 +			pwm_err(ctx, "password must not be empty");
   1.315  			return (CMD_ERR);
   1.316  		}
   1.317  		if (readpassphrase("Confirm password: ", confirm_buf,
   1.318 @@ -882,7 +897,7 @@
   1.319  			err(1, "readpassphrase");
   1.320  		}
   1.321  		if (strcmp(password_buf, confirm_buf) != 0) {
   1.322 -			fprintf(stderr, "passwords do not match\n");
   1.323 +			pwm_err(ctx, "passwords do not match");
   1.324  			return (CMD_ERR);
   1.325  		}
   1.326  		memcpy(ctx->password, password_buf, password_len + 1);
     2.1 --- a/cmd.h	Mon Aug 07 19:11:56 2017 +0200
     2.2 +++ b/cmd.h	Tue Aug 08 10:47:04 2017 +0200
     2.3 @@ -1,5 +1,5 @@
     2.4  /*
     2.5 - * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     2.6 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
     2.7   *
     2.8   * Permission is hereby granted, free of charge, to any person obtaining
     2.9   * a copy of this software and associated documentation files (the
    2.10 @@ -28,6 +28,7 @@
    2.11  
    2.12  enum cmd_return {
    2.13  	CMD_OK,
    2.14 +	CMD_STATUS,
    2.15  	CMD_ERR,
    2.16  	CMD_USAGE,
    2.17  	CMD_QUIT
     3.1 --- a/pwm.1.xml	Mon Aug 07 19:11:56 2017 +0200
     3.2 +++ b/pwm.1.xml	Tue Aug 08 10:47:04 2017 +0200
     3.3 @@ -34,7 +34,7 @@
     3.4        <email>guido+pwm@berhoerster.name</email>
     3.5        <personblurb/>
     3.6      </author>
     3.7 -    <date>7 August, 2017</date>
     3.8 +    <date>8 August, 2017</date>
     3.9    </info>
    3.10    <refmeta>
    3.11      <refentrytitle>pwm</refentrytitle>
    3.12 @@ -491,6 +491,20 @@
    3.13            </listitem>
    3.14          </varlistentry>
    3.15          <varlistentry>
    3.16 +          <term>Display status messages</term>
    3.17 +          <listitem>
    3.18 +            <cmdsynopsis>
    3.19 +              <command>status</command>
    3.20 +            </cmdsynopsis>
    3.21 +            <cmdsynopsis>
    3.22 +              <command>t</command>
    3.23 +              <sbr/>
    3.24 +            </cmdsynopsis>
    3.25 +            <para>Redisplay any error message from the previous command and
    3.26 +            whether there are unsaved changes.</para>
    3.27 +          </listitem>
    3.28 +        </varlistentry>
    3.29 +        <varlistentry>
    3.30            <term>Write database</term>
    3.31            <listitem>
    3.32              <cmdsynopsis>
     4.1 --- a/pwm.c	Mon Aug 07 19:11:56 2017 +0200
     4.2 +++ b/pwm.c	Tue Aug 08 10:47:04 2017 +0200
     4.3 @@ -57,6 +57,24 @@
     4.4  	fprintf(stderr, "usage: %s [-P file] [filename]\n", getprogname());
     4.5  }
     4.6  
     4.7 +void
     4.8 +pwm_err(struct pwm_ctx *ctx, char *fmt, ...)
     4.9 +{
    4.10 +	va_list	args;
    4.11 +
    4.12 +	free(ctx->errmsg);
    4.13 +
    4.14 +	if (fmt != NULL) {
    4.15 +		va_start(args, fmt);
    4.16 +		xvasprintf(&ctx->errmsg, fmt, args);
    4.17 +		va_end(args);
    4.18 +
    4.19 +		fprintf(stderr, "%s\n", ctx->errmsg);
    4.20 +	} else {
    4.21 +		ctx->errmsg = NULL;
    4.22 +	}
    4.23 +}
    4.24 +
    4.25  static int
    4.26  run_input_loop(struct pwm_ctx *ctx, int is_interactive)
    4.27  {
    4.28 @@ -125,7 +143,7 @@
    4.29  		/* find and execute the command */
    4.30  		cmd = cmd_match(argv[0]);
    4.31  		if (cmd == NULL) {
    4.32 -			fprintf(stderr, "unknown command: %s\n", argv[0]);
    4.33 +			pwm_err(ctx, "unknown command: %s", argv[0]);
    4.34  			if (is_interactive) {
    4.35  				goto next;
    4.36  			} else {
    4.37 @@ -134,6 +152,7 @@
    4.38  		}
    4.39  		switch (cmd->cmd_func(ctx, argc, argv)) {
    4.40  		case CMD_OK:
    4.41 +			pwm_err(ctx, NULL); /* clear error */
    4.42  			break;
    4.43  		case CMD_USAGE:
    4.44  			fprintf(stderr, "usage: %s\n", cmd->usage);
    4.45 @@ -145,12 +164,9 @@
    4.46  		case CMD_QUIT:
    4.47  			goto quit;
    4.48  		}
    4.49 +		ctx->prev_cmd = cmd->full_cmd;
    4.50  
    4.51  next:
    4.52 -		if (cmd != NULL) {
    4.53 -			ctx->prev_cmd = cmd->full_cmd;
    4.54 -		}
    4.55 -
    4.56  		for (i = 0; i < argc; i++) {
    4.57  			free(argv[i]);
    4.58  		}
    4.59 @@ -366,6 +382,7 @@
    4.60  		fclose(fp);
    4.61  	}
    4.62  	free(ctx.filename);
    4.63 +	free(ctx.errmsg);
    4.64  	free(pwm_dirname);
    4.65  
    4.66  	exit(status);
     5.1 --- a/pwm.h	Mon Aug 07 19:11:56 2017 +0200
     5.2 +++ b/pwm.h	Tue Aug 08 10:47:04 2017 +0200
     5.3 @@ -28,6 +28,7 @@
     5.4  
     5.5  struct pwm_ctx {
     5.6  	const char	*prev_cmd;
     5.7 +	char		*errmsg;
     5.8  	char		*filename;
     5.9  	struct pws3_file *file;
    5.10  	struct record_id_tree *record_id_tree;
    5.11 @@ -36,4 +37,6 @@
    5.12  	char		password[PWS3_MAX_PASSWORD_LEN + 1];
    5.13  };
    5.14  
    5.15 +void	pwm_err(struct pwm_ctx *, char *, ...);
    5.16 +
    5.17  #endif /* PWM_H */
     6.1 --- a/util.c	Mon Aug 07 19:11:56 2017 +0200
     6.2 +++ b/util.c	Tue Aug 08 10:47:04 2017 +0200
     6.3 @@ -1,5 +1,5 @@
     6.4  /*
     6.5 - * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     6.6 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
     6.7   *
     6.8   * Permission is hereby granted, free of charge, to any person obtaining
     6.9   * a copy of this software and associated documentation files (the
    6.10 @@ -69,14 +69,22 @@
    6.11  }
    6.12  
    6.13  char *
    6.14 +xvasprintf(char **strp, const char *fmt, va_list args)
    6.15 +{
    6.16 +	if (vasprintf(strp, fmt, args) < 0) {
    6.17 +		err(1, "vasprintf");
    6.18 +	}
    6.19 +
    6.20 +	return (*strp);
    6.21 +}
    6.22 +
    6.23 +char *
    6.24  xasprintf(char **strp, const char *fmt, ...)
    6.25  {
    6.26  	va_list	args;
    6.27  
    6.28  	va_start(args, fmt);
    6.29 -	if (vasprintf(strp, fmt, args) < 0) {
    6.30 -		err(1, "vasprintf");
    6.31 -	}
    6.32 +	xvasprintf(strp, fmt, args);
    6.33  	va_end(args);
    6.34  
    6.35  	return (*strp);
     7.1 --- a/util.h	Mon Aug 07 19:11:56 2017 +0200
     7.2 +++ b/util.h	Tue Aug 08 10:47:04 2017 +0200
     7.3 @@ -1,5 +1,5 @@
     7.4  /*
     7.5 - * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     7.6 + * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
     7.7   *
     7.8   * Permission is hereby granted, free of charge, to any person obtaining
     7.9   * a copy of this software and associated documentation files (the
    7.10 @@ -38,6 +38,7 @@
    7.11  void *	xmalloc(size_t);
    7.12  void *	xrealloc(void *, size_t);
    7.13  char *	xstrdup(const char *);
    7.14 +char *	xvasprintf(char **, const char *, va_list);
    7.15  char *	xasprintf(char **, const char *, ...);
    7.16  
    7.17  #endif /* UTIL_H */