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, 08 Aug 2017 10:47:04 +0200
parents 3380c8fd9776
children a08ef0674d8e
files cmd.c cmd.h pwm.1.xml pwm.c pwm.h util.c util.h
diffstat 7 files changed, 113 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- 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);
--- 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 <guido+pwm@berhoerster.name>
+ * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
  *
  * 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
--- 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 @@
       <email>guido+pwm@berhoerster.name</email>
       <personblurb/>
     </author>
-    <date>7 August, 2017</date>
+    <date>8 August, 2017</date>
   </info>
   <refmeta>
     <refentrytitle>pwm</refentrytitle>
@@ -491,6 +491,20 @@
           </listitem>
         </varlistentry>
         <varlistentry>
+          <term>Display status messages</term>
+          <listitem>
+            <cmdsynopsis>
+              <command>status</command>
+            </cmdsynopsis>
+            <cmdsynopsis>
+              <command>t</command>
+              <sbr/>
+            </cmdsynopsis>
+            <para>Redisplay any error message from the previous command and
+            whether there are unsaved changes.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
           <term>Write database</term>
           <listitem>
             <cmdsynopsis>
--- 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);
--- 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 */
--- 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 <guido+pwm@berhoerster.name>
+ * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
  *
  * 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);
--- 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 <guido+pwm@berhoerster.name>
+ * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
  *
  * 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 */