comparison cmd.c @ 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
comparison
equal deleted inserted replaced
15:3380c8fd9776 16:a07665727c19
80 CHARCLASS_ALNUM, 80 CHARCLASS_ALNUM,
81 CHARCLASS_XDIGIT, 81 CHARCLASS_XDIGIT,
82 CHARCLASS_GRAPH 82 CHARCLASS_GRAPH
83 }; 83 };
84 84
85 static enum cmd_return cmd_status(struct pwm_ctx *, int, char *[]);
85 static enum cmd_return cmd_info(struct pwm_ctx *, int, char *[]); 86 static enum cmd_return cmd_info(struct pwm_ctx *, int, char *[]);
86 static enum cmd_return cmd_list(struct pwm_ctx *, int, char *[]); 87 static enum cmd_return cmd_list(struct pwm_ctx *, int, char *[]);
87 static enum cmd_return cmd_create(struct pwm_ctx *, int, char *[]); 88 static enum cmd_return cmd_create(struct pwm_ctx *, int, char *[]);
88 static enum cmd_return cmd_modify(struct pwm_ctx *, int, char *[]); 89 static enum cmd_return cmd_modify(struct pwm_ctx *, int, char *[]);
89 static enum cmd_return cmd_generatepassword(struct pwm_ctx *, int, char *[]); 90 static enum cmd_return cmd_generatepassword(struct pwm_ctx *, int, char *[]);
149 CHARS_DIGIT "abcdef", 150 CHARS_DIGIT "abcdef",
150 CHARS_DIGIT CHARS_UPPER CHARS_LOWER CHARS_PUNCT 151 CHARS_DIGIT CHARS_UPPER CHARS_LOWER CHARS_PUNCT
151 }; 152 };
152 153
153 static struct cmd cmds[] = { 154 static struct cmd cmds[] = {
155 { "t", "status", "status", "Redisplay an error message of the previous "
156 "command and unsaved changes", cmd_status },
154 { "i", "info", "info", "Show metadata information about the current file", 157 { "i", "info", "info", "Show metadata information about the current file",
155 cmd_info }, 158 cmd_info },
156 { "ls", "list", "list [field~regex ...]", "List entries", cmd_list }, 159 { "ls", "list", "list [field~regex ...]", "List entries", cmd_list },
157 { "c", "create", "create field=value ...", "Create entry", cmd_create }, 160 { "c", "create", "create field=value ...", "Create entry", cmd_create },
158 { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify }, 161 { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify },
212 215
213 return (0); 216 return (0);
214 } 217 }
215 218
216 static enum cmd_return 219 static enum cmd_return
220 cmd_status(struct pwm_ctx *ctx, int argc, char *argv[])
221 {
222 if (argc != 1) {
223 return (CMD_USAGE);
224 }
225
226 if (ctx->errmsg != NULL) {
227 printf("%s\n", ctx->errmsg);
228 }
229 printf("There are%sunsaved changes\n", ctx->unsaved_changes ? " " :
230 " no ");
231
232 return (CMD_STATUS);
233 }
234
235 static enum cmd_return
217 cmd_info(struct pwm_ctx *ctx, int argc, char *argv[]) 236 cmd_info(struct pwm_ctx *ctx, int argc, char *argv[])
218 { 237 {
219 struct metadata *metadata; 238 struct metadata *metadata;
220 struct tm *tm; 239 struct tm *tm;
221 char timebuf[TIME_SIZE]; 240 char timebuf[TIME_SIZE];
265 struct record *record; 284 struct record *record;
266 285
267 for (i = 1; i < argc; i++) { 286 for (i = 1; i < argc; i++) {
268 type = parse_arg(argv[i], field_namev, '~', &value); 287 type = parse_arg(argv[i], field_namev, '~', &value);
269 if (type == FIELD_UNKNOWN) { 288 if (type == FIELD_UNKNOWN) {
270 fprintf(stderr, "bad field name \"%s\"\n", argv[i]); 289 pwm_err(ctx, "bad field name \"%s\"", argv[i]);
271 goto out; 290 goto out;
272 } 291 }
273 if (value[0] == '\0') { 292 if (value[0] == '\0') {
274 /* skip empty expressions */ 293 /* skip empty expressions */
275 continue; 294 continue;
289 break; 308 break;
290 case FIELD_URL: 309 case FIELD_URL:
291 repp = &url_re; 310 repp = &url_re;
292 break; 311 break;
293 default: 312 default:
294 fprintf(stderr, "bad field name \"%s\"\n", argv[i]); 313 pwm_err(ctx, "bad field name \"%s\"", argv[i]);
295 goto out; 314 goto out;
296 } 315 }
297 316
298 if (*repp == NULL) { 317 if (*repp == NULL) {
299 *repp = xmalloc(sizeof (regex_t)); 318 *repp = xmalloc(sizeof (regex_t));
303 errcode = regcomp(*repp, value, REG_EXTENDED | REG_NOSUB); 322 errcode = regcomp(*repp, value, REG_EXTENDED | REG_NOSUB);
304 if (errcode != 0) { 323 if (errcode != 0) {
305 errbuf_size = regerror(errcode, *repp, "", 0); 324 errbuf_size = regerror(errcode, *repp, "", 0);
306 errbuf = xmalloc(errbuf_size); 325 errbuf = xmalloc(errbuf_size);
307 regerror(errcode, *repp, errbuf, errbuf_size); 326 regerror(errcode, *repp, errbuf, errbuf_size);
308 fprintf(stderr, "bad regular expression \"%s\"\n", 327 pwm_err(ctx, "bad regular expression \"%s\"", errbuf);
309 errbuf);
310 free(errbuf); 328 free(errbuf);
311 329
312 free(*repp); 330 free(*repp);
313 *repp = NULL; 331 *repp = NULL;
314 332
381 } 399 }
382 400
383 for (i = 1; i < argc; i++) { 401 for (i = 1; i < argc; i++) {
384 type = parse_arg(argv[i], field_namev, '=', &value); 402 type = parse_arg(argv[i], field_namev, '=', &value);
385 if (type == FIELD_UNKNOWN) { 403 if (type == FIELD_UNKNOWN) {
386 fprintf(stderr, "bad field assignment \"%s\"\n", 404 pwm_err(ctx, "bad field assignment \"%s\"", argv[i]);
387 argv[i]);
388 } 405 }
389 if (value[0] == '\0') { 406 if (value[0] == '\0') {
390 /* skip empty assignments */ 407 /* skip empty assignments */
391 continue; 408 continue;
392 } 409 }
408 break; 425 break;
409 case FIELD_URL: 426 case FIELD_URL:
410 record.url = value; 427 record.url = value;
411 break; 428 break;
412 default: 429 default:
413 fprintf(stderr, "bad field name \"%s\"\n", argv[i]); 430 pwm_err(ctx, "bad field name \"%s\"", argv[i]);
414 return (CMD_ERR); 431 return (CMD_ERR);
415 } 432 }
416 } 433 }
417 434
418 pwfile_create_record(ctx, &record); 435 pwfile_create_record(ctx, &record);
432 if (argc < 2) { 449 if (argc < 2) {
433 return (CMD_USAGE); 450 return (CMD_USAGE);
434 } 451 }
435 452
436 if (parse_id(argv[1], &id) != 0) { 453 if (parse_id(argv[1], &id) != 0) {
437 fprintf(stderr, "invalid id %s\n", argv[1]); 454 pwm_err(ctx, "invalid id %s", argv[1]);
438 return (CMD_ERR); 455 return (CMD_ERR);
439 } 456 }
440 457
441 for (i = 2; i < argc; i++) { 458 for (i = 2; i < argc; i++) {
442 type = parse_arg(argv[i], field_namev, '=', &value); 459 type = parse_arg(argv[i], field_namev, '=', &value);
443 if (type == FIELD_UNKNOWN) { 460 if (type == FIELD_UNKNOWN) {
444 fprintf(stderr, "bad field assignment \"%s\"\n", 461 pwm_err(ctx, "bad field assignment \"%s\"", argv[i]);
445 argv[i]);
446 return (CMD_ERR); 462 return (CMD_ERR);
447 } 463 }
448 if (value[0] == '\0') { 464 if (value[0] == '\0') {
449 /* skip empty assignments */ 465 /* skip empty assignments */
450 continue; 466 continue;
467 break; 483 break;
468 case FIELD_URL: 484 case FIELD_URL:
469 record.url = value; 485 record.url = value;
470 break; 486 break;
471 default: 487 default:
472 fprintf(stderr, "bad field name \"%s\"\n", argv[i]); 488 pwm_err(ctx, "bad field name \"%s\"", argv[i]);
473 return (CMD_ERR); 489 return (CMD_ERR);
474 } 490 }
475 } 491 }
476 492
477 pwfile_modify_record(ctx, id, &record); 493 pwfile_modify_record(ctx, id, &record);
508 case CMD_GP_ARG_LEN: 524 case CMD_GP_ARG_LEN:
509 errno = 0; 525 errno = 0;
510 x = strtol(value, &p, 10); 526 x = strtol(value, &p, 10);
511 if ((errno != 0) || (*value == '\0') || (*p != '\0') || 527 if ((errno != 0) || (*value == '\0') || (*p != '\0') ||
512 (x > PWS3_MAX_PASSWORD_LEN) || (x <= 0)) { 528 (x > PWS3_MAX_PASSWORD_LEN) || (x <= 0)) {
513 fprintf(stderr, "invalid password length " 529 pwm_err(ctx, "invalid password length \"%s\"",
514 "\"%s\"\n", argv[i]); 530 argv[i]);
515 goto out; 531 goto out;
516 } 532 }
517 password_len = x; 533 password_len = x;
518 break; 534 break;
519 case CMD_GP_ARG_CHARS: 535 case CMD_GP_ARG_CHARS:
520 errno = 0; 536 errno = 0;
521 x = strtol(value, &p, 10); 537 x = strtol(value, &p, 10);
522 if ((errno != 0) || (*value == '\0') || (*p != ':') || 538 if ((errno != 0) || (*value == '\0') || (*p != ':') ||
523 (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) { 539 (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) {
524 fprintf(stderr, "invalid minimum number of " 540 pwm_err(ctx, "invalid minimum number of "
525 "characters \"%s\"\n", argv[i]); 541 "characters \"%s\"", argv[i]);
526 goto out; 542 goto out;
527 } 543 }
528 chars_min = x; 544 chars_min = x;
529 545
530 chars = ++p; 546 chars = ++p;
531 while (*p != '\0') { 547 while (*p != '\0') {
532 if (!isascii(*p) || !isprint(*p)) { 548 if (!isascii(*p) || !isprint(*p)) {
533 fprintf(stderr, "invalid character in " 549 pwm_err(ctx, "invalid character in "
534 "character group \"%s\"\n", 550 "character group \"%s\"", argv[i]);
535 argv[i]);
536 goto out; 551 goto out;
537 } 552 }
538 p++; 553 p++;
539 } 554 }
540 555
548 case CMD_GP_ARG_CHARCLASS: 563 case CMD_GP_ARG_CHARCLASS:
549 errno = 0; 564 errno = 0;
550 x = strtol(value, &p, 10); 565 x = strtol(value, &p, 10);
551 if ((errno != 0) || (*value == '\0') || (*p != ':') || 566 if ((errno != 0) || (*value == '\0') || (*p != ':') ||
552 (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) { 567 (x < 0) || (x > PWS3_MAX_PASSWORD_LEN)) {
553 fprintf(stderr, "invalid minimum number of " 568 pwm_err(ctx, "invalid minimum number of "
554 "characters \"%s\"\n", argv[i]); 569 "characters \"%s\"", argv[i]);
555 goto out; 570 goto out;
556 } 571 }
557 chars_min = x; 572 chars_min = x;
558 573
559 charclass = parse_arg(++p, charclass_namev, '\0', NULL); 574 charclass = parse_arg(++p, charclass_namev, '\0', NULL);
560 if (charclass < 0) { 575 if (charclass < 0) {
561 fprintf(stderr, "unknown character class " 576 pwm_err(ctx, "unknown character class \"%s\"",
562 "\"%s\"\n", argv[i]); 577 argv[i]);
563 goto out; 578 goto out;
564 } 579 }
565 chars = charclass_values[charclass]; 580 chars = charclass_values[charclass];
566 char_groupv = xrealloc(char_groupv, 581 char_groupv = xrealloc(char_groupv,
567 sizeof (struct pw_char_group) * (char_groupv_len + 582 sizeof (struct pw_char_group) * (char_groupv_len +
569 char_groupv[char_groupv_len].chars = chars; 584 char_groupv[char_groupv_len].chars = chars;
570 char_groupv[char_groupv_len].chars_min = chars_min; 585 char_groupv[char_groupv_len].chars_min = chars_min;
571 char_groupv_len++; 586 char_groupv_len++;
572 break; 587 break;
573 default: 588 default:
574 fprintf(stderr, "invalid argument \"%s\"\n", argv[i]); 589 pwm_err(ctx, "invalid argument \"%s\"", argv[i]);
575 retval = CMD_USAGE; 590 retval = CMD_USAGE;
576 goto out; 591 goto out;
577 } 592 }
578 } 593 }
579 594
580 for (j = 0; j < char_groupv_len; j++) { 595 for (j = 0; j < char_groupv_len; j++) {
581 if (char_groupv[j].chars_min > password_len) { 596 if (char_groupv[j].chars_min > password_len) {
582 fprintf(stderr, "invalid minimum number of " 597 pwm_err(ctx, "invalid minimum number of characters "
583 "characters \"%zu:%s\"\n", char_groupv[j].chars_min, 598 "\"%zu:%s\"", char_groupv[j].chars_min,
584 char_groupv[j].chars); 599 char_groupv[j].chars);
585 goto out; 600 goto out;
586 } 601 }
587 } 602 }
588 603
594 char_groupv_len++; 609 char_groupv_len++;
595 } 610 }
596 611
597 if (pw_genrandom(char_groupv, char_groupv_len, password, 612 if (pw_genrandom(char_groupv, char_groupv_len, password,
598 password_len) != 0) { 613 password_len) != 0) {
599 fprintf(stderr, "failed to generate password that meets the " 614 pwm_err(ctx, "failed to generate password that meets the given "
600 "given constraints\n"); 615 "constraints");
601 goto out; 616 goto out;
602 } 617 }
603 618
604 if (id != 0) { 619 if (id != 0) {
605 if (pwfile_modify_record(ctx, id, 620 if (pwfile_modify_record(ctx, id,
606 &(struct record){ .password = password }) != 0) { 621 &(struct record){ .password = password }) != 0) {
607 fprintf(stderr, "record %u does not exist\n", id); 622 pwm_err(ctx, "record %u does not exist", id);
608 goto out; 623 goto out;
609 } 624 }
610 } else { 625 } else {
611 printf("%s\n", password); 626 printf("%s\n", password);
612 } 627 }
627 if (argc != 2) { 642 if (argc != 2) {
628 return (CMD_USAGE); 643 return (CMD_USAGE);
629 } 644 }
630 645
631 if (parse_id(argv[1], &id) != 0) { 646 if (parse_id(argv[1], &id) != 0) {
632 fprintf(stderr, "invalid id %s\n", argv[1]); 647 pwm_err(ctx, "invalid id %s", argv[1]);
633 return (CMD_ERR); 648 return (CMD_ERR);
634 } 649 }
635 650
636 if (pwfile_remove_record(ctx, id) != 0) { 651 if (pwfile_remove_record(ctx, id) != 0) {
637 fprintf(stderr, "failed to remove record %u\n", id); 652 pwm_err(ctx, "failed to remove record %u", id);
638 return (CMD_ERR); 653 return (CMD_ERR);
639 } 654 }
640 655
641 return (CMD_OK); 656 return (CMD_OK);
642 } 657 }
734 if (argc < 2) { 749 if (argc < 2) {
735 return (CMD_USAGE); 750 return (CMD_USAGE);
736 } 751 }
737 752
738 if (parse_id(argv[1], &id) != 0) { 753 if (parse_id(argv[1], &id) != 0) {
739 fprintf(stderr, "invalid id %s\n", argv[1]); 754 pwm_err(ctx, "invalid id %s", argv[1]);
740 return (CMD_ERR); 755 return (CMD_ERR);
741 } 756 }
742 757
743 if (argc > 2) { 758 if (argc > 2) {
744 /* show only explicitly given field names */ 759 /* show only explicitly given field names */
746 } 761 }
747 762
748 for (i = 2; i < argc; i++) { 763 for (i = 2; i < argc; i++) {
749 type = parse_arg(argv[i], field_namev, '\0', NULL); 764 type = parse_arg(argv[i], field_namev, '\0', NULL);
750 if (type < 0) { 765 if (type < 0) {
751 fprintf(stderr, "bad field name \"%s\"\n", argv[i]); 766 pwm_err(ctx, "bad field name \"%s\"", argv[i]);
752 return (CMD_ERR); 767 return (CMD_ERR);
753 } 768 }
754 fields[type] = 1; 769 fields[type] = 1;
755 } 770 }
756 771
757 record = pwfile_get_record(ctx, id); 772 record = pwfile_get_record(ctx, id);
758 if (record == NULL) { 773 if (record == NULL) {
759 fprintf(stderr, "record %u does not exist\n", id); 774 pwm_err(ctx, "record %u does not exist", id);
760 return (CMD_ERR); 775 return (CMD_ERR);
761 } 776 }
762 print_record(record, fields, 1, stdout); 777 print_record(record, fields, 1, stdout);
763 pwfile_destroy_record(record); 778 pwfile_destroy_record(record);
764 779
778 if (argc != 4) { 793 if (argc != 4) {
779 return (CMD_USAGE); 794 return (CMD_USAGE);
780 } 795 }
781 796
782 if (parse_id(argv[1], &id) != 0) { 797 if (parse_id(argv[1], &id) != 0) {
783 fprintf(stderr, "invalid id %s\n", argv[1]); 798 pwm_err(ctx, "invalid id %s", argv[1]);
784 return (CMD_ERR); 799 return (CMD_ERR);
785 } 800 }
786 801
787 type = parse_arg(argv[2], field_namev, '\0', NULL); 802 type = parse_arg(argv[2], field_namev, '\0', NULL);
788 if (type < 0) { 803 if (type < 0) {
789 fprintf(stderr, "bad field name \"%s\"\n", argv[2]); 804 pwm_err(ctx, "bad field name \"%s\"", argv[2]);
790 return (CMD_ERR); 805 return (CMD_ERR);
791 } 806 }
792 fields[type] = 1; 807 fields[type] = 1;
793 808
794 fp = popen(argv[3], "w"); 809 fp = popen(argv[3], "w");
797 goto out; 812 goto out;
798 } 813 }
799 814
800 record = pwfile_get_record(ctx, id); 815 record = pwfile_get_record(ctx, id);
801 if (record == NULL) { 816 if (record == NULL) {
802 fprintf(stderr, "record %u does not exist\n", id); 817 pwm_err(ctx, "record %u does not exist", id);
803 goto out; 818 goto out;
804 } 819 }
805 820
806 print_record(record, fields, 0, fp); 821 print_record(record, fields, 0, fp);
807 822
822 if (argc != 2) { 837 if (argc != 2) {
823 return (CMD_USAGE); 838 return (CMD_USAGE);
824 } 839 }
825 840
826 if (pwfile_create_group(ctx, argv[1]) != 0) { 841 if (pwfile_create_group(ctx, argv[1]) != 0) {
827 fprintf(stderr, "group \"%s\" already exists\n", argv[1]); 842 pwm_err(ctx, "group \"%s\" already exists", argv[1]);
828 return (CMD_ERR); 843 return (CMD_ERR);
829 } 844 }
830 845
831 return (CMD_OK); 846 return (CMD_OK);
832 } 847 }
837 if (argc != 2) { 852 if (argc != 2) {
838 return (CMD_USAGE); 853 return (CMD_USAGE);
839 } 854 }
840 855
841 if (pwfile_remove_group(ctx, argv[1]) != 0) { 856 if (pwfile_remove_group(ctx, argv[1]) != 0) {
842 fprintf(stderr, "there is no empty group \"%s\"\n", argv[1]); 857 pwm_err(ctx, "empty group \"%s\" does not exist", argv[1]);
843 return (CMD_ERR); 858 return (CMD_ERR);
844 } 859 }
845 860
846 return (CMD_OK); 861 return (CMD_OK);
847 } 862 }
856 if (argc > 2) { 871 if (argc > 2) {
857 return (CMD_USAGE); 872 return (CMD_USAGE);
858 } else if (argc == 2) { 873 } else if (argc == 2) {
859 password_len = strlen(argv[1]); 874 password_len = strlen(argv[1]);
860 if (password_len == 0) { 875 if (password_len == 0) {
861 fprintf(stderr, "password must not be empty\n"); 876 pwm_err(ctx, "password must not be empty");
862 return (CMD_ERR); 877 return (CMD_ERR);
863 } else if (password_len + 1 > sizeof (ctx->password)) { 878 } else if (password_len + 1 > sizeof (ctx->password)) {
864 fprintf(stderr, "password too long\n"); 879 pwm_err(ctx, "password too long");
865 return (CMD_ERR); 880 return (CMD_ERR);
866 } 881 }
867 memcpy(ctx->password, argv[1], password_len + 1); 882 memcpy(ctx->password, argv[1], password_len + 1);
868 } else { 883 } else {
869 if (readpassphrase("Enter password: ", password_buf, 884 if (readpassphrase("Enter password: ", password_buf,
871 NULL) { 886 NULL) {
872 err(1, "readpassphrase"); 887 err(1, "readpassphrase");
873 } 888 }
874 password_len = strlen(password_buf); 889 password_len = strlen(password_buf);
875 if (password_len == 0) { 890 if (password_len == 0) {
876 fprintf(stderr, "password must not be empty\n"); 891 pwm_err(ctx, "password must not be empty");
877 return (CMD_ERR); 892 return (CMD_ERR);
878 } 893 }
879 if (readpassphrase("Confirm password: ", confirm_buf, 894 if (readpassphrase("Confirm password: ", confirm_buf,
880 sizeof (confirm_buf), 895 sizeof (confirm_buf),
881 RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { 896 RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
882 err(1, "readpassphrase"); 897 err(1, "readpassphrase");
883 } 898 }
884 if (strcmp(password_buf, confirm_buf) != 0) { 899 if (strcmp(password_buf, confirm_buf) != 0) {
885 fprintf(stderr, "passwords do not match\n"); 900 pwm_err(ctx, "passwords do not match");
886 return (CMD_ERR); 901 return (CMD_ERR);
887 } 902 }
888 memcpy(ctx->password, password_buf, password_len + 1); 903 memcpy(ctx->password, password_buf, password_len + 1);
889 } 904 }
890 905