projects/pwm

changeset 0:a7e41e1a79c8

Initial revision
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Thu Jan 19 22:39:51 2017 +0100 (2017-01-19)
parents
children 55281f14dc9b
files Makefile cmd.c cmd.h compat.h compat/asprintf.c compat/asprintf.h compat/err.c compat/err.h compat/getentropy.c compat/getentropy.h compat/readpassphrase.c compat/readpassphrase.h compat/setprogname.c compat/setprogname.h compat/tree.h deps.sed pwfile.c pwfile.h pwm.c pwm.h tok.c tok.h util.c util.h
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Makefile	Thu Jan 19 22:39:51 2017 +0100
     1.3 @@ -0,0 +1,167 @@
     1.4 +#
     1.5 +# Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     1.6 +#
     1.7 +# Permission is hereby granted, free of charge, to any person obtaining
     1.8 +# a copy of this software and associated documentation files (the
     1.9 +# "Software"), to deal in the Software without restriction, including
    1.10 +# without limitation the rights to use, copy, modify, merge, publish,
    1.11 +# distribute, sublicense, and/or sell copies of the Software, and to
    1.12 +# permit persons to whom the Software is furnished to do so, subject to
    1.13 +# the following conditions:
    1.14 +#
    1.15 +# The above copyright notice and this permission notice shall be included
    1.16 +# in all copies or substantial portions of the Software.
    1.17 +#
    1.18 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    1.19 +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    1.20 +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    1.21 +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    1.22 +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    1.23 +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    1.24 +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    1.25 +#
    1.26 +
    1.27 +PACKAGE =	pwm
    1.28 +VERSION =	1
    1.29 +DISTNAME :=	$(PACKAGE)-$(VERSION)
    1.30 +
    1.31 +# gcc, clang, icc, Sun/Solaris Studio
    1.32 +CC :=		$(CC) -std=c99
    1.33 +COMPILE.c =	$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) $(TARGET_ARCH) -c
    1.34 +# gcc, clang, icc
    1.35 +MAKEDEPEND.c =	$(CC) -MM $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS)
    1.36 +# Sun/Solaris Studio
    1.37 +#MAKEDEPEND.c =	$(CC) -xM1 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS)
    1.38 +# X makedepend
    1.39 +#MAKEDEPEND.c =	makedepend -f- -Y -- $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) --
    1.40 +LINK.c =	$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) $(XCPPFLAGS) $(LDFLAGS) $(XLDFLAGS) $(TARGET_ARCH)
    1.41 +LINK.o =	$(CC) $(LDFLAGS) $(XLDFLAGS) $(TARGET_ARCH)
    1.42 +CP :=		cp
    1.43 +INSTALL :=	install
    1.44 +INSTALL.exec :=	$(INSTALL) -D -m 0755
    1.45 +INSTALL.data :=	$(INSTALL) -D -m 0644
    1.46 +INSTALL.link :=	$(CP) -f -P
    1.47 +PAX :=		pax
    1.48 +GZIP :=		gzip
    1.49 +SED :=		sed
    1.50 +
    1.51 +DESTDIR ?=
    1.52 +prefix ?=	/usr/local
    1.53 +bindir ?=	$(prefix)/bin
    1.54 +
    1.55 +OS_NAME :=	$(shell uname -s)
    1.56 +OS_RELEASE :=	$(shell uname -r)
    1.57 +
    1.58 +ifeq ($(OS_NAME),Linux)
    1.59 +  HAVE_ASPRINTF ?=	1
    1.60 +  HAVE_ERR_H ?=		1
    1.61 +  HAVE_READPASSPHRASE_H ?= 0
    1.62 +  HAVE_SETPROGNAME ?=	0
    1.63 +  HAVE_SYS_TREE_H ?=	0
    1.64 +else ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly),)
    1.65 +  HAVE_ASPRINTF ?=	1
    1.66 +  HAVE_ERR_H ?=		1
    1.67 +  HAVE_READPASSPHRASE_H ?= 1
    1.68 +  HAVE_SETPROGNAME ?=	1
    1.69 +  HAVE_SYS_TREE_H ?=	1
    1.70 +else ifeq ($(OS_NAME),NetBSD)
    1.71 +  HAVE_ASPRINTF ?=	1
    1.72 +  HAVE_ERR_H ?=		1
    1.73 +  HAVE_READPASSPHRASE_H ?= 0
    1.74 +  HAVE_SYS_TREE_H ?=	1
    1.75 +  HAVE_SETPROGNAME ?=	1
    1.76 +else ifeq ($(OS_NAME),OpenBSD)
    1.77 +  HAVE_ASPRINTF ?=	1
    1.78 +  HAVE_ERR_H ?=		1
    1.79 +  HAVE_READPASSPHRASE_H ?= 1
    1.80 +  HAVE_SYS_TREE_H ?=	1
    1.81 +  HAVE_SETPROGNAME ?=	1
    1.82 +else ifeq ($(OS_NAME),SunOS)
    1.83 +  ifeq ($(OS_RELEASE),5.10)
    1.84 +    HAVE_ASPRINTF ?=	0
    1.85 +    HAVE_ERR_H ?=	0
    1.86 +  else
    1.87 +    HAVE_ASPRINTF ?=	1
    1.88 +    HAVE_ERR_H ?=	1
    1.89 +  endif
    1.90 +  HAVE_READPASSPHRASE_H ?= 0
    1.91 +  HAVE_SYS_TREE_H ?=	0
    1.92 +  HAVE_SETPROGNAME ?=	0
    1.93 +else
    1.94 +  HAVE_ASPRINTF ?=	0
    1.95 +  HAVE_ERR_H ?=		0
    1.96 +  HAVE_READPASSPHRASE_H ?= 0
    1.97 +  HAVE_SETPROGNAME ?=	0
    1.98 +  HAVE_SYS_TREE_H ?=	0
    1.99 +endif
   1.100 +
   1.101 +OBJS =	cmd.o \
   1.102 +	pwfile.o \
   1.103 +	pwm.o \
   1.104 +	tok.o \
   1.105 +	util.o
   1.106 +
   1.107 +.DEFAULT_TARGET = all
   1.108 +
   1.109 +.PHONY: all clean clobber dist install
   1.110 +
   1.111 +all: $(PACKAGE)
   1.112 +
   1.113 +XCPPFLAGS =	-DPACKAGE=\"$(PACKAGE)\" \
   1.114 +		-DVERSION=\"$(VERSION)\"
   1.115 +LDLIBS =	-lpws -lnettle
   1.116 +ifeq ($(HAVE_ERR_H),1)
   1.117 +  XCPPFLAGS +=	-DHAVE_ERR_H
   1.118 +else
   1.119 +  OBJS +=	compat/err.o
   1.120 +endif
   1.121 +ifeq ($(HAVE_READPASSPHRASE_H),1)
   1.122 +  XCPPFLAGS +=	-DHAVE_READPASSPHRASE_H
   1.123 +else
   1.124 +  OBJS +=	compat/readpassphrase.o
   1.125 +endif
   1.126 +ifeq ($(HAVE_SETPROGNAME),1)
   1.127 +  XCPPFLAGS +=	-DHAVE_SETPROGNAME
   1.128 +else
   1.129 +  OBJS +=	compat/setprogname.o
   1.130 +endif
   1.131 +ifeq ($(HAVE_SYS_TREE_H),1)
   1.132 +  XCPPFLAGS +=	-DHAVE_SYS_TREE_H
   1.133 +endif
   1.134 +ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly OpenBSD),)
   1.135 +  XCPPFLAGS +=	-I/usr/local/include
   1.136 +  XLDFLAGS +=	-L/usr/local/lib
   1.137 +else ifeq ($(OS_NAME),NetBSD)
   1.138 +  XCPPFLAGS +=	-I/usr/pkg/include
   1.139 +  XLDFLAGS +=	-L/usr/pkg/lib
   1.140 +endif
   1.141 +ifeq ($(findstring $(OS_NAME),FreeBSD DragonFly NetBSD OpenBSD),)
   1.142 +  XCPPFLAGS +=	-D_XOPEN_SOURCE=600
   1.143 +endif
   1.144 +ifeq ($(OS_NAME),SunOS)
   1.145 +  XCPPFLAGS +=	-I/usr/xpg4/include -D__EXTENSIONS__
   1.146 +  XLDFLAGS +=	-L/usr/xpg4/lib -R/usr/xpg4/lib
   1.147 +endif
   1.148 +
   1.149 +$(PACKAGE): $(OBJS)
   1.150 +	$(LINK.o) $^ $(LDLIBS) -o $@
   1.151 +
   1.152 +%.o: %.c
   1.153 +	$(MAKEDEPEND.c) $< | $(SED) -f deps.sed >$*.d
   1.154 +	$(COMPILE.c) -o $@ $<
   1.155 +
   1.156 +install:
   1.157 +	$(INSTALL.exec) $(PACKAGE) "$(DESTDIR)$(bindir)/$(PACKAGE)"
   1.158 +
   1.159 +clean:
   1.160 +	rm -f $(PACKAGE) $(OBJS)
   1.161 +
   1.162 +clobber: clean
   1.163 +	rm -f $(patsubst %.o,%.d,$(OBJS))
   1.164 +
   1.165 +dist: clobber
   1.166 +	$(PAX) -w -x ustar -s ',.*/\..*,,' -s ',./[^/]*\.tar\.gz,,' \
   1.167 +	    -s ',^\.$$,,' -s ',\./,$(DISTNAME)/,' . | \
   1.168 +	    $(GZIP) > $(DISTNAME).tar.gz
   1.169 +
   1.170 +-include $(patsubst %.o,%.d,$(OBJS))
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/cmd.c	Thu Jan 19 22:39:51 2017 +0100
     2.3 @@ -0,0 +1,546 @@
     2.4 +/*
     2.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     2.6 + *
     2.7 + * Permission is hereby granted, free of charge, to any person obtaining
     2.8 + * a copy of this software and associated documentation files (the
     2.9 + * "Software"), to deal in the Software without restriction, including
    2.10 + * without limitation the rights to use, copy, modify, merge, publish,
    2.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    2.12 + * permit persons to whom the Software is furnished to do so, subject to
    2.13 + * the following conditions:
    2.14 + *
    2.15 + * The above copyright notice and this permission notice shall be included
    2.16 + * in all copies or substantial portions of the Software.
    2.17 + *
    2.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    2.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    2.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    2.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    2.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    2.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    2.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    2.25 + */
    2.26 +
    2.27 +#include "compat.h"
    2.28 +
    2.29 +#ifdef	HAVE_ERR_H
    2.30 +#include <err.h>
    2.31 +#endif /* HAVE_ERR_H */
    2.32 +#include <errno.h>
    2.33 +#include <limits.h>
    2.34 +#ifdef	HAVE_READPASSPHRASE_H
    2.35 +#include <readpassphrase.h>
    2.36 +#endif /* READPASSPHRASE_H */
    2.37 +#include <stdlib.h>
    2.38 +#include <string.h>
    2.39 +#include <unistd.h>
    2.40 +
    2.41 +#include "cmd.h"
    2.42 +#include "pwfile.h"
    2.43 +#include "util.h"
    2.44 +
    2.45 +enum field_type {
    2.46 +	FIELD_UNKNOWN = -1,
    2.47 +	FIELD_GROUP,
    2.48 +	FIELD_TITLE,
    2.49 +	FIELD_USERNAME,
    2.50 +	FIELD_PASSWORD,
    2.51 +	FIELD_NOTES,
    2.52 +	FIELD_URL
    2.53 +};
    2.54 +
    2.55 +static enum cmd_return	cmd_list(struct pwm_ctx *, int, char *[]);
    2.56 +static enum cmd_return	cmd_create(struct pwm_ctx *, int, char *[]);
    2.57 +static enum cmd_return	cmd_modify(struct pwm_ctx *, int, char *[]);
    2.58 +static enum cmd_return	cmd_remove(struct pwm_ctx *, int, char *[]);
    2.59 +static enum cmd_return	cmd_show(struct pwm_ctx *, int, char *[]);
    2.60 +static enum cmd_return	cmd_pipe(struct pwm_ctx *, int, char *[]);
    2.61 +static enum cmd_return	cmd_creategroup(struct pwm_ctx *, int, char *[]);
    2.62 +static enum cmd_return	cmd_removegroup(struct pwm_ctx *, int, char *[]);
    2.63 +static enum cmd_return	cmd_changepassword(struct pwm_ctx *, int, char *[]);
    2.64 +static enum cmd_return	cmd_help(struct pwm_ctx *, int, char *[]);
    2.65 +static enum cmd_return	cmd_write(struct pwm_ctx *, int, char *[]);
    2.66 +static enum cmd_return	cmd_quit(struct pwm_ctx *, int, char *[]);
    2.67 +
    2.68 +static const char *field_names[] = {
    2.69 +    "group",
    2.70 +    "title",
    2.71 +    "username",
    2.72 +    "password",
    2.73 +    "notes",
    2.74 +    "url"
    2.75 +};
    2.76 +
    2.77 +static const char *field_labels[] = {
    2.78 +    "Group:    ",
    2.79 +    "Title:    ",
    2.80 +    "Username: ",
    2.81 +    "Password: ",
    2.82 +    "Notes:    ",
    2.83 +    "URL:      "
    2.84 +};
    2.85 +
    2.86 +static struct cmd cmds[] = {
    2.87 +    { "ls", "list", "list", "List entries", cmd_list },
    2.88 +    { "c", "create", "create field=value ...", "Create entry", cmd_create },
    2.89 +    { "m", "modify", "modify id field=value ...", "Modify entry", cmd_modify },
    2.90 +    { "rm", "remove", "remove id", "Delete entry", cmd_remove },
    2.91 +    { "s", "show", "show id field", "Show entry", cmd_show },
    2.92 +    { "p", "pipe", "pipe id field command", "Pipe entry to external command",
    2.93 +    cmd_pipe },
    2.94 +    { "cg", "creategroup", "creategroup name", "Create empty group",
    2.95 +    cmd_creategroup },
    2.96 +    { "rg", "removegroup", "removegroup name", "Delete empty group",
    2.97 +    cmd_removegroup },
    2.98 +    { "ch", "changepassword", "changepassword", "Change password",
    2.99 +    cmd_changepassword },
   2.100 +    { "h", "help", "help", "Show help text", cmd_help },
   2.101 +    { "w", "write", "write", "Write the database", cmd_write },
   2.102 +    { "q", "quit", "quit", "Quit", cmd_quit },
   2.103 +    { 0 }
   2.104 +};
   2.105 +
   2.106 +static enum field_type
   2.107 +parse_field_name(const char *name)
   2.108 +{
   2.109 +	int	i;
   2.110 +
   2.111 +	for (i = 0; i < (int)COUNTOF(field_names); i++) {
   2.112 +		if (strcmp(field_names[i], name) == 0) {
   2.113 +			return (i);
   2.114 +		}
   2.115 +	}
   2.116 +
   2.117 +	return (FIELD_UNKNOWN);
   2.118 +}
   2.119 +
   2.120 +static enum field_type
   2.121 +parse_field_assignment(char *arg, struct record *record)
   2.122 +{
   2.123 +	int	i;
   2.124 +	size_t	field_name_len;
   2.125 +	char	*value;
   2.126 +
   2.127 +	for (i = 0; i < (int)COUNTOF(field_names); i++) {
   2.128 +		field_name_len = strlen(field_names[i]);
   2.129 +		if ((strncmp(field_names[i], arg, field_name_len) == 0) &&
   2.130 +		    (arg[field_name_len] == '=')){
   2.131 +			value = arg + field_name_len + 1;
   2.132 +			if (*value == '\0') {
   2.133 +				/* skip empty assignments */
   2.134 +				return (i);
   2.135 +			}
   2.136 +			switch (i) {
   2.137 +			case FIELD_GROUP:
   2.138 +				record->group = value;
   2.139 +				break;
   2.140 +			case FIELD_TITLE:
   2.141 +				record->title = value;
   2.142 +				break;
   2.143 +			case FIELD_USERNAME:
   2.144 +				record->username = value;
   2.145 +				break;
   2.146 +			case FIELD_PASSWORD:
   2.147 +				record->password = value;
   2.148 +				break;
   2.149 +			case FIELD_NOTES:
   2.150 +				record->notes = value;
   2.151 +				break;
   2.152 +			case FIELD_URL:
   2.153 +				record->url = value;
   2.154 +				break;
   2.155 +			default:
   2.156 +				break;
   2.157 +			}
   2.158 +			return (i);
   2.159 +		}
   2.160 +	}
   2.161 +
   2.162 +	return (FIELD_UNKNOWN);
   2.163 +}
   2.164 +
   2.165 +static int
   2.166 +parse_id(const char *arg, unsigned int *idp)
   2.167 +{
   2.168 +	long	x;
   2.169 +	char	*p;
   2.170 +
   2.171 +	errno = 0;
   2.172 +	x = strtol(arg, &p, 10);
   2.173 +	if ((errno != 0) || (*arg == '\0') || (*p != '\0') || (x > UINT_MAX) ||
   2.174 +	    (x <= 0)) {
   2.175 +		return (-1);
   2.176 +	}
   2.177 +	*idp = (unsigned int)x;
   2.178 +
   2.179 +	return (0);
   2.180 +}
   2.181 +
   2.182 +static enum cmd_return
   2.183 +cmd_list(struct pwm_ctx *ctx, int argc, char *argv[])
   2.184 +{
   2.185 +	union list_item	**list;
   2.186 +	size_t		i;
   2.187 +
   2.188 +	if (argc != 1) {
   2.189 +		return (CMD_USAGE);
   2.190 +	}
   2.191 +
   2.192 +	list = pwfile_create_list(ctx);
   2.193 +	for (i = 0; list[i] != NULL; i++) {
   2.194 +		if (list[i]->any.type == ITEM_TYPE_GROUP) {
   2.195 +			printf("[%s]\n", list[i]->group.group);
   2.196 +		} else {
   2.197 +			printf("%4u %s\n", list[i]->record.id,
   2.198 +			    (list[i]->record.title != NULL) ?
   2.199 +			    list[i]->record.title : "");
   2.200 +		}
   2.201 +	}
   2.202 +	pwfile_destroy_list(list);
   2.203 +
   2.204 +	return (CMD_OK);
   2.205 +}
   2.206 +
   2.207 +static enum cmd_return
   2.208 +cmd_create(struct pwm_ctx *ctx, int argc, char *argv[])
   2.209 +{
   2.210 +	int		i;
   2.211 +	struct record record = { 0 };
   2.212 +
   2.213 +	if (argc < 2) {
   2.214 +		return (CMD_USAGE);
   2.215 +	}
   2.216 +
   2.217 +	for (i = 1; i < argc; i++) {
   2.218 +		if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) {
   2.219 +			fprintf(stderr, "bad field assignment \"%s\"\n",
   2.220 +			    argv[i]);
   2.221 +			return (CMD_ERR);
   2.222 +		}
   2.223 +	}
   2.224 +
   2.225 +	pwfile_create_record(ctx, &record);
   2.226 +
   2.227 +	return (CMD_OK);
   2.228 +}
   2.229 +
   2.230 +static enum cmd_return
   2.231 +cmd_modify(struct pwm_ctx *ctx, int argc, char *argv[])
   2.232 +{
   2.233 +	unsigned int	id;
   2.234 +	int		i;
   2.235 +	struct record record = { 0 };
   2.236 +
   2.237 +	if (argc < 2) {
   2.238 +		return (CMD_USAGE);
   2.239 +	}
   2.240 +
   2.241 +	if (parse_id(argv[1], &id) != 0) {
   2.242 +		fprintf(stderr, "invalid id %s\n", argv[1]);
   2.243 +		return (CMD_ERR);
   2.244 +	}
   2.245 +
   2.246 +	for (i = 2; i < argc; i++) {
   2.247 +		if (parse_field_assignment(argv[i], &record) == FIELD_UNKNOWN) {
   2.248 +			fprintf(stderr, "bad field assignment \"%s\"\n",
   2.249 +			    argv[i]);
   2.250 +			return (CMD_ERR);
   2.251 +		}
   2.252 +	}
   2.253 +
   2.254 +	pwfile_modify_record(ctx, id, &record);
   2.255 +
   2.256 +	return (CMD_OK);
   2.257 +}
   2.258 +
   2.259 +static enum cmd_return
   2.260 +cmd_remove(struct pwm_ctx *ctx, int argc, char *argv[])
   2.261 +{
   2.262 +	unsigned int	id;
   2.263 +
   2.264 +	if (argc != 2) {
   2.265 +		return (CMD_USAGE);
   2.266 +	}
   2.267 +
   2.268 +	if (parse_id(argv[1], &id) != 0) {
   2.269 +		fprintf(stderr, "invalid id %s\n", argv[1]);
   2.270 +		return (CMD_ERR);
   2.271 +	}
   2.272 +
   2.273 +	if (pwfile_remove_record(ctx, id) != 0) {
   2.274 +		fprintf(stderr, "failed to remove record %u\n", id);
   2.275 +		return (CMD_ERR);
   2.276 +	}
   2.277 +
   2.278 +	return (CMD_OK);
   2.279 +}
   2.280 +
   2.281 +static int
   2.282 +print_field(const char *label, const char *value, int show_label, FILE *fp)
   2.283 +{
   2.284 +	fprintf(fp, "%s%s\n", show_label ? label : "", (value != NULL) ?
   2.285 +	    value : "");
   2.286 +	if (ferror(fp)) {
   2.287 +		warn("fprintf");
   2.288 +		return (-1);
   2.289 +	}
   2.290 +	return (0);
   2.291 +}
   2.292 +
   2.293 +static void
   2.294 +print_record(struct record *record, int fields[], int show_labels, FILE *fp)
   2.295 +{
   2.296 +	if (fields[FIELD_TITLE]) {
   2.297 +		if (print_field(field_labels[FIELD_TITLE], record->title,
   2.298 +		    show_labels, fp) != 0) {
   2.299 +			return;
   2.300 +		}
   2.301 +	}
   2.302 +	if (fields[FIELD_GROUP]) {
   2.303 +		if (print_field(field_labels[FIELD_GROUP], record->group,
   2.304 +		    show_labels, fp)) {
   2.305 +			return;
   2.306 +		}
   2.307 +	}
   2.308 +	if (fields[FIELD_USERNAME]) {
   2.309 +		if (print_field(field_labels[FIELD_USERNAME], record->username,
   2.310 +		    show_labels, fp)) {
   2.311 +			return;
   2.312 +		}
   2.313 +	}
   2.314 +	if (fields[FIELD_PASSWORD]) {
   2.315 +		if (print_field(field_labels[FIELD_PASSWORD], record->password,
   2.316 +		    show_labels, fp)) {
   2.317 +			return;
   2.318 +		}
   2.319 +	}
   2.320 +	if (fields[FIELD_NOTES]) {
   2.321 +		if (print_field(field_labels[FIELD_NOTES], record->notes,
   2.322 +		    show_labels, fp)) {
   2.323 +			return;
   2.324 +		}
   2.325 +	}
   2.326 +	if (fields[FIELD_URL]) {
   2.327 +		if (print_field(field_labels[FIELD_URL], record->url,
   2.328 +		    show_labels, fp)) {
   2.329 +			return;
   2.330 +		}
   2.331 +	}
   2.332 +}
   2.333 +
   2.334 +static enum cmd_return
   2.335 +cmd_show(struct pwm_ctx *ctx, int argc, char *argv[])
   2.336 +{
   2.337 +	unsigned int	id;
   2.338 +	struct record	*record;
   2.339 +	int		i;
   2.340 +	enum field_type	type;
   2.341 +	int		fields[COUNTOF(field_names)] = { 0 };
   2.342 +
   2.343 +	if (argc < 2) {
   2.344 +		return (CMD_USAGE);
   2.345 +	}
   2.346 +
   2.347 +	if (parse_id(argv[1], &id) != 0) {
   2.348 +		fprintf(stderr, "invalid id %s\n", argv[1]);
   2.349 +		return (CMD_ERR);
   2.350 +	}
   2.351 +
   2.352 +	for (i = 2; i < argc; i++) {
   2.353 +		type = parse_field_name(argv[i]);
   2.354 +		if (type < 0) {
   2.355 +			fprintf(stderr, "bad field name \"%s\"\n", argv[i]);
   2.356 +			return (CMD_ERR);
   2.357 +		}
   2.358 +		fields[type] = 1;
   2.359 +	}
   2.360 +
   2.361 +	record = pwfile_get_record(ctx, id);
   2.362 +	if (record == NULL) {
   2.363 +		fprintf(stderr, "record %u does not exist\n", id);
   2.364 +		return (CMD_ERR);
   2.365 +	}
   2.366 +	print_record(record, fields, 1, stdout);
   2.367 +	pwfile_destroy_record(record);
   2.368 +
   2.369 +	return (CMD_OK);
   2.370 +}
   2.371 +
   2.372 +static enum cmd_return
   2.373 +cmd_pipe(struct pwm_ctx *ctx, int argc, char *argv[])
   2.374 +{
   2.375 +	enum cmd_return	retval = CMD_ERR;
   2.376 +	unsigned int	id;
   2.377 +	struct record	*record = NULL;
   2.378 +	enum field_type	type;
   2.379 +	int		fields[COUNTOF(field_names)] = { 0 };
   2.380 +	FILE		*fp = NULL;
   2.381 +
   2.382 +	if (argc != 4) {
   2.383 +		return (CMD_USAGE);
   2.384 +	}
   2.385 +
   2.386 +	if (parse_id(argv[1], &id) != 0) {
   2.387 +		fprintf(stderr, "invalid id %s\n", argv[1]);
   2.388 +		return (CMD_ERR);
   2.389 +	}
   2.390 +
   2.391 +	type = parse_field_name(argv[2]);
   2.392 +	if (type < 0) {
   2.393 +		fprintf(stderr, "bad field name \"%s\"\n", argv[2]);
   2.394 +		return (CMD_ERR);
   2.395 +	}
   2.396 +	fields[type] = 1;
   2.397 +
   2.398 +	fp = popen(argv[3], "w");
   2.399 +	if (fp == NULL) {
   2.400 +		warn("popen");
   2.401 +		goto out;
   2.402 +	}
   2.403 +
   2.404 +	record = pwfile_get_record(ctx, id);
   2.405 +	if (record == NULL) {
   2.406 +		fprintf(stderr, "record %u does not exist\n", id);
   2.407 +		goto out;
   2.408 +	}
   2.409 +
   2.410 +	print_record(record, fields, 0, fp);
   2.411 +
   2.412 +	retval = CMD_OK;
   2.413 +
   2.414 +out:
   2.415 +	pwfile_destroy_record(record);
   2.416 +	if (fp != NULL) {
   2.417 +		pclose(fp);
   2.418 +	}
   2.419 +
   2.420 +	return (retval);
   2.421 +}
   2.422 +
   2.423 +static enum cmd_return
   2.424 +cmd_creategroup(struct pwm_ctx *ctx, int argc, char *argv[])
   2.425 +{
   2.426 +	if (argc != 2) {
   2.427 +		return (CMD_USAGE);
   2.428 +	}
   2.429 +
   2.430 +	if (pwfile_create_group(ctx, argv[1]) != 0) {
   2.431 +		fprintf(stderr, "group \"%s\" already exists\n", argv[1]);
   2.432 +		return (CMD_ERR);
   2.433 +	}
   2.434 +
   2.435 +	return (CMD_OK);
   2.436 +}
   2.437 +
   2.438 +static enum cmd_return
   2.439 +cmd_removegroup(struct pwm_ctx *ctx, int argc, char *argv[])
   2.440 +{
   2.441 +	if (argc != 2) {
   2.442 +		return (CMD_USAGE);
   2.443 +	}
   2.444 +
   2.445 +	if (pwfile_remove_group(ctx, argv[1]) != 0) {
   2.446 +		fprintf(stderr, "group \"%s\" does not exist\n", argv[1]);
   2.447 +		return (CMD_ERR);
   2.448 +	}
   2.449 +
   2.450 +	return (CMD_OK);
   2.451 +}
   2.452 +
   2.453 +static enum cmd_return
   2.454 +cmd_changepassword(struct pwm_ctx *ctx, int argc, char *argv[])
   2.455 +{
   2.456 +	size_t	password_len;
   2.457 +	char	password_buf[PWS3_MAX_PASSWORD_LEN + 1];
   2.458 +	char	confirm_buf[PWS3_MAX_PASSWORD_LEN + 1];
   2.459 +
   2.460 +	if (argc > 2) {
   2.461 +		return (CMD_USAGE);
   2.462 +	} else if (argc == 2) {
   2.463 +		password_len = strlen(argv[1]);
   2.464 +		if (password_len == 0) {
   2.465 +			fprintf(stderr, "password must not be empty\n");
   2.466 +			return (CMD_ERR);
   2.467 +		} else if (password_len + 1 > sizeof (ctx->password)) {
   2.468 +			fprintf(stderr, "password too long\n");
   2.469 +			return (CMD_ERR);
   2.470 +		}
   2.471 +		memcpy(ctx->password, argv[1], password_len + 1);
   2.472 +	} else {
   2.473 +		if (readpassphrase("Enter password: ", password_buf,
   2.474 +		    sizeof (password_buf), RPP_ECHO_OFF | RPP_REQUIRE_TTY) ==
   2.475 +		    NULL) {
   2.476 +			err(1, "readpassphrase");
   2.477 +		}
   2.478 +		password_len = strlen(password_buf);
   2.479 +		if (password_len == 0) {
   2.480 +			fprintf(stderr, "password must not be empty\n");
   2.481 +			return (CMD_ERR);
   2.482 +		}
   2.483 +		if (readpassphrase("Confirm password: ", confirm_buf,
   2.484 +		    sizeof (confirm_buf),
   2.485 +		    RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
   2.486 +			err(1, "readpassphrase");
   2.487 +		}
   2.488 +		if (strcmp(password_buf, confirm_buf) != 0) {
   2.489 +			fprintf(stderr, "passwords do not match\n");
   2.490 +			return (CMD_ERR);
   2.491 +		}
   2.492 +		memcpy(ctx->password, password_buf, password_len + 1);
   2.493 +	}
   2.494 +
   2.495 +	return (CMD_OK);
   2.496 +}
   2.497 +
   2.498 +static enum cmd_return
   2.499 +cmd_help(struct pwm_ctx *ctx, int argc, char *argv[])
   2.500 +{
   2.501 +	struct cmd	*cmd;
   2.502 +
   2.503 +	if (argc != 1) {
   2.504 +		return (CMD_USAGE);
   2.505 +	}
   2.506 +
   2.507 +	printf("Commands:\n");
   2.508 +	for (cmd = cmds; cmd->cmd_func != NULL; cmd++) {
   2.509 +		printf("%-2s %-16s %s\n", cmd->abbrev_cmd, cmd->full_cmd,
   2.510 +		    cmd->description);
   2.511 +	}
   2.512 +
   2.513 +	return (CMD_OK);
   2.514 +}
   2.515 +
   2.516 +static enum cmd_return
   2.517 +cmd_write(struct pwm_ctx *ctx, int argc, char *argv[])
   2.518 +{
   2.519 +	if (argc != 1) {
   2.520 +		return (CMD_USAGE);
   2.521 +	}
   2.522 +
   2.523 +	return ((pwfile_write_file(ctx) == 0) ? CMD_OK : CMD_ERR);
   2.524 +}
   2.525 +
   2.526 +static enum cmd_return
   2.527 +cmd_quit(struct pwm_ctx *ctx, int argc, char *argv[])
   2.528 +{
   2.529 +	if (argc != 1) {
   2.530 +		return (CMD_USAGE);
   2.531 +	}
   2.532 +
   2.533 +	return (CMD_QUIT);
   2.534 +}
   2.535 +
   2.536 +struct cmd *
   2.537 +cmd_match(const char *cmd_name)
   2.538 +{
   2.539 +	size_t	i;
   2.540 +
   2.541 +	for (i = 0; cmds[i].cmd_func != NULL; i++) {
   2.542 +		if ((strcmp(cmds[i].abbrev_cmd, cmd_name) == 0) ||
   2.543 +		    (strcmp(cmds[i].full_cmd, cmd_name) == 0)) {
   2.544 +			return (&cmds[i]);
   2.545 +		}
   2.546 +	}
   2.547 +
   2.548 +	return (NULL);
   2.549 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/cmd.h	Thu Jan 19 22:39:51 2017 +0100
     3.3 @@ -0,0 +1,46 @@
     3.4 +/*
     3.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     3.6 + *
     3.7 + * Permission is hereby granted, free of charge, to any person obtaining
     3.8 + * a copy of this software and associated documentation files (the
     3.9 + * "Software"), to deal in the Software without restriction, including
    3.10 + * without limitation the rights to use, copy, modify, merge, publish,
    3.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    3.12 + * permit persons to whom the Software is furnished to do so, subject to
    3.13 + * the following conditions:
    3.14 + *
    3.15 + * The above copyright notice and this permission notice shall be included
    3.16 + * in all copies or substantial portions of the Software.
    3.17 + *
    3.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    3.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    3.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    3.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    3.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    3.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    3.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    3.25 + */
    3.26 +
    3.27 +#ifndef	CMD_H
    3.28 +#define	CMD_H
    3.29 +
    3.30 +#include "pwm.h"
    3.31 +
    3.32 +enum cmd_return {
    3.33 +	CMD_OK,
    3.34 +	CMD_ERR,
    3.35 +	CMD_USAGE,
    3.36 +	CMD_QUIT
    3.37 +};
    3.38 +
    3.39 +struct cmd {
    3.40 +	const char	*abbrev_cmd;
    3.41 +	const char	*full_cmd;
    3.42 +	const char	*usage;
    3.43 +	const char	*description;
    3.44 +	enum cmd_return (*cmd_func)(struct pwm_ctx *, int argc, char *argv[]);
    3.45 +};
    3.46 +
    3.47 +struct cmd *	cmd_match(const char *);
    3.48 +
    3.49 +#endif /* CMD_H */
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/compat.h	Thu Jan 19 22:39:51 2017 +0100
     4.3 @@ -0,0 +1,54 @@
     4.4 +/*
     4.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     4.6 + *
     4.7 + * Permission is hereby granted, free of charge, to any person obtaining
     4.8 + * a copy of this software and associated documentation files (the
     4.9 + * "Software"), to deal in the Software without restriction, including
    4.10 + * without limitation the rights to use, copy, modify, merge, publish,
    4.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    4.12 + * permit persons to whom the Software is furnished to do so, subject to
    4.13 + * the following conditions:
    4.14 + *
    4.15 + * The above copyright notice and this permission notice shall be included
    4.16 + * in all copies or substantial portions of the Software.
    4.17 + *
    4.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    4.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    4.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    4.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    4.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    4.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    4.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    4.25 + */
    4.26 +
    4.27 +#ifndef	COMPAT_H
    4.28 +#define	COMPAT_H
    4.29 +
    4.30 +/* for glibc asprintf, getline */
    4.31 +#define	_BSD_SOURCE
    4.32 +
    4.33 +#ifndef	HAVE_ASPRINTF
    4.34 +#include "compat/asprintf.h"
    4.35 +#endif /* !HAVE_ASPRINTF */
    4.36 +
    4.37 +#ifndef	HAVE_ERR_H
    4.38 +#include "compat/err.h"
    4.39 +#endif /* !HAVE_ERR_H */
    4.40 +
    4.41 +#ifndef	HAVE_GETENTROPY
    4.42 +#include "compat/getentropy.h"
    4.43 +#endif /* !HAVE_GETENTROPY */
    4.44 +
    4.45 +#ifndef	HAVE_READPASSPHRASE_H
    4.46 +#include "compat/readpassphrase.h"
    4.47 +#endif /* !HAVE_READPASSPHRASE_H */
    4.48 +
    4.49 +#ifndef	HAVE_SETPROGNAME
    4.50 +#include "compat/setprogname.h"
    4.51 +#endif /* !HAVE_SETPROGNAME */
    4.52 +
    4.53 +#ifndef	HAVE_SYS_TREE_H
    4.54 +#include "compat/tree.h"
    4.55 +#endif /* !HAVE_SYS_TREE_H */
    4.56 +
    4.57 +#endif /* COMPAT_H */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/compat/asprintf.c	Thu Jan 19 22:39:51 2017 +0100
     5.3 @@ -0,0 +1,73 @@
     5.4 +/*
     5.5 + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name>
     5.6 + *
     5.7 + * Permission is hereby granted, free of charge, to any person obtaining
     5.8 + * a copy of this software and associated documentation files (the
     5.9 + * "Software"), to deal in the Software without restriction, including
    5.10 + * without limitation the rights to use, copy, modify, merge, publish,
    5.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    5.12 + * permit persons to whom the Software is furnished to do so, subject to
    5.13 + * the following conditions:
    5.14 + *
    5.15 + * The above copyright notice and this permission notice shall be included
    5.16 + * in all copies or substantial portions of the Software.
    5.17 + *
    5.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    5.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    5.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    5.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    5.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    5.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    5.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    5.25 + */
    5.26 +
    5.27 +#include <stdlib.h>
    5.28 +#include <stdio.h>
    5.29 +#include <string.h>
    5.30 +#include <errno.h>
    5.31 +
    5.32 +#include "asprintf.h"
    5.33 +
    5.34 +int
    5.35 +vasprintf(char **strp, const char *fmt, va_list args)
    5.36 +{
    5.37 +	int	len;
    5.38 +	va_list	args_new;
    5.39 +	char	*str;
    5.40 +
    5.41 +	*strp = NULL;
    5.42 +	va_copy(args_new, args);
    5.43 +
    5.44 +	len = vsnprintf(NULL, 0, fmt, args);
    5.45 +	if (len < 0) {
    5.46 +		goto out;
    5.47 +	}
    5.48 +
    5.49 +	str = malloc(len + 1);
    5.50 +	if (str == NULL) {
    5.51 +		goto out;
    5.52 +	}
    5.53 +	len = vsnprintf(str, len + 1, fmt, args_new);
    5.54 +	if (len < 0) {
    5.55 +		free(str);
    5.56 +		goto out;
    5.57 +	}
    5.58 +	*strp = str;
    5.59 +out:
    5.60 +	va_end(args_new);
    5.61 +
    5.62 +	return (len);
    5.63 +}
    5.64 +
    5.65 +int
    5.66 +asprintf(char **strp, const char *fmt, ...)
    5.67 +{
    5.68 +	int	len;
    5.69 +	va_list	args;
    5.70 +
    5.71 +	va_start(args, fmt);
    5.72 +	len = vasprintf(strp, fmt, args);
    5.73 +	va_end(args);
    5.74 +
    5.75 +	return (len);
    5.76 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/compat/asprintf.h	Thu Jan 19 22:39:51 2017 +0100
     6.3 @@ -0,0 +1,32 @@
     6.4 +/*
     6.5 + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name>
     6.6 + *
     6.7 + * Permission is hereby granted, free of charge, to any person obtaining
     6.8 + * a copy of this software and associated documentation files (the
     6.9 + * "Software"), to deal in the Software without restriction, including
    6.10 + * without limitation the rights to use, copy, modify, merge, publish,
    6.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    6.12 + * permit persons to whom the Software is furnished to do so, subject to
    6.13 + * the following conditions:
    6.14 + *
    6.15 + * The above copyright notice and this permission notice shall be included
    6.16 + * in all copies or substantial portions of the Software.
    6.17 + *
    6.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    6.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    6.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    6.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    6.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    6.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    6.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    6.25 + */
    6.26 +
    6.27 +#ifndef	ASPRINTF_H
    6.28 +#define	ASPRINTF_H
    6.29 +
    6.30 +#include <stdarg.h>
    6.31 +
    6.32 +int	asprintf(char **, const char *, ...);
    6.33 +int	vasprintf(char **, const char *, va_list);
    6.34 +
    6.35 +#endif /* !ASPRINTF_H */
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/compat/err.c	Thu Jan 19 22:39:51 2017 +0100
     7.3 @@ -0,0 +1,114 @@
     7.4 +/*
     7.5 + * Copyright (C) 2011 Guido Berhoerster <guido+pwm@berhoerster.name>
     7.6 + *
     7.7 + * Permission is hereby granted, free of charge, to any person obtaining
     7.8 + * a copy of this software and associated documentation files (the
     7.9 + * "Software"), to deal in the Software without restriction, including
    7.10 + * without limitation the rights to use, copy, modify, merge, publish,
    7.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    7.12 + * permit persons to whom the Software is furnished to do so, subject to
    7.13 + * the following conditions:
    7.14 + *
    7.15 + * The above copyright notice and this permission notice shall be included
    7.16 + * in all copies or substantial portions of the Software.
    7.17 + *
    7.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    7.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    7.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    7.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    7.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    7.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    7.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    7.25 + */
    7.26 +
    7.27 +#include <stdlib.h>
    7.28 +#include <stdio.h>
    7.29 +#include <string.h>
    7.30 +#include <errno.h>
    7.31 +
    7.32 +#include "err.h"
    7.33 +#include "../compat.h"
    7.34 +
    7.35 +void
    7.36 +err(int eval, const char *fmt, ...)
    7.37 +{
    7.38 +	va_list	args;
    7.39 +
    7.40 +	va_start(args, fmt);
    7.41 +	vwarn(fmt, args);
    7.42 +	va_end(args);
    7.43 +
    7.44 +	exit(eval);
    7.45 +}
    7.46 +
    7.47 +void
    7.48 +errx(int eval, const char *fmt, ...)
    7.49 +{
    7.50 +	va_list	args;
    7.51 +
    7.52 +	va_start(args, fmt);
    7.53 +	vwarnx(fmt, args);
    7.54 +	va_end(args);
    7.55 +
    7.56 +	exit(eval);
    7.57 +}
    7.58 +
    7.59 +void
    7.60 +warn(const char *fmt, ...)
    7.61 +{
    7.62 +	va_list	args;
    7.63 +
    7.64 +	va_start(args, fmt);
    7.65 +	vwarn(fmt, args);
    7.66 +	va_end(args);
    7.67 +}
    7.68 +
    7.69 +void
    7.70 +warnx(const char *fmt, ...)
    7.71 +{
    7.72 +	va_list	args;
    7.73 +
    7.74 +	va_start(args, fmt);
    7.75 +	vwarnx(fmt, args);
    7.76 +	va_end(args);
    7.77 +}
    7.78 +
    7.79 +void
    7.80 +verr(int eval, const char *fmt, va_list args)
    7.81 +{
    7.82 +	vwarn(fmt, args);
    7.83 +
    7.84 +	exit(eval);
    7.85 +}
    7.86 +
    7.87 +void
    7.88 +verrx(int eval, const char *fmt, va_list args)
    7.89 +{
    7.90 +	vwarnx(fmt, args);
    7.91 +
    7.92 +	exit(eval);
    7.93 +}
    7.94 +
    7.95 +void
    7.96 +vwarn(const char *fmt, va_list args)
    7.97 +{
    7.98 +	int	old_errno = errno;
    7.99 +
   7.100 +	fprintf(stderr, "%s: ", getprogname());
   7.101 +	if (fmt != NULL) {
   7.102 +		vfprintf(stderr, fmt, args);
   7.103 +		fprintf(stderr, ": ");
   7.104 +	}
   7.105 +	fprintf(stderr, "%s\n", strerror(old_errno));
   7.106 +	errno = old_errno;
   7.107 +}
   7.108 +
   7.109 +void
   7.110 +vwarnx(const char *fmt, va_list args)
   7.111 +{
   7.112 +	fprintf(stderr, "%s: ", getprogname());
   7.113 +	if (fmt != NULL) {
   7.114 +		vfprintf(stderr, fmt, args);
   7.115 +	}
   7.116 +	fputc('\n', stderr);
   7.117 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/compat/err.h	Thu Jan 19 22:39:51 2017 +0100
     8.3 @@ -0,0 +1,38 @@
     8.4 +/*
     8.5 + * Copyright (C) 2011 Guido Berhoerster <guido+pwm@berhoerster.name>
     8.6 + *
     8.7 + * Permission is hereby granted, free of charge, to any person obtaining
     8.8 + * a copy of this software and associated documentation files (the
     8.9 + * "Software"), to deal in the Software without restriction, including
    8.10 + * without limitation the rights to use, copy, modify, merge, publish,
    8.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    8.12 + * permit persons to whom the Software is furnished to do so, subject to
    8.13 + * the following conditions:
    8.14 + *
    8.15 + * The above copyright notice and this permission notice shall be included
    8.16 + * in all copies or substantial portions of the Software.
    8.17 + *
    8.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    8.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    8.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    8.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    8.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    8.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    8.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    8.25 + */
    8.26 +
    8.27 +#ifndef ERR_H
    8.28 +#define ERR_H
    8.29 +
    8.30 +#include <stdarg.h>
    8.31 +
    8.32 +void	err(int, const char *, ...);
    8.33 +void	errx(int, const char *, ...);
    8.34 +void	warn(const char *, ...);
    8.35 +void	warnx(const char *, ...);
    8.36 +void	verr(int, const char *, va_list);
    8.37 +void	verrx(int, const char *, va_list);
    8.38 +void	vwarn(const char *, va_list);
    8.39 +void	vwarnx(const char *, va_list);
    8.40 +
    8.41 +#endif /* ERR_H */
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/compat/getentropy.c	Thu Jan 19 22:39:51 2017 +0100
     9.3 @@ -0,0 +1,110 @@
     9.4 +/*
     9.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
     9.6 + *
     9.7 + * Permission is hereby granted, free of charge, to any person obtaining
     9.8 + * a copy of this software and associated documentation files (the
     9.9 + * "Software"), to deal in the Software without restriction, including
    9.10 + * without limitation the rights to use, copy, modify, merge, publish,
    9.11 + * distribute, sublicense, and/or sell copies of the Software, and to
    9.12 + * permit persons to whom the Software is furnished to do so, subject to
    9.13 + * the following conditions:
    9.14 + *
    9.15 + * The above copyright notice and this permission notice shall be included
    9.16 + * in all copies or substantial portions of the Software.
    9.17 + *
    9.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    9.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    9.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    9.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    9.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    9.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    9.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    9.25 + */
    9.26 +
    9.27 +/* needed for syscall(2) on Linux */
    9.28 +#define	_GNU_SOURCE
    9.29 +
    9.30 +/* Linux >= 3.17 has getrandom(2) system call */
    9.31 +#ifdef	__linux__
    9.32 +#include <unistd.h>
    9.33 +#include <sys/syscall.h>
    9.34 +#include <linux/random.h>
    9.35 +#ifdef	SYS_getrandom
    9.36 +#define	HAVE_GETRANDOM
    9.37 +#endif /* SYS_getrandom */
    9.38 +#endif /* __linux__ */
    9.39 +/*
    9.40 + * on unknown Unix systems without getentropy(2) or Linux without getrandom(2)
    9.41 + * fall back to * reading from /dev/(u)random
    9.42 + */
    9.43 +#ifndef	HAVE_GETRANDOM
    9.44 +#include <stdio.h>
    9.45 +#ifndef	RANDOM_DEVICE
    9.46 +#ifdef	__linux__
    9.47 +/* on Linux /dev/urandom should be good enough */
    9.48 +#define	RANDOM_DEVICE	"/dev/urandom"
    9.49 +#else /* __linux__ */
    9.50 +/* on unknown Unix systems use the possibly blocking /dev/random */
    9.51 +#define	RANDOM_DEVICE	"/dev/random"
    9.52 +#endif /* __linux__ */
    9.53 +#endif /* !RANDOM_DEVICE */
    9.54 +#endif /* !HAVE_GETRANDOM */
    9.55 +#include <errno.h>
    9.56 +
    9.57 +#ifdef	HAVE_GETRANDOM
    9.58 +static int
    9.59 +getentropy_linux_getrandom(void *buf, size_t buf_len)
    9.60 +{
    9.61 +	int retval;
    9.62 +
    9.63 +	retval = syscall(SYS_getrandom, buf, buf_len, 0);
    9.64 +	if (retval < 0) {
    9.65 +		return (-1);
    9.66 +	} else if ((size_t)retval != buf_len) {
    9.67 +		errno = EIO;
    9.68 +		return (-1);
    9.69 +	}
    9.70 +
    9.71 +	return (0);
    9.72 +}
    9.73 +#else
    9.74 +static int
    9.75 +getentropy_dev_random(void *buf, size_t buf_len)
    9.76 +{
    9.77 +	FILE	*fp;
    9.78 +	int	saved_errno;
    9.79 +
    9.80 +	fp = fopen(RANDOM_DEVICE, "r");
    9.81 +	if (fp == NULL) {
    9.82 +		return (-1);
    9.83 +	}
    9.84 +	if (fread(buf, 1, buf_len, fp) != buf_len) {
    9.85 +		saved_errno = errno;
    9.86 +		fclose(fp);
    9.87 +		errno = saved_errno;
    9.88 +		return (-1);
    9.89 +	}
    9.90 +	if (fclose(fp) != 0) {
    9.91 +		return (-1);
    9.92 +	}
    9.93 +
    9.94 +	return (0);
    9.95 +}
    9.96 +#endif /* HAVE_GETRANDOM */
    9.97 +
    9.98 +int
    9.99 +getentropy(void *buf, size_t buf_len)
   9.100 +{
   9.101 +	if (buf_len > 256) {
   9.102 +		errno = EIO;
   9.103 +		return (-1);
   9.104 +	}
   9.105 +
   9.106 +	return (
   9.107 +#ifdef	HAVE_GETRANDOM
   9.108 +	    getentropy_linux_getrandom(
   9.109 +#else /* HAVE_GETRANDOM */
   9.110 +	    getentropy_dev_random(
   9.111 +#endif /* HAVE_GETRANDOM */
   9.112 +	    buf, buf_len));
   9.113 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/compat/getentropy.h	Thu Jan 19 22:39:51 2017 +0100
    10.3 @@ -0,0 +1,31 @@
    10.4 +/*
    10.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    10.6 + *
    10.7 + * Permission is hereby granted, free of charge, to any person obtaining
    10.8 + * a copy of this software and associated documentation files (the
    10.9 + * "Software"), to deal in the Software without restriction, including
   10.10 + * without limitation the rights to use, copy, modify, merge, publish,
   10.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   10.12 + * permit persons to whom the Software is furnished to do so, subject to
   10.13 + * the following conditions:
   10.14 + *
   10.15 + * The above copyright notice and this permission notice shall be included
   10.16 + * in all copies or substantial portions of the Software.
   10.17 + *
   10.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   10.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   10.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   10.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   10.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   10.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   10.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   10.25 + */
   10.26 +
   10.27 +#ifndef	GETENTROPY_H
   10.28 +#define	GETENTROPY_H
   10.29 +
   10.30 +#include <stddef.h>
   10.31 +
   10.32 +int	getentropy(void *, size_t);
   10.33 +
   10.34 +#endif /* !GETENTROPY_H */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/compat/readpassphrase.c	Thu Jan 19 22:39:51 2017 +0100
    11.3 @@ -0,0 +1,209 @@
    11.4 +/*	$OpenBSD: readpassphrase.c,v 1.24 2013/11/24 23:51:29 deraadt Exp $	*/
    11.5 +
    11.6 +/*
    11.7 + * Copyright (c) 2000-2002, 2007, 2010
    11.8 + *	Todd C. Miller <Todd.Miller@courtesan.com>
    11.9 + *
   11.10 + * Permission to use, copy, modify, and distribute this software for any
   11.11 + * purpose with or without fee is hereby granted, provided that the above
   11.12 + * copyright notice and this permission notice appear in all copies.
   11.13 + *
   11.14 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11.15 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   11.16 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   11.17 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   11.18 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   11.19 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   11.20 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   11.21 + *
   11.22 + * Sponsored in part by the Defense Advanced Research Projects
   11.23 + * Agency (DARPA) and Air Force Research Laboratory, Air Force
   11.24 + * Materiel Command, USAF, under agreement number F39502-99-1-0512.
   11.25 + */
   11.26 +
   11.27 +#include <ctype.h>
   11.28 +#include <errno.h>
   11.29 +#include <fcntl.h>
   11.30 +#ifdef	HAVE_PATHS_H
   11.31 +#include <paths.h>
   11.32 +#endif /* HAVE_PATHS_H */
   11.33 +#include <pwd.h>
   11.34 +#include <signal.h>
   11.35 +#include <string.h>
   11.36 +#include <termios.h>
   11.37 +#include <unistd.h>
   11.38 +#include "readpassphrase.h"
   11.39 +
   11.40 +#ifndef TCSASOFT
   11.41 +#define TCSASOFT 0
   11.42 +#endif /* !TCSASOFT */
   11.43 +
   11.44 +#ifndef	_NSIG
   11.45 +#ifdef NSIG
   11.46 +#define	_NSIG NSIG
   11.47 +#else /* NSIG */
   11.48 +#define	_NSIG 128
   11.49 +#endif /* NSIG */
   11.50 +#endif /* !_NSIG */
   11.51 +
   11.52 +#ifndef	_PATH_TTY
   11.53 +#define	_PATH_TTY	"/dev/tty"
   11.54 +#endif
   11.55 +
   11.56 +static volatile sig_atomic_t signo[_NSIG];
   11.57 +
   11.58 +static void handler(int);
   11.59 +
   11.60 +char *
   11.61 +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
   11.62 +{
   11.63 +	ssize_t nr;
   11.64 +	int input, output, save_errno, i, need_restart;
   11.65 +	char ch, *p, *end;
   11.66 +	struct termios term, oterm;
   11.67 +	struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
   11.68 +	struct sigaction savetstp, savettin, savettou, savepipe;
   11.69 +
   11.70 +	/* I suppose we could alloc on demand in this case (XXX). */
   11.71 +	if (bufsiz == 0) {
   11.72 +		errno = EINVAL;
   11.73 +		return(NULL);
   11.74 +	}
   11.75 +
   11.76 +restart:
   11.77 +	for (i = 0; i < _NSIG; i++)
   11.78 +		signo[i] = 0;
   11.79 +	nr = -1;
   11.80 +	save_errno = 0;
   11.81 +	need_restart = 0;
   11.82 +	/*
   11.83 +	 * Read and write to /dev/tty if available.  If not, read from
   11.84 +	 * stdin and write to stderr unless a tty is required.
   11.85 +	 */
   11.86 +	if ((flags & RPP_STDIN) ||
   11.87 +	    (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
   11.88 +		if (flags & RPP_REQUIRE_TTY) {
   11.89 +			errno = ENOTTY;
   11.90 +			return(NULL);
   11.91 +		}
   11.92 +		input = STDIN_FILENO;
   11.93 +		output = STDERR_FILENO;
   11.94 +	}
   11.95 +
   11.96 +	/*
   11.97 +	 * Turn off echo if possible.
   11.98 +	 * If we are using a tty but are not the foreground pgrp this will
   11.99 +	 * generate SIGTTOU, so do it *before* installing the signal handlers.
  11.100 +	 */
  11.101 +	if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
  11.102 +		memcpy(&term, &oterm, sizeof(term));
  11.103 +		if (!(flags & RPP_ECHO_ON))
  11.104 +			term.c_lflag &= ~(ECHO | ECHONL);
  11.105 +#ifdef VSTATUS
  11.106 +		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
  11.107 +			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
  11.108 +#endif /* VSTATUS */
  11.109 +		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
  11.110 +	} else {
  11.111 +		memset(&term, 0, sizeof(term));
  11.112 +		term.c_lflag |= ECHO;
  11.113 +		memset(&oterm, 0, sizeof(oterm));
  11.114 +		oterm.c_lflag |= ECHO;
  11.115 +	}
  11.116 +
  11.117 +	/*
  11.118 +	 * Catch signals that would otherwise cause the user to end
  11.119 +	 * up with echo turned off in the shell.  Don't worry about
  11.120 +	 * things like SIGXCPU and SIGVTALRM for now.
  11.121 +	 */
  11.122 +	sigemptyset(&sa.sa_mask);
  11.123 +	sa.sa_flags = 0;		/* don't restart system calls */
  11.124 +	sa.sa_handler = handler;
  11.125 +	(void)sigaction(SIGALRM, &sa, &savealrm);
  11.126 +	(void)sigaction(SIGHUP, &sa, &savehup);
  11.127 +	(void)sigaction(SIGINT, &sa, &saveint);
  11.128 +	(void)sigaction(SIGPIPE, &sa, &savepipe);
  11.129 +	(void)sigaction(SIGQUIT, &sa, &savequit);
  11.130 +	(void)sigaction(SIGTERM, &sa, &saveterm);
  11.131 +	(void)sigaction(SIGTSTP, &sa, &savetstp);
  11.132 +	(void)sigaction(SIGTTIN, &sa, &savettin);
  11.133 +	(void)sigaction(SIGTTOU, &sa, &savettou);
  11.134 +
  11.135 +	if (!(flags & RPP_STDIN))
  11.136 +		(void)write(output, prompt, strlen(prompt));
  11.137 +	end = buf + bufsiz - 1;
  11.138 +	p = buf;
  11.139 +	while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
  11.140 +		if (p < end) {
  11.141 +			if ((flags & RPP_SEVENBIT))
  11.142 +				ch &= 0x7f;
  11.143 +			if (isalpha((unsigned char)ch)) {
  11.144 +				if ((flags & RPP_FORCELOWER))
  11.145 +					ch = (char)tolower((unsigned char)ch);
  11.146 +				if ((flags & RPP_FORCEUPPER))
  11.147 +					ch = (char)toupper((unsigned char)ch);
  11.148 +			}
  11.149 +			*p++ = ch;
  11.150 +		}
  11.151 +	}
  11.152 +	*p = '\0';
  11.153 +	save_errno = errno;
  11.154 +	if (!(term.c_lflag & ECHO))
  11.155 +		(void)write(output, "\n", 1);
  11.156 +
  11.157 +	/* Restore old terminal settings and signals. */
  11.158 +	if (memcmp(&term, &oterm, sizeof(term)) != 0) {
  11.159 +		while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
  11.160 +		    errno == EINTR && !signo[SIGTTOU])
  11.161 +			continue;
  11.162 +	}
  11.163 +	(void)sigaction(SIGALRM, &savealrm, NULL);
  11.164 +	(void)sigaction(SIGHUP, &savehup, NULL);
  11.165 +	(void)sigaction(SIGINT, &saveint, NULL);
  11.166 +	(void)sigaction(SIGQUIT, &savequit, NULL);
  11.167 +	(void)sigaction(SIGPIPE, &savepipe, NULL);
  11.168 +	(void)sigaction(SIGTERM, &saveterm, NULL);
  11.169 +	(void)sigaction(SIGTSTP, &savetstp, NULL);
  11.170 +	(void)sigaction(SIGTTIN, &savettin, NULL);
  11.171 +	(void)sigaction(SIGTTOU, &savettou, NULL);
  11.172 +	if (input != STDIN_FILENO)
  11.173 +		(void)close(input);
  11.174 +
  11.175 +	/*
  11.176 +	 * If we were interrupted by a signal, resend it to ourselves
  11.177 +	 * now that we have restored the signal handlers.
  11.178 +	 */
  11.179 +	for (i = 0; i < _NSIG; i++) {
  11.180 +		if (signo[i]) {
  11.181 +			kill(getpid(), i);
  11.182 +			switch (i) {
  11.183 +			case SIGTSTP:
  11.184 +			case SIGTTIN:
  11.185 +			case SIGTTOU:
  11.186 +				need_restart = 1;
  11.187 +			}
  11.188 +		}
  11.189 +	}
  11.190 +	if (need_restart)
  11.191 +		goto restart;
  11.192 +
  11.193 +	if (save_errno)
  11.194 +		errno = save_errno;
  11.195 +	return(nr == -1 ? NULL : buf);
  11.196 +}
  11.197 +
  11.198 +#if 0
  11.199 +char *
  11.200 +getpass(const char *prompt)
  11.201 +{
  11.202 +	static char buf[_PASSWORD_LEN + 1];
  11.203 +
  11.204 +	return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
  11.205 +}
  11.206 +#endif
  11.207 +
  11.208 +static void handler(int s)
  11.209 +{
  11.210 +
  11.211 +	signo[s] = 1;
  11.212 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/compat/readpassphrase.h	Thu Jan 19 22:39:51 2017 +0100
    12.3 @@ -0,0 +1,38 @@
    12.4 +/*	$OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $	*/
    12.5 +
    12.6 +/*
    12.7 + * Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
    12.8 + *
    12.9 + * Permission to use, copy, modify, and distribute this software for any
   12.10 + * purpose with or without fee is hereby granted, provided that the above
   12.11 + * copyright notice and this permission notice appear in all copies.
   12.12 + *
   12.13 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12.14 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12.15 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   12.16 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   12.17 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   12.18 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   12.19 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   12.20 + *
   12.21 + * Sponsored in part by the Defense Advanced Research Projects
   12.22 + * Agency (DARPA) and Air Force Research Laboratory, Air Force
   12.23 + * Materiel Command, USAF, under agreement number F39502-99-1-0512.
   12.24 + */
   12.25 +
   12.26 +#ifndef _READPASSPHRASE_H_
   12.27 +#define _READPASSPHRASE_H_
   12.28 +
   12.29 +#define RPP_ECHO_OFF    0x00		/* Turn off echo (default). */
   12.30 +#define RPP_ECHO_ON     0x01		/* Leave echo on. */
   12.31 +#define RPP_REQUIRE_TTY 0x02		/* Fail if there is no tty. */
   12.32 +#define RPP_FORCELOWER  0x04		/* Force input to lower case. */
   12.33 +#define RPP_FORCEUPPER  0x08		/* Force input to upper case. */
   12.34 +#define RPP_SEVENBIT    0x10		/* Strip the high bit from input. */
   12.35 +#define RPP_STDIN       0x20		/* Read from stdin, not /dev/tty */
   12.36 +
   12.37 +#include <sys/types.h>
   12.38 +
   12.39 +char * readpassphrase(const char *, char *, size_t, int);
   12.40 +
   12.41 +#endif /* !_READPASSPHRASE_H_ */
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/compat/setprogname.c	Thu Jan 19 22:39:51 2017 +0100
    13.3 @@ -0,0 +1,44 @@
    13.4 +/*
    13.5 + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name>
    13.6 + *
    13.7 + * Permission is hereby granted, free of charge, to any person obtaining
    13.8 + * a copy of this software and associated documentation files (the
    13.9 + * "Software"), to deal in the Software without restriction, including
   13.10 + * without limitation the rights to use, copy, modify, merge, publish,
   13.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   13.12 + * permit persons to whom the Software is furnished to do so, subject to
   13.13 + * the following conditions:
   13.14 + *
   13.15 + * The above copyright notice and this permission notice shall be included
   13.16 + * in all copies or substantial portions of the Software.
   13.17 + *
   13.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   13.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   13.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   13.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   13.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   13.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   13.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   13.25 + */
   13.26 +
   13.27 +#include <string.h>
   13.28 +
   13.29 +#include "setprogname.h"
   13.30 +#include "../compat.h"
   13.31 +
   13.32 +static const char *progname = "<unknown_program>";
   13.33 +
   13.34 +void
   13.35 +setprogname(const char *name)
   13.36 +{
   13.37 +	const char	*p;
   13.38 +
   13.39 +	p = strrchr(name, '/');
   13.40 +	progname = (p != NULL) ? p + 1 : name;
   13.41 +}
   13.42 +
   13.43 +const char *
   13.44 +getprogname(void)
   13.45 +{
   13.46 +	return (progname);
   13.47 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/compat/setprogname.h	Thu Jan 19 22:39:51 2017 +0100
    14.3 @@ -0,0 +1,30 @@
    14.4 +/*
    14.5 + * Copyright (C) 2015 Guido Berhoerster <guido+pwm@berhoerster.name>
    14.6 + *
    14.7 + * Permission is hereby granted, free of charge, to any person obtaining
    14.8 + * a copy of this software and associated documentation files (the
    14.9 + * "Software"), to deal in the Software without restriction, including
   14.10 + * without limitation the rights to use, copy, modify, merge, publish,
   14.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   14.12 + * permit persons to whom the Software is furnished to do so, subject to
   14.13 + * the following conditions:
   14.14 + *
   14.15 + * The above copyright notice and this permission notice shall be included
   14.16 + * in all copies or substantial portions of the Software.
   14.17 + *
   14.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   14.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   14.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   14.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   14.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   14.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   14.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   14.25 + */
   14.26 +
   14.27 +#ifndef	SETPROGNAME_H
   14.28 +#define	SETPROGNAME_H
   14.29 +
   14.30 +void		setprogname(const char *);
   14.31 +const char *	getprogname(void);
   14.32 +
   14.33 +#endif /* !SETPROGNAME_H */
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/compat/tree.h	Thu Jan 19 22:39:51 2017 +0100
    15.3 @@ -0,0 +1,748 @@
    15.4 +/*	$OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $	*/
    15.5 +/*
    15.6 + * Copyright 2002 Niels Provos <provos@citi.umich.edu>
    15.7 + * All rights reserved.
    15.8 + *
    15.9 + * Redistribution and use in source and binary forms, with or without
   15.10 + * modification, are permitted provided that the following conditions
   15.11 + * are met:
   15.12 + * 1. Redistributions of source code must retain the above copyright
   15.13 + *    notice, this list of conditions and the following disclaimer.
   15.14 + * 2. Redistributions in binary form must reproduce the above copyright
   15.15 + *    notice, this list of conditions and the following disclaimer in the
   15.16 + *    documentation and/or other materials provided with the distribution.
   15.17 + *
   15.18 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   15.19 + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15.20 + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   15.21 + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   15.22 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   15.23 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   15.24 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   15.25 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   15.26 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   15.27 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   15.28 + */
   15.29 +
   15.30 +#ifndef	_SYS_TREE_H_
   15.31 +#define	_SYS_TREE_H_
   15.32 +
   15.33 +/*
   15.34 + * This file defines data structures for different types of trees:
   15.35 + * splay trees and red-black trees.
   15.36 + *
   15.37 + * A splay tree is a self-organizing data structure.  Every operation
   15.38 + * on the tree causes a splay to happen.  The splay moves the requested
   15.39 + * node to the root of the tree and partly rebalances it.
   15.40 + *
   15.41 + * This has the benefit that request locality causes faster lookups as
   15.42 + * the requested nodes move to the top of the tree.  On the other hand,
   15.43 + * every lookup causes memory writes.
   15.44 + *
   15.45 + * The Balance Theorem bounds the total access time for m operations
   15.46 + * and n inserts on an initially empty tree as O((m + n)lg n).  The
   15.47 + * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
   15.48 + *
   15.49 + * A red-black tree is a binary search tree with the node color as an
   15.50 + * extra attribute.  It fulfills a set of conditions:
   15.51 + *	- every search path from the root to a leaf consists of the
   15.52 + *	  same number of black nodes,
   15.53 + *	- each red node (except for the root) has a black parent,
   15.54 + *	- each leaf node is black.
   15.55 + *
   15.56 + * Every operation on a red-black tree is bounded as O(lg n).
   15.57 + * The maximum height of a red-black tree is 2lg (n+1).
   15.58 + */
   15.59 +
   15.60 +#define SPLAY_HEAD(name, type)						\
   15.61 +struct name {								\
   15.62 +	struct type *sph_root; /* root of the tree */			\
   15.63 +}
   15.64 +
   15.65 +#define SPLAY_INITIALIZER(root)						\
   15.66 +	{ NULL }
   15.67 +
   15.68 +#define SPLAY_INIT(root) do {						\
   15.69 +	(root)->sph_root = NULL;					\
   15.70 +} while (0)
   15.71 +
   15.72 +#define SPLAY_ENTRY(type)						\
   15.73 +struct {								\
   15.74 +	struct type *spe_left; /* left element */			\
   15.75 +	struct type *spe_right; /* right element */			\
   15.76 +}
   15.77 +
   15.78 +#define SPLAY_LEFT(elm, field)		(elm)->field.spe_left
   15.79 +#define SPLAY_RIGHT(elm, field)		(elm)->field.spe_right
   15.80 +#define SPLAY_ROOT(head)		(head)->sph_root
   15.81 +#define SPLAY_EMPTY(head)		(SPLAY_ROOT(head) == NULL)
   15.82 +
   15.83 +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
   15.84 +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do {			\
   15.85 +	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field);	\
   15.86 +	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
   15.87 +	(head)->sph_root = tmp;						\
   15.88 +} while (0)
   15.89 +	
   15.90 +#define SPLAY_ROTATE_LEFT(head, tmp, field) do {			\
   15.91 +	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field);	\
   15.92 +	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
   15.93 +	(head)->sph_root = tmp;						\
   15.94 +} while (0)
   15.95 +
   15.96 +#define SPLAY_LINKLEFT(head, tmp, field) do {				\
   15.97 +	SPLAY_LEFT(tmp, field) = (head)->sph_root;			\
   15.98 +	tmp = (head)->sph_root;						\
   15.99 +	(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);		\
  15.100 +} while (0)
  15.101 +
  15.102 +#define SPLAY_LINKRIGHT(head, tmp, field) do {				\
  15.103 +	SPLAY_RIGHT(tmp, field) = (head)->sph_root;			\
  15.104 +	tmp = (head)->sph_root;						\
  15.105 +	(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);	\
  15.106 +} while (0)
  15.107 +
  15.108 +#define SPLAY_ASSEMBLE(head, node, left, right, field) do {		\
  15.109 +	SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field);	\
  15.110 +	SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
  15.111 +	SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field);	\
  15.112 +	SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field);	\
  15.113 +} while (0)
  15.114 +
  15.115 +/* Generates prototypes and inline functions */
  15.116 +
  15.117 +#define SPLAY_PROTOTYPE(name, type, field, cmp)				\
  15.118 +void name##_SPLAY(struct name *, struct type *);			\
  15.119 +void name##_SPLAY_MINMAX(struct name *, int);				\
  15.120 +struct type *name##_SPLAY_INSERT(struct name *, struct type *);		\
  15.121 +struct type *name##_SPLAY_REMOVE(struct name *, struct type *);		\
  15.122 +									\
  15.123 +/* Finds the node with the same key as elm */				\
  15.124 +static __inline struct type *						\
  15.125 +name##_SPLAY_FIND(struct name *head, struct type *elm)			\
  15.126 +{									\
  15.127 +	if (SPLAY_EMPTY(head))						\
  15.128 +		return(NULL);						\
  15.129 +	name##_SPLAY(head, elm);					\
  15.130 +	if ((cmp)(elm, (head)->sph_root) == 0)				\
  15.131 +		return (head->sph_root);				\
  15.132 +	return (NULL);							\
  15.133 +}									\
  15.134 +									\
  15.135 +static __inline struct type *						\
  15.136 +name##_SPLAY_NEXT(struct name *head, struct type *elm)			\
  15.137 +{									\
  15.138 +	name##_SPLAY(head, elm);					\
  15.139 +	if (SPLAY_RIGHT(elm, field) != NULL) {				\
  15.140 +		elm = SPLAY_RIGHT(elm, field);				\
  15.141 +		while (SPLAY_LEFT(elm, field) != NULL) {		\
  15.142 +			elm = SPLAY_LEFT(elm, field);			\
  15.143 +		}							\
  15.144 +	} else								\
  15.145 +		elm = NULL;						\
  15.146 +	return (elm);							\
  15.147 +}									\
  15.148 +									\
  15.149 +static __inline struct type *						\
  15.150 +name##_SPLAY_MIN_MAX(struct name *head, int val)			\
  15.151 +{									\
  15.152 +	name##_SPLAY_MINMAX(head, val);					\
  15.153 +        return (SPLAY_ROOT(head));					\
  15.154 +}
  15.155 +
  15.156 +/* Main splay operation.
  15.157 + * Moves node close to the key of elm to top
  15.158 + */
  15.159 +#define SPLAY_GENERATE(name, type, field, cmp)				\
  15.160 +struct type *								\
  15.161 +name##_SPLAY_INSERT(struct name *head, struct type *elm)		\
  15.162 +{									\
  15.163 +    if (SPLAY_EMPTY(head)) {						\
  15.164 +	    SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL;	\
  15.165 +    } else {								\
  15.166 +	    int __comp;							\
  15.167 +	    name##_SPLAY(head, elm);					\
  15.168 +	    __comp = (cmp)(elm, (head)->sph_root);			\
  15.169 +	    if(__comp < 0) {						\
  15.170 +		    SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
  15.171 +		    SPLAY_RIGHT(elm, field) = (head)->sph_root;		\
  15.172 +		    SPLAY_LEFT((head)->sph_root, field) = NULL;		\
  15.173 +	    } else if (__comp > 0) {					\
  15.174 +		    SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
  15.175 +		    SPLAY_LEFT(elm, field) = (head)->sph_root;		\
  15.176 +		    SPLAY_RIGHT((head)->sph_root, field) = NULL;	\
  15.177 +	    } else							\
  15.178 +		    return ((head)->sph_root);				\
  15.179 +    }									\
  15.180 +    (head)->sph_root = (elm);						\
  15.181 +    return (NULL);							\
  15.182 +}									\
  15.183 +									\
  15.184 +struct type *								\
  15.185 +name##_SPLAY_REMOVE(struct name *head, struct type *elm)		\
  15.186 +{									\
  15.187 +	struct type *__tmp;						\
  15.188 +	if (SPLAY_EMPTY(head))						\
  15.189 +		return (NULL);						\
  15.190 +	name##_SPLAY(head, elm);					\
  15.191 +	if ((cmp)(elm, (head)->sph_root) == 0) {			\
  15.192 +		if (SPLAY_LEFT((head)->sph_root, field) == NULL) {	\
  15.193 +			(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
  15.194 +		} else {						\
  15.195 +			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
  15.196 +			(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
  15.197 +			name##_SPLAY(head, elm);			\
  15.198 +			SPLAY_RIGHT((head)->sph_root, field) = __tmp;	\
  15.199 +		}							\
  15.200 +		return (elm);						\
  15.201 +	}								\
  15.202 +	return (NULL);							\
  15.203 +}									\
  15.204 +									\
  15.205 +void									\
  15.206 +name##_SPLAY(struct name *head, struct type *elm)			\
  15.207 +{									\
  15.208 +	struct type __node, *__left, *__right, *__tmp;			\
  15.209 +	int __comp;							\
  15.210 +\
  15.211 +	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
  15.212 +	__left = __right = &__node;					\
  15.213 +\
  15.214 +	while ((__comp = (cmp)(elm, (head)->sph_root))) {		\
  15.215 +		if (__comp < 0) {					\
  15.216 +			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
  15.217 +			if (__tmp == NULL)				\
  15.218 +				break;					\
  15.219 +			if ((cmp)(elm, __tmp) < 0){			\
  15.220 +				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
  15.221 +				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
  15.222 +					break;				\
  15.223 +			}						\
  15.224 +			SPLAY_LINKLEFT(head, __right, field);		\
  15.225 +		} else if (__comp > 0) {				\
  15.226 +			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
  15.227 +			if (__tmp == NULL)				\
  15.228 +				break;					\
  15.229 +			if ((cmp)(elm, __tmp) > 0){			\
  15.230 +				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
  15.231 +				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
  15.232 +					break;				\
  15.233 +			}						\
  15.234 +			SPLAY_LINKRIGHT(head, __left, field);		\
  15.235 +		}							\
  15.236 +	}								\
  15.237 +	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
  15.238 +}									\
  15.239 +									\
  15.240 +/* Splay with either the minimum or the maximum element			\
  15.241 + * Used to find minimum or maximum element in tree.			\
  15.242 + */									\
  15.243 +void name##_SPLAY_MINMAX(struct name *head, int __comp) \
  15.244 +{									\
  15.245 +	struct type __node, *__left, *__right, *__tmp;			\
  15.246 +\
  15.247 +	SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
  15.248 +	__left = __right = &__node;					\
  15.249 +\
  15.250 +	while (1) {							\
  15.251 +		if (__comp < 0) {					\
  15.252 +			__tmp = SPLAY_LEFT((head)->sph_root, field);	\
  15.253 +			if (__tmp == NULL)				\
  15.254 +				break;					\
  15.255 +			if (__comp < 0){				\
  15.256 +				SPLAY_ROTATE_RIGHT(head, __tmp, field);	\
  15.257 +				if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
  15.258 +					break;				\
  15.259 +			}						\
  15.260 +			SPLAY_LINKLEFT(head, __right, field);		\
  15.261 +		} else if (__comp > 0) {				\
  15.262 +			__tmp = SPLAY_RIGHT((head)->sph_root, field);	\
  15.263 +			if (__tmp == NULL)				\
  15.264 +				break;					\
  15.265 +			if (__comp > 0) {				\
  15.266 +				SPLAY_ROTATE_LEFT(head, __tmp, field);	\
  15.267 +				if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
  15.268 +					break;				\
  15.269 +			}						\
  15.270 +			SPLAY_LINKRIGHT(head, __left, field);		\
  15.271 +		}							\
  15.272 +	}								\
  15.273 +	SPLAY_ASSEMBLE(head, &__node, __left, __right, field);		\
  15.274 +}
  15.275 +
  15.276 +#define SPLAY_NEGINF	-1
  15.277 +#define SPLAY_INF	1
  15.278 +
  15.279 +#define SPLAY_INSERT(name, x, y)	name##_SPLAY_INSERT(x, y)
  15.280 +#define SPLAY_REMOVE(name, x, y)	name##_SPLAY_REMOVE(x, y)
  15.281 +#define SPLAY_FIND(name, x, y)		name##_SPLAY_FIND(x, y)
  15.282 +#define SPLAY_NEXT(name, x, y)		name##_SPLAY_NEXT(x, y)
  15.283 +#define SPLAY_MIN(name, x)		(SPLAY_EMPTY(x) ? NULL	\
  15.284 +					: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
  15.285 +#define SPLAY_MAX(name, x)		(SPLAY_EMPTY(x) ? NULL	\
  15.286 +					: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
  15.287 +
  15.288 +#define SPLAY_FOREACH(x, name, head)					\
  15.289 +	for ((x) = SPLAY_MIN(name, head);				\
  15.290 +	     (x) != NULL;						\
  15.291 +	     (x) = SPLAY_NEXT(name, head, x))
  15.292 +
  15.293 +/* Macros that define a red-black tree */
  15.294 +#define RB_HEAD(name, type)						\
  15.295 +struct name {								\
  15.296 +	struct type *rbh_root; /* root of the tree */			\
  15.297 +}
  15.298 +
  15.299 +#define RB_INITIALIZER(root)						\
  15.300 +	{ NULL }
  15.301 +
  15.302 +#define RB_INIT(root) do {						\
  15.303 +	(root)->rbh_root = NULL;					\
  15.304 +} while (0)
  15.305 +
  15.306 +#define RB_BLACK	0
  15.307 +#define RB_RED		1
  15.308 +#define RB_ENTRY(type)							\
  15.309 +struct {								\
  15.310 +	struct type *rbe_left;		/* left element */		\
  15.311 +	struct type *rbe_right;		/* right element */		\
  15.312 +	struct type *rbe_parent;	/* parent element */		\
  15.313 +	int rbe_color;			/* node color */		\
  15.314 +}
  15.315 +
  15.316 +#define RB_LEFT(elm, field)		(elm)->field.rbe_left
  15.317 +#define RB_RIGHT(elm, field)		(elm)->field.rbe_right
  15.318 +#define RB_PARENT(elm, field)		(elm)->field.rbe_parent
  15.319 +#define RB_COLOR(elm, field)		(elm)->field.rbe_color
  15.320 +#define RB_ROOT(head)			(head)->rbh_root
  15.321 +#define RB_EMPTY(head)			(RB_ROOT(head) == NULL)
  15.322 +
  15.323 +#define RB_SET(elm, parent, field) do {					\
  15.324 +	RB_PARENT(elm, field) = parent;					\
  15.325 +	RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL;		\
  15.326 +	RB_COLOR(elm, field) = RB_RED;					\
  15.327 +} while (0)
  15.328 +
  15.329 +#define RB_SET_BLACKRED(black, red, field) do {				\
  15.330 +	RB_COLOR(black, field) = RB_BLACK;				\
  15.331 +	RB_COLOR(red, field) = RB_RED;					\
  15.332 +} while (0)
  15.333 +
  15.334 +#ifndef RB_AUGMENT
  15.335 +#define RB_AUGMENT(x)	do {} while (0)
  15.336 +#endif
  15.337 +
  15.338 +#define RB_ROTATE_LEFT(head, elm, tmp, field) do {			\
  15.339 +	(tmp) = RB_RIGHT(elm, field);					\
  15.340 +	if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) {		\
  15.341 +		RB_PARENT(RB_LEFT(tmp, field), field) = (elm);		\
  15.342 +	}								\
  15.343 +	RB_AUGMENT(elm);						\
  15.344 +	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
  15.345 +		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
  15.346 +			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
  15.347 +		else							\
  15.348 +			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
  15.349 +	} else								\
  15.350 +		(head)->rbh_root = (tmp);				\
  15.351 +	RB_LEFT(tmp, field) = (elm);					\
  15.352 +	RB_PARENT(elm, field) = (tmp);					\
  15.353 +	RB_AUGMENT(tmp);						\
  15.354 +	if ((RB_PARENT(tmp, field)))					\
  15.355 +		RB_AUGMENT(RB_PARENT(tmp, field));			\
  15.356 +} while (0)
  15.357 +
  15.358 +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do {			\
  15.359 +	(tmp) = RB_LEFT(elm, field);					\
  15.360 +	if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) {		\
  15.361 +		RB_PARENT(RB_RIGHT(tmp, field), field) = (elm);		\
  15.362 +	}								\
  15.363 +	RB_AUGMENT(elm);						\
  15.364 +	if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) {		\
  15.365 +		if ((elm) == RB_LEFT(RB_PARENT(elm, field), field))	\
  15.366 +			RB_LEFT(RB_PARENT(elm, field), field) = (tmp);	\
  15.367 +		else							\
  15.368 +			RB_RIGHT(RB_PARENT(elm, field), field) = (tmp);	\
  15.369 +	} else								\
  15.370 +		(head)->rbh_root = (tmp);				\
  15.371 +	RB_RIGHT(tmp, field) = (elm);					\
  15.372 +	RB_PARENT(elm, field) = (tmp);					\
  15.373 +	RB_AUGMENT(tmp);						\
  15.374 +	if ((RB_PARENT(tmp, field)))					\
  15.375 +		RB_AUGMENT(RB_PARENT(tmp, field));			\
  15.376 +} while (0)
  15.377 +
  15.378 +/* Generates prototypes and inline functions */
  15.379 +#define	RB_PROTOTYPE(name, type, field, cmp)				\
  15.380 +	RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
  15.381 +#define	RB_PROTOTYPE_STATIC(name, type, field, cmp)			\
  15.382 +	RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
  15.383 +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr)		\
  15.384 +attr void name##_RB_INSERT_COLOR(struct name *, struct type *);		\
  15.385 +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
  15.386 +attr struct type *name##_RB_REMOVE(struct name *, struct type *);	\
  15.387 +attr struct type *name##_RB_INSERT(struct name *, struct type *);	\
  15.388 +attr struct type *name##_RB_FIND(struct name *, struct type *);		\
  15.389 +attr struct type *name##_RB_NFIND(struct name *, struct type *);	\
  15.390 +attr struct type *name##_RB_NEXT(struct type *);			\
  15.391 +attr struct type *name##_RB_PREV(struct type *);			\
  15.392 +attr struct type *name##_RB_MINMAX(struct name *, int);			\
  15.393 +									\
  15.394 +
  15.395 +/* Main rb operation.
  15.396 + * Moves node close to the key of elm to top
  15.397 + */
  15.398 +#define	RB_GENERATE(name, type, field, cmp)				\
  15.399 +	RB_GENERATE_INTERNAL(name, type, field, cmp,)
  15.400 +#define	RB_GENERATE_STATIC(name, type, field, cmp)			\
  15.401 +	RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
  15.402 +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr)		\
  15.403 +attr void								\
  15.404 +name##_RB_INSERT_COLOR(struct name *head, struct type *elm)		\
  15.405 +{									\
  15.406 +	struct type *parent, *gparent, *tmp;				\
  15.407 +	while ((parent = RB_PARENT(elm, field)) &&			\
  15.408 +	    RB_COLOR(parent, field) == RB_RED) {			\
  15.409 +		gparent = RB_PARENT(parent, field);			\
  15.410 +		if (parent == RB_LEFT(gparent, field)) {		\
  15.411 +			tmp = RB_RIGHT(gparent, field);			\
  15.412 +			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
  15.413 +				RB_COLOR(tmp, field) = RB_BLACK;	\
  15.414 +				RB_SET_BLACKRED(parent, gparent, field);\
  15.415 +				elm = gparent;				\
  15.416 +				continue;				\
  15.417 +			}						\
  15.418 +			if (RB_RIGHT(parent, field) == elm) {		\
  15.419 +				RB_ROTATE_LEFT(head, parent, tmp, field);\
  15.420 +				tmp = parent;				\
  15.421 +				parent = elm;				\
  15.422 +				elm = tmp;				\
  15.423 +			}						\
  15.424 +			RB_SET_BLACKRED(parent, gparent, field);	\
  15.425 +			RB_ROTATE_RIGHT(head, gparent, tmp, field);	\
  15.426 +		} else {						\
  15.427 +			tmp = RB_LEFT(gparent, field);			\
  15.428 +			if (tmp && RB_COLOR(tmp, field) == RB_RED) {	\
  15.429 +				RB_COLOR(tmp, field) = RB_BLACK;	\
  15.430 +				RB_SET_BLACKRED(parent, gparent, field);\
  15.431 +				elm = gparent;				\
  15.432 +				continue;				\
  15.433 +			}						\
  15.434 +			if (RB_LEFT(parent, field) == elm) {		\
  15.435 +				RB_ROTATE_RIGHT(head, parent, tmp, field);\
  15.436 +				tmp = parent;				\
  15.437 +				parent = elm;				\
  15.438 +				elm = tmp;				\
  15.439 +			}						\
  15.440 +			RB_SET_BLACKRED(parent, gparent, field);	\
  15.441 +			RB_ROTATE_LEFT(head, gparent, tmp, field);	\
  15.442 +		}							\
  15.443 +	}								\
  15.444 +	RB_COLOR(head->rbh_root, field) = RB_BLACK;			\
  15.445 +}									\
  15.446 +									\
  15.447 +attr void								\
  15.448 +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
  15.449 +{									\
  15.450 +	struct type *tmp;						\
  15.451 +	while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) &&	\
  15.452 +	    elm != RB_ROOT(head)) {					\
  15.453 +		if (RB_LEFT(parent, field) == elm) {			\
  15.454 +			tmp = RB_RIGHT(parent, field);			\
  15.455 +			if (RB_COLOR(tmp, field) == RB_RED) {		\
  15.456 +				RB_SET_BLACKRED(tmp, parent, field);	\
  15.457 +				RB_ROTATE_LEFT(head, parent, tmp, field);\
  15.458 +				tmp = RB_RIGHT(parent, field);		\
  15.459 +			}						\
  15.460 +			if ((RB_LEFT(tmp, field) == NULL ||		\
  15.461 +			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
  15.462 +			    (RB_RIGHT(tmp, field) == NULL ||		\
  15.463 +			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
  15.464 +				RB_COLOR(tmp, field) = RB_RED;		\
  15.465 +				elm = parent;				\
  15.466 +				parent = RB_PARENT(elm, field);		\
  15.467 +			} else {					\
  15.468 +				if (RB_RIGHT(tmp, field) == NULL ||	\
  15.469 +				    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
  15.470 +					struct type *oleft;		\
  15.471 +					if ((oleft = RB_LEFT(tmp, field)))\
  15.472 +						RB_COLOR(oleft, field) = RB_BLACK;\
  15.473 +					RB_COLOR(tmp, field) = RB_RED;	\
  15.474 +					RB_ROTATE_RIGHT(head, tmp, oleft, field);\
  15.475 +					tmp = RB_RIGHT(parent, field);	\
  15.476 +				}					\
  15.477 +				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
  15.478 +				RB_COLOR(parent, field) = RB_BLACK;	\
  15.479 +				if (RB_RIGHT(tmp, field))		\
  15.480 +					RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
  15.481 +				RB_ROTATE_LEFT(head, parent, tmp, field);\
  15.482 +				elm = RB_ROOT(head);			\
  15.483 +				break;					\
  15.484 +			}						\
  15.485 +		} else {						\
  15.486 +			tmp = RB_LEFT(parent, field);			\
  15.487 +			if (RB_COLOR(tmp, field) == RB_RED) {		\
  15.488 +				RB_SET_BLACKRED(tmp, parent, field);	\
  15.489 +				RB_ROTATE_RIGHT(head, parent, tmp, field);\
  15.490 +				tmp = RB_LEFT(parent, field);		\
  15.491 +			}						\
  15.492 +			if ((RB_LEFT(tmp, field) == NULL ||		\
  15.493 +			    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
  15.494 +			    (RB_RIGHT(tmp, field) == NULL ||		\
  15.495 +			    RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
  15.496 +				RB_COLOR(tmp, field) = RB_RED;		\
  15.497 +				elm = parent;				\
  15.498 +				parent = RB_PARENT(elm, field);		\
  15.499 +			} else {					\
  15.500 +				if (RB_LEFT(tmp, field) == NULL ||	\
  15.501 +				    RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
  15.502 +					struct type *oright;		\
  15.503 +					if ((oright = RB_RIGHT(tmp, field)))\
  15.504 +						RB_COLOR(oright, field) = RB_BLACK;\
  15.505 +					RB_COLOR(tmp, field) = RB_RED;	\
  15.506 +					RB_ROTATE_LEFT(head, tmp, oright, field);\
  15.507 +					tmp = RB_LEFT(parent, field);	\
  15.508 +				}					\
  15.509 +				RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
  15.510 +				RB_COLOR(parent, field) = RB_BLACK;	\
  15.511 +				if (RB_LEFT(tmp, field))		\
  15.512 +					RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
  15.513 +				RB_ROTATE_RIGHT(head, parent, tmp, field);\
  15.514 +				elm = RB_ROOT(head);			\
  15.515 +				break;					\
  15.516 +			}						\
  15.517 +		}							\
  15.518 +	}								\
  15.519 +	if (elm)							\
  15.520 +		RB_COLOR(elm, field) = RB_BLACK;			\
  15.521 +}									\
  15.522 +									\
  15.523 +attr struct type *							\
  15.524 +name##_RB_REMOVE(struct name *head, struct type *elm)			\
  15.525 +{									\
  15.526 +	struct type *child, *parent, *old = elm;			\
  15.527 +	int color;							\
  15.528 +	if (RB_LEFT(elm, field) == NULL)				\
  15.529 +		child = RB_RIGHT(elm, field);				\
  15.530 +	else if (RB_RIGHT(elm, field) == NULL)				\
  15.531 +		child = RB_LEFT(elm, field);				\
  15.532 +	else {								\
  15.533 +		struct type *left;					\
  15.534 +		elm = RB_RIGHT(elm, field);				\
  15.535 +		while ((left = RB_LEFT(elm, field)))			\
  15.536 +			elm = left;					\
  15.537 +		child = RB_RIGHT(elm, field);				\
  15.538 +		parent = RB_PARENT(elm, field);				\
  15.539 +		color = RB_COLOR(elm, field);				\
  15.540 +		if (child)						\
  15.541 +			RB_PARENT(child, field) = parent;		\
  15.542 +		if (parent) {						\
  15.543 +			if (RB_LEFT(parent, field) == elm)		\
  15.544 +				RB_LEFT(parent, field) = child;		\
  15.545 +			else						\
  15.546 +				RB_RIGHT(parent, field) = child;	\
  15.547 +			RB_AUGMENT(parent);				\
  15.548 +		} else							\
  15.549 +			RB_ROOT(head) = child;				\
  15.550 +		if (RB_PARENT(elm, field) == old)			\
  15.551 +			parent = elm;					\
  15.552 +		(elm)->field = (old)->field;				\
  15.553 +		if (RB_PARENT(old, field)) {				\
  15.554 +			if (RB_LEFT(RB_PARENT(old, field), field) == old)\
  15.555 +				RB_LEFT(RB_PARENT(old, field), field) = elm;\
  15.556 +			else						\
  15.557 +				RB_RIGHT(RB_PARENT(old, field), field) = elm;\
  15.558 +			RB_AUGMENT(RB_PARENT(old, field));		\
  15.559 +		} else							\
  15.560 +			RB_ROOT(head) = elm;				\
  15.561 +		RB_PARENT(RB_LEFT(old, field), field) = elm;		\
  15.562 +		if (RB_RIGHT(old, field))				\
  15.563 +			RB_PARENT(RB_RIGHT(old, field), field) = elm;	\
  15.564 +		if (parent) {						\
  15.565 +			left = parent;					\
  15.566 +			do {						\
  15.567 +				RB_AUGMENT(left);			\
  15.568 +			} while ((left = RB_PARENT(left, field)));	\
  15.569 +		}							\
  15.570 +		goto color;						\
  15.571 +	}								\
  15.572 +	parent = RB_PARENT(elm, field);					\
  15.573 +	color = RB_COLOR(elm, field);					\
  15.574 +	if (child)							\
  15.575 +		RB_PARENT(child, field) = parent;			\
  15.576 +	if (parent) {							\
  15.577 +		if (RB_LEFT(parent, field) == elm)			\
  15.578 +			RB_LEFT(parent, field) = child;			\
  15.579 +		else							\
  15.580 +			RB_RIGHT(parent, field) = child;		\
  15.581 +		RB_AUGMENT(parent);					\
  15.582 +	} else								\
  15.583 +		RB_ROOT(head) = child;					\
  15.584 +color:									\
  15.585 +	if (color == RB_BLACK)						\
  15.586 +		name##_RB_REMOVE_COLOR(head, parent, child);		\
  15.587 +	return (old);							\
  15.588 +}									\
  15.589 +									\
  15.590 +/* Inserts a node into the RB tree */					\
  15.591 +attr struct type *							\
  15.592 +name##_RB_INSERT(struct name *head, struct type *elm)			\
  15.593 +{									\
  15.594 +	struct type *tmp;						\
  15.595 +	struct type *parent = NULL;					\
  15.596 +	int comp = 0;							\
  15.597 +	tmp = RB_ROOT(head);						\
  15.598 +	while (tmp) {							\
  15.599 +		parent = tmp;						\
  15.600 +		comp = (cmp)(elm, parent);				\
  15.601 +		if (comp < 0)						\
  15.602 +			tmp = RB_LEFT(tmp, field);			\
  15.603 +		else if (comp > 0)					\
  15.604 +			tmp = RB_RIGHT(tmp, field);			\
  15.605 +		else							\
  15.606 +			return (tmp);					\
  15.607 +	}								\
  15.608 +	RB_SET(elm, parent, field);					\
  15.609 +	if (parent != NULL) {						\
  15.610 +		if (comp < 0)						\
  15.611 +			RB_LEFT(parent, field) = elm;			\
  15.612 +		else							\
  15.613 +			RB_RIGHT(parent, field) = elm;			\
  15.614 +		RB_AUGMENT(parent);					\
  15.615 +	} else								\
  15.616 +		RB_ROOT(head) = elm;					\
  15.617 +	name##_RB_INSERT_COLOR(head, elm);				\
  15.618 +	return (NULL);							\
  15.619 +}									\
  15.620 +									\
  15.621 +/* Finds the node with the same key as elm */				\
  15.622 +attr struct type *							\
  15.623 +name##_RB_FIND(struct name *head, struct type *elm)			\
  15.624 +{									\
  15.625 +	struct type *tmp = RB_ROOT(head);				\
  15.626 +	int comp;							\
  15.627 +	while (tmp) {							\
  15.628 +		comp = cmp(elm, tmp);					\
  15.629 +		if (comp < 0)						\
  15.630 +			tmp = RB_LEFT(tmp, field);			\
  15.631 +		else if (comp > 0)					\
  15.632 +			tmp = RB_RIGHT(tmp, field);			\
  15.633 +		else							\
  15.634 +			return (tmp);					\
  15.635 +	}								\
  15.636 +	return (NULL);							\
  15.637 +}									\
  15.638 +									\
  15.639 +/* Finds the first node greater than or equal to the search key */	\
  15.640 +attr struct type *							\
  15.641 +name##_RB_NFIND(struct name *head, struct type *elm)			\
  15.642 +{									\
  15.643 +	struct type *tmp = RB_ROOT(head);				\
  15.644 +	struct type *res = NULL;					\
  15.645 +	int comp;							\
  15.646 +	while (tmp) {							\
  15.647 +		comp = cmp(elm, tmp);					\
  15.648 +		if (comp < 0) {						\
  15.649 +			res = tmp;					\
  15.650 +			tmp = RB_LEFT(tmp, field);			\
  15.651 +		}							\
  15.652 +		else if (comp > 0)					\
  15.653 +			tmp = RB_RIGHT(tmp, field);			\
  15.654 +		else							\
  15.655 +			return (tmp);					\
  15.656 +	}								\
  15.657 +	return (res);							\
  15.658 +}									\
  15.659 +									\
  15.660 +/* ARGSUSED */								\
  15.661 +attr struct type *							\
  15.662 +name##_RB_NEXT(struct type *elm)					\
  15.663 +{									\
  15.664 +	if (RB_RIGHT(elm, field)) {					\
  15.665 +		elm = RB_RIGHT(elm, field);				\
  15.666 +		while (RB_LEFT(elm, field))				\
  15.667 +			elm = RB_LEFT(elm, field);			\
  15.668 +	} else {							\
  15.669 +		if (RB_PARENT(elm, field) &&				\
  15.670 +		    (elm == RB_LEFT(RB_PARENT(elm, field), field)))	\
  15.671 +			elm = RB_PARENT(elm, field);			\
  15.672 +		else {							\
  15.673 +			while (RB_PARENT(elm, field) &&			\
  15.674 +			    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
  15.675 +				elm = RB_PARENT(elm, field);		\
  15.676 +			elm = RB_PARENT(elm, field);			\
  15.677 +		}							\
  15.678 +	}								\
  15.679 +	return (elm);							\
  15.680 +}									\
  15.681 +									\
  15.682 +/* ARGSUSED */								\
  15.683 +attr struct type *							\
  15.684 +name##_RB_PREV(struct type *elm)					\
  15.685 +{									\
  15.686 +	if (RB_LEFT(elm, field)) {					\
  15.687 +		elm = RB_LEFT(elm, field);				\
  15.688 +		while (RB_RIGHT(elm, field))				\
  15.689 +			elm = RB_RIGHT(elm, field);			\
  15.690 +	} else {							\
  15.691 +		if (RB_PARENT(elm, field) &&				\
  15.692 +		    (elm == RB_RIGHT(RB_PARENT(elm, field), field)))	\
  15.693 +			elm = RB_PARENT(elm, field);			\
  15.694 +		else {							\
  15.695 +			while (RB_PARENT(elm, field) &&			\
  15.696 +			    (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
  15.697 +				elm = RB_PARENT(elm, field);		\
  15.698 +			elm = RB_PARENT(elm, field);			\
  15.699 +		}							\
  15.700 +	}								\
  15.701 +	return (elm);							\
  15.702 +}									\
  15.703 +									\
  15.704 +attr struct type *							\
  15.705 +name##_RB_MINMAX(struct name *head, int val)				\
  15.706 +{									\
  15.707 +	struct type *tmp = RB_ROOT(head);				\
  15.708 +	struct type *parent = NULL;					\
  15.709 +	while (tmp) {							\
  15.710 +		parent = tmp;						\
  15.711 +		if (val < 0)						\
  15.712 +			tmp = RB_LEFT(tmp, field);			\
  15.713 +		else							\
  15.714 +			tmp = RB_RIGHT(tmp, field);			\
  15.715 +	}								\
  15.716 +	return (parent);						\
  15.717 +}
  15.718 +
  15.719 +#define RB_NEGINF	-1
  15.720 +#define RB_INF	1
  15.721 +
  15.722 +#define RB_INSERT(name, x, y)	name##_RB_INSERT(x, y)
  15.723 +#define RB_REMOVE(name, x, y)	name##_RB_REMOVE(x, y)
  15.724 +#define RB_FIND(name, x, y)	name##_RB_FIND(x, y)
  15.725 +#define RB_NFIND(name, x, y)	name##_RB_NFIND(x, y)
  15.726 +#define RB_NEXT(name, x, y)	name##_RB_NEXT(y)
  15.727 +#define RB_PREV(name, x, y)	name##_RB_PREV(y)
  15.728 +#define RB_MIN(name, x)		name##_RB_MINMAX(x, RB_NEGINF)
  15.729 +#define RB_MAX(name, x)		name##_RB_MINMAX(x, RB_INF)
  15.730 +
  15.731 +#define RB_FOREACH(x, name, head)					\
  15.732 +	for ((x) = RB_MIN(name, head);					\
  15.733 +	     (x) != NULL;						\
  15.734 +	     (x) = name##_RB_NEXT(x))
  15.735 +
  15.736 +#define RB_FOREACH_SAFE(x, name, head, y)				\
  15.737 +	for ((x) = RB_MIN(name, head);					\
  15.738 +	    ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1);		\
  15.739 +	     (x) = (y))
  15.740 +
  15.741 +#define RB_FOREACH_REVERSE(x, name, head)				\
  15.742 +	for ((x) = RB_MAX(name, head);					\
  15.743 +	     (x) != NULL;						\
  15.744 +	     (x) = name##_RB_PREV(x))
  15.745 +
  15.746 +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y)			\
  15.747 +	for ((x) = RB_MAX(name, head);					\
  15.748 +	    ((x) != NULL) && ((y) = name##_RB_PREV(x), 1);		\
  15.749 +	     (x) = (y))
  15.750 +
  15.751 +#endif	/* _SYS_TREE_H_ */
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/deps.sed	Thu Jan 19 22:39:51 2017 +0100
    16.3 @@ -0,0 +1,26 @@
    16.4 +/^[^:]\{1,\}:.*\\$/{
    16.5 +    h
    16.6 +    s/\([^:]\{1,\}:\).*/\1/
    16.7 +    x
    16.8 +    s/[^:]\{1,\}://
    16.9 +}
   16.10 +/\\$/,/^$/bgen
   16.11 +/\\$/,/[^\\]$/{
   16.12 +:gen
   16.13 +    s/[[:blank:]]*\\$//
   16.14 +    s/^[[:blank:]]*//
   16.15 +    G
   16.16 +    s/\(.*\)\n\(.*\)/\2 \1/
   16.17 +}
   16.18 +/^[^:]\{1,\}:[[:blank:]]*$/d
   16.19 +/^[^:]\{1,\}\.o:/{
   16.20 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC][[:blank:]]*/ /g
   16.21 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.[cC]$//g
   16.22 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc[[:blank:]]*/ /g
   16.23 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cc$//g
   16.24 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp[[:blank:]]*/ /g
   16.25 +    s/[[:blank:]]*[^[:blank:]]\{1,\}\.cpp$//g
   16.26 +    /^[^:]\{1,\}:[[:blank:]]*$/d
   16.27 +    s/^\([^:]\{1,\}\)\.o[[:blank:]]*:[[:blank:]]*\(.*\)/\1.d: $(wildcard \2)\
   16.28 +&/
   16.29 +}
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/pwfile.c	Thu Jan 19 22:39:51 2017 +0100
    17.3 @@ -0,0 +1,783 @@
    17.4 +/*
    17.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    17.6 + *
    17.7 + * Permission is hereby granted, free of charge, to any person obtaining
    17.8 + * a copy of this software and associated documentation files (the
    17.9 + * "Software"), to deal in the Software without restriction, including
   17.10 + * without limitation the rights to use, copy, modify, merge, publish,
   17.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   17.12 + * permit persons to whom the Software is furnished to do so, subject to
   17.13 + * the following conditions:
   17.14 + *
   17.15 + * The above copyright notice and this permission notice shall be included
   17.16 + * in all copies or substantial portions of the Software.
   17.17 + *
   17.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   17.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   17.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   17.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   17.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   17.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   17.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   17.25 + */
   17.26 +
   17.27 +#include "compat.h"
   17.28 +
   17.29 +#ifdef	HAVE_ERR_H
   17.30 +#include <err.h>
   17.31 +#endif /* HAVE_ERR_H */
   17.32 +#include <errno.h>
   17.33 +#include <stdio.h>
   17.34 +#include <string.h>
   17.35 +#include <sys/stat.h>
   17.36 +#ifdef	HAVE_SYS_TREE_H
   17.37 +#include <sys/tree.h>
   17.38 +#endif
   17.39 +#include <unistd.h>
   17.40 +
   17.41 +#include "pwfile.h"
   17.42 +#include "util.h"
   17.43 +
   17.44 +#define	MIN_ARRAY_SIZE	1024
   17.45 +
   17.46 +struct record_id_entry {
   17.47 +	RB_ENTRY(record_id_entry) record_id_entry;
   17.48 +	unsigned int	id;
   17.49 +	unsigned char	uuid[PWS3_UUID_SIZE];
   17.50 +};
   17.51 +
   17.52 +RB_HEAD(record_id_tree, record_id_entry);
   17.53 +
   17.54 +static int	record_id_entry_cmp(struct record_id_entry *,
   17.55 +    struct record_id_entry *);
   17.56 +RB_PROTOTYPE_STATIC(record_id_tree, record_id_entry, record_id_entry,
   17.57 +    record_id_entry_cmp)
   17.58 +RB_GENERATE_STATIC(record_id_tree, record_id_entry, record_id_entry,
   17.59 +    record_id_entry_cmp)
   17.60 +
   17.61 +static int
   17.62 +record_id_entry_cmp(struct record_id_entry *entry1,
   17.63 +    struct record_id_entry *entry2)
   17.64 +{
   17.65 +	if (entry1->id > entry2->id) {
   17.66 +		return (-1);
   17.67 +	} else if (entry1->id < entry2->id) {
   17.68 +		return (1);
   17.69 +	}
   17.70 +	return (0);
   17.71 +}
   17.72 +
   17.73 +static int
   17.74 +pws_record_cmp(const void *p1, const void *p2)
   17.75 +{
   17.76 +	int		retval;
   17.77 +	struct pws3_record *record1 = *(struct pws3_record **)p1;
   17.78 +	struct pws3_record *record2 = *(struct pws3_record **)p2;
   17.79 +	struct pws3_field *group_field1;
   17.80 +	const char	*group1;
   17.81 +	struct pws3_field *group_field2;
   17.82 +	const char	*group2;
   17.83 +	struct pws3_field *title_field1;
   17.84 +	const char	*title1;
   17.85 +	struct pws3_field *title_field2;
   17.86 +	const char	*title2;
   17.87 +
   17.88 +	group_field1 = pws3_record_get_field(record1, PWS3_RECORD_FIELD_GROUP);
   17.89 +	group1 = (group_field1 != NULL) ?  pws3_field_get_text(group_field1) :
   17.90 +	    "";
   17.91 +	group_field2 = pws3_record_get_field(record2, PWS3_RECORD_FIELD_GROUP);
   17.92 +	group2 = (group_field1 != NULL) ?  pws3_field_get_text(group_field2) :
   17.93 +	    "";
   17.94 +	retval = strcmp(group1, group2);
   17.95 +	if (retval != 0) {
   17.96 +		return (retval);
   17.97 +	}
   17.98 +
   17.99 +	title_field1 = pws3_record_get_field(record1, PWS3_RECORD_FIELD_TITLE);
  17.100 +	title1 = (title_field1 != NULL) ?  pws3_field_get_text(title_field1) :
  17.101 +	    "";
  17.102 +	title_field2 = pws3_record_get_field(record2, PWS3_RECORD_FIELD_TITLE);
  17.103 +	title2 = (title_field2 != NULL) ?  pws3_field_get_text(title_field2) :
  17.104 +	    "";
  17.105 +	return (strcmp(title1, title2));
  17.106 +}
  17.107 +
  17.108 +static void
  17.109 +record_id_tree_clear(struct record_id_tree *tree)
  17.110 +{
  17.111 +	struct record_id_entry *entry;
  17.112 +	struct record_id_entry *entry_tmp;
  17.113 +
  17.114 +	RB_FOREACH_SAFE(entry, record_id_tree, tree, entry_tmp) {
  17.115 +		RB_REMOVE(record_id_tree, tree, entry);
  17.116 +		free(entry);
  17.117 +	}
  17.118 +}
  17.119 +
  17.120 +static void
  17.121 +record_id_tree_destroy(struct record_id_tree *tree)
  17.122 +{
  17.123 +	if (tree == NULL) {
  17.124 +		return;
  17.125 +	}
  17.126 +
  17.127 +	record_id_tree_clear(tree);
  17.128 +	free(tree);
  17.129 +}
  17.130 +
  17.131 +static const unsigned char *
  17.132 +record_id_tree_get_uuid(struct record_id_tree *tree, unsigned int id)
  17.133 +{
  17.134 +	struct record_id_entry *entry;
  17.135 +
  17.136 +	entry = RB_FIND(record_id_tree, tree,
  17.137 +	    &(struct record_id_entry){ .id = id });
  17.138 +	if (entry == NULL) {
  17.139 +		return (NULL);
  17.140 +	}
  17.141 +
  17.142 +	return (entry->uuid);
  17.143 +}
  17.144 +
  17.145 +void
  17.146 +pwfile_init(struct pwm_ctx *ctx)
  17.147 +{
  17.148 +	ctx->file = pws3_file_create();
  17.149 +	if (ctx->file == NULL) {
  17.150 +		err(1, "pws3_file_create");
  17.151 +	}
  17.152 +
  17.153 +	ctx->next_id = 1;
  17.154 +
  17.155 +	ctx->record_id_tree = xmalloc(sizeof (struct record_id_tree));
  17.156 +	RB_INIT(ctx->record_id_tree);
  17.157 +}
  17.158 +
  17.159 +void
  17.160 +pwfile_destroy(struct pwm_ctx *ctx)
  17.161 +{
  17.162 +	record_id_tree_destroy(ctx->record_id_tree);
  17.163 +	ctx->record_id_tree = NULL;
  17.164 +	pws3_file_destroy(ctx->file);
  17.165 +	ctx->file = NULL;
  17.166 +}
  17.167 +
  17.168 +int
  17.169 +pwfile_read_file(struct pwm_ctx *ctx, FILE *fp)
  17.170 +{
  17.171 +	struct pws3_record *pws3_record;
  17.172 +	struct pws3_record **pws3_record_list;
  17.173 +	size_t		record_list_size = MIN_ARRAY_SIZE;
  17.174 +	size_t		record_list_len = 0;
  17.175 +	size_t		i;
  17.176 +	struct pws3_field *uuid_field;
  17.177 +	const unsigned char *uuid;
  17.178 +	struct record_id_entry *entry;
  17.179 +
  17.180 +	if (pws3_file_read_stream(ctx->file, ctx->password, fp) != 0) {
  17.181 +		warnx("failed to read password database: %s",
  17.182 +		    pws3_file_get_error_message(ctx->file));
  17.183 +		return (-1);
  17.184 +	}
  17.185 +
  17.186 +	record_id_tree_clear(ctx->record_id_tree);
  17.187 +
  17.188 +	/* sort records by group and title */
  17.189 +	pws3_record_list = xmalloc(sizeof (struct pws3_record *) *
  17.190 +	    record_list_size);
  17.191 +	for (pws3_record = pws3_file_first_record(ctx->file);
  17.192 +	    pws3_record != NULL; pws3_record = pws3_file_next_record(ctx->file,
  17.193 +	    pws3_record)) {
  17.194 +		if (record_list_len == record_list_size) {
  17.195 +			record_list_size *= 2;
  17.196 +			pws3_record_list = xrealloc(pws3_record_list,
  17.197 +			    sizeof (struct pws3_record *) * record_list_size);
  17.198 +		}
  17.199 +		pws3_record_list[record_list_len++] = pws3_record;
  17.200 +	}
  17.201 +	qsort(pws3_record_list, record_list_len, sizeof (struct pws3_record *),
  17.202 +	    pws_record_cmp);
  17.203 +
  17.204 +	/* build the tree of record IDs */
  17.205 +	for (i = 0; i < record_list_len; i++) {
  17.206 +		uuid_field = pws3_record_get_field(pws3_record_list[i],
  17.207 +		    PWS3_RECORD_FIELD_UUID);
  17.208 +		uuid = pws3_field_get_uuid(uuid_field);
  17.209 +
  17.210 +		entry = xmalloc(sizeof (struct record_id_entry));
  17.211 +		entry->id = ctx->next_id++;
  17.212 +		memcpy(entry->uuid, uuid, sizeof (entry->uuid));
  17.213 +
  17.214 +		RB_INSERT(record_id_tree, ctx->record_id_tree, entry);
  17.215 +	}
  17.216 +
  17.217 +	free(pws3_record_list);
  17.218 +
  17.219 +	return (0);
  17.220 +}
  17.221 +
  17.222 +static int
  17.223 +make_backup_copy(const char *filename)
  17.224 +{
  17.225 +	int		retval = -1;
  17.226 +	FILE		*fp_orig = NULL;
  17.227 +	char		*backup_filename = NULL;
  17.228 +	char		*tmpfilename = NULL;
  17.229 +	mode_t		old_mode;
  17.230 +	int		fd_backup = -1;
  17.231 +	unsigned char	buf[BUFSIZ];
  17.232 +	size_t		read_len;
  17.233 +	FILE		*fp_backup = NULL;
  17.234 +
  17.235 +	fp_orig = fopen(filename, "r");
  17.236 +	if (fp_orig == NULL) {
  17.237 +		if (errno != ENOENT) {
  17.238 +			warn("fopen");
  17.239 +			return (-1);
  17.240 +		}
  17.241 +		return (0);
  17.242 +	}
  17.243 +
  17.244 +	xasprintf(&backup_filename, "%s~", filename);
  17.245 +	xasprintf(&tmpfilename, "%s.XXXXXX", filename);
  17.246 +
  17.247 +	/* create temporary file */
  17.248 +	old_mode = umask(S_IRWXG | S_IRWXO);
  17.249 +	fd_backup = mkstemp(tmpfilename);
  17.250 +	umask(old_mode);
  17.251 +	if (fd_backup == -1) {
  17.252 +		warn("mkstemp");
  17.253 +		goto out;
  17.254 +	}
  17.255 +	fp_backup = fdopen(fd_backup, "w");
  17.256 +	if (fp_backup == NULL) {
  17.257 +		warn("fdopen");
  17.258 +		goto out;
  17.259 +	}
  17.260 +
  17.261 +	/* copy file contents */
  17.262 +	while (!feof(fp_orig)) {
  17.263 +		read_len = fread(buf, 1, sizeof (buf), fp_orig);
  17.264 +		if ((read_len < sizeof (buf)) && ferror(fp_orig)) {
  17.265 +			warn("fread");
  17.266 +			goto out;
  17.267 +		}
  17.268 +		if (fwrite(buf, 1, read_len, fp_backup) != read_len) {
  17.269 +			warn("fwrite");
  17.270 +			goto out;
  17.271 +		}
  17.272 +	}
  17.273 +	if (fflush(fp_backup) != 0) {
  17.274 +		warn("fflush");
  17.275 +		goto out;
  17.276 +	}
  17.277 +	if (fsync(fileno(fp_backup)) != 0) {
  17.278 +		warn("fsync");
  17.279 +		goto out;
  17.280 +	}
  17.281 +
  17.282 +	retval = 0;
  17.283 +
  17.284 +out:
  17.285 +	if ((fd_backup != -1) && (fp_backup == NULL)) {
  17.286 +		close(fd_backup);
  17.287 +	}
  17.288 +	if (fp_backup != NULL) {
  17.289 +		fclose(fp_backup);
  17.290 +	}
  17.291 +	if (fp_orig != NULL) {
  17.292 +		fclose(fp_orig);
  17.293 +	}
  17.294 +	if (retval == 0) {
  17.295 +		/* rename temporary file and overwrite existing file */
  17.296 +		if (rename(tmpfilename, backup_filename) != 0) {
  17.297 +			warn("rename");
  17.298 +			retval = -1;
  17.299 +		}
  17.300 +	}
  17.301 +	if ((retval != 0) && ((fd_backup != -1) || (fp_backup != NULL))) {
  17.302 +		unlink(tmpfilename);
  17.303 +	}
  17.304 +	free(tmpfilename);
  17.305 +	free(backup_filename);
  17.306 +
  17.307 +	return (retval);
  17.308 +}
  17.309 +
  17.310 +int
  17.311 +pwfile_write_file(struct pwm_ctx *ctx)
  17.312 +{
  17.313 +	int	retval = -1;
  17.314 +	char	*tmpfilename = NULL;
  17.315 +	mode_t	old_mode;
  17.316 +	int	fd = -1;
  17.317 +	FILE	*fp = NULL;
  17.318 +
  17.319 +	if (make_backup_copy(ctx->filename) != 0) {
  17.320 +		goto out;
  17.321 +	}
  17.322 +
  17.323 +	xasprintf(&tmpfilename, "%s.XXXXXX", ctx->filename);
  17.324 +
  17.325 +	/* create temporary file */
  17.326 +	old_mode = umask(S_IRWXG | S_IRWXO);
  17.327 +	fd = mkstemp(tmpfilename);
  17.328 +	if (fd == -1) {
  17.329 +		warn("mkstemp");
  17.330 +		goto out;
  17.331 +	}
  17.332 +	umask(old_mode);
  17.333 +	fp = fdopen(fd, "w");
  17.334 +	if (fp == NULL) {
  17.335 +		warn("fdopen");
  17.336 +		goto out;
  17.337 +	}
  17.338 +
  17.339 +	/* write contents */
  17.340 +	if (pws3_file_write_stream(ctx->file, ctx->password, 10000, fp) != 0) {
  17.341 +		warnx("pws3_file_write_stream: %s",
  17.342 +		    pws3_file_get_error_message(ctx->file));
  17.343 +		goto out;
  17.344 +	}
  17.345 +	if (fflush(fp) != 0) {
  17.346 +		warn("fflush");
  17.347 +		goto out;
  17.348 +	}
  17.349 +	if (fsync(fileno(fp)) != 0) {
  17.350 +		warn("fsync");
  17.351 +		goto out;
  17.352 +	}
  17.353 +
  17.354 +	retval = 0;
  17.355 +
  17.356 +out:
  17.357 +	if ((fd != -1) && (fp == NULL)) {
  17.358 +		close(fd);
  17.359 +	}
  17.360 +	if (fp != NULL) {
  17.361 +		fclose(fp);
  17.362 +	}
  17.363 +	if (retval == 0) {
  17.364 +		/* rename temporary file and overwrite existing file */
  17.365 +		if (rename(tmpfilename, ctx->filename) != 0) {
  17.366 +			warn("rename");
  17.367 +			retval = -1;
  17.368 +		}
  17.369 +	}
  17.370 +	if ((retval != 0) && ((fd != -1) || (fp != NULL))) {
  17.371 +		unlink(tmpfilename);
  17.372 +	}
  17.373 +	free(tmpfilename);
  17.374 +
  17.375 +	return (retval);
  17.376 +}
  17.377 +
  17.378 +static int
  17.379 +list_item_cmp(const void *p1, const void *p2)
  17.380 +{
  17.381 +	int		retval;
  17.382 +	const union list_item *item1 = *(const union list_item **)p1;
  17.383 +	const union list_item *item2 = *(const union list_item **)p2;
  17.384 +	const char	*group1;
  17.385 +	const char	*group2;
  17.386 +	const char	*title1;
  17.387 +	const char	*title2;
  17.388 +
  17.389 +	/* sort both groups and records first by group name */
  17.390 +	group1 = (item1->any.group != NULL) ? item1->any.group : "";
  17.391 +	group2 = (item2->any.group != NULL) ? item2->any.group : "";
  17.392 +	retval = strcmp(group1, group2);
  17.393 +	if ((retval != 0) || ((item1->any.type == ITEM_TYPE_GROUP) &&
  17.394 +	    (item2->any.type == ITEM_TYPE_GROUP))) {
  17.395 +		return (retval);
  17.396 +	} else if ((item1->any.type == ITEM_TYPE_GROUP) &&
  17.397 +	    (item2->any.type == ITEM_TYPE_RECORD)) {
  17.398 +		/* groups come before records belonging to it */
  17.399 +		return (-1);
  17.400 +	} else if ((item1->any.type == ITEM_TYPE_RECORD) &&
  17.401 +	    (item2->any.type == ITEM_TYPE_GROUP)) {
  17.402 +		return (1);
  17.403 +	}
  17.404 +
  17.405 +	/* sort records also by title */
  17.406 +	title1 = (item1->record.title != NULL) ?  item1->record.title : "";
  17.407 +	title2 = (item2->record.title != NULL) ?  item2->record.title : "";
  17.408 +	return (strcmp(title1, title2));
  17.409 +}
  17.410 +
  17.411 +union list_item **
  17.412 +pwfile_create_list(struct pwm_ctx *ctx)
  17.413 +{
  17.414 +	union list_item	**list;
  17.415 +	size_t		list_size = MIN_ARRAY_SIZE;
  17.416 +	size_t		list_len = 0;
  17.417 +	struct record_id_entry *entry;
  17.418 +	union list_item	*item;
  17.419 +	struct pws3_record *pws3_record;
  17.420 +	struct pws3_field *group_field;
  17.421 +	const char	*group;
  17.422 +	struct pws3_field *title_field;
  17.423 +	const char	*title;
  17.424 +	size_t		i;
  17.425 +	size_t		records_len;
  17.426 +	const char	*prev_group = "";
  17.427 +	struct pws3_field *empty_group_field;
  17.428 +
  17.429 +	list = xmalloc(sizeof (union list_item *) * list_size);
  17.430 +	list[0] = NULL;
  17.431 +
  17.432 +	/* build list of records and sort it by group and title */
  17.433 +	RB_FOREACH(entry, record_id_tree, ctx->record_id_tree) {
  17.434 +		if (list_len == list_size - 1) {
  17.435 +			list_size *= 2;
  17.436 +			list = xrealloc(list, sizeof (union list_item *) *
  17.437 +			    list_size);
  17.438 +		}
  17.439 +
  17.440 +		pws3_record = pws3_file_get_record(ctx->file, entry->uuid);
  17.441 +		group_field = pws3_record_get_field(pws3_record,
  17.442 +		    PWS3_RECORD_FIELD_GROUP);
  17.443 +		group = (group_field != NULL) ?
  17.444 +		    pws3_field_get_text(group_field) : NULL;
  17.445 +		title_field = pws3_record_get_field(pws3_record,
  17.446 +		    PWS3_RECORD_FIELD_TITLE);
  17.447 +		title = (title_field != NULL) ?
  17.448 +		    pws3_field_get_text(title_field) : NULL;
  17.449 +
  17.450 +		item = xmalloc(sizeof (union list_item));
  17.451 +		item->record.type = ITEM_TYPE_RECORD;
  17.452 +		item->record.group = (group != NULL) ? xstrdup(group) : NULL;
  17.453 +		item->record.title = (title != NULL) ? xstrdup(title) : NULL;
  17.454 +		item->record.id = entry->id;
  17.455 +		memcpy(item->record.uuid, entry->uuid,
  17.456 +		    sizeof (item->record.uuid));
  17.457 +
  17.458 +		list[list_len++] = item;
  17.459 +		list[list_len] = NULL;
  17.460 +	}
  17.461 +	qsort(list, list_len, sizeof (union list_item *), list_item_cmp);
  17.462 +
  17.463 +	/* build list of groups by comparing the groups of the sorted records */
  17.464 +	for (i = 0, records_len = list_len; i < records_len; i++) {
  17.465 +		if (list_len == list_size - 1) {
  17.466 +			list_size *= 1.5;
  17.467 +			list = xrealloc(list, sizeof (union list_item *) *
  17.468 +			    list_size);
  17.469 +		}
  17.470 +
  17.471 +		group = (list[i]->record.group != NULL) ?
  17.472 +		    list[i]->record.group : "";
  17.473 +		if (strcmp(prev_group, group) != 0) {
  17.474 +			item = xmalloc(sizeof (union list_item));
  17.475 +			item->record.type = ITEM_TYPE_GROUP;
  17.476 +			item->record.group = (group != NULL) ? xstrdup(group) :
  17.477 +			    NULL;
  17.478 +
  17.479 +			list[list_len++] = item;
  17.480 +			list[list_len] = NULL;
  17.481 +
  17.482 +			prev_group = group;
  17.483 +		}
  17.484 +	}
  17.485 +
  17.486 +	/* add empty groups to the list */
  17.487 +	for (empty_group_field = pws3_file_first_empty_group(ctx->file);
  17.488 +	    empty_group_field != NULL;
  17.489 +	    empty_group_field = pws3_file_next_empty_group(ctx->file,
  17.490 +	    empty_group_field)) {
  17.491 +		if (list_len == list_size - 1) {
  17.492 +			list_size *= 1.5;
  17.493 +			list = xrealloc(list, sizeof (union list_item *) *
  17.494 +			    list_size);
  17.495 +		}
  17.496 +
  17.497 +		group = pws3_field_get_text(empty_group_field);
  17.498 +
  17.499 +		item = xmalloc(sizeof (union list_item));
  17.500 +		item->record.type = ITEM_TYPE_GROUP;
  17.501 +		item->record.group = xstrdup(group);
  17.502 +
  17.503 +		list[list_len++] = item;
  17.504 +		list[list_len] = NULL;
  17.505 +	}
  17.506 +
  17.507 +	list_size = list_len + 2;
  17.508 +	list = xrealloc(list, sizeof (union list_item *) * list_size);
  17.509 +	/* sort the final list by group and title */
  17.510 +	qsort(list, list_len, sizeof (union list_item *), list_item_cmp);
  17.511 +
  17.512 +	return (list);
  17.513 +}
  17.514 +
  17.515 +void
  17.516 +pwfile_destroy_list(union list_item **list)
  17.517 +{
  17.518 +	size_t	i;
  17.519 +
  17.520 +	if (list == NULL) {
  17.521 +		return;
  17.522 +	}
  17.523 +
  17.524 +	for (i = 0; list[i] != NULL; i++) {
  17.525 +		if (list[i]->any.type == ITEM_TYPE_RECORD) {
  17.526 +			free(list[i]->record.title);
  17.527 +		}
  17.528 +		free(list[i]->any.group);
  17.529 +		free(list[i]);
  17.530 +	}
  17.531 +
  17.532 +	free(list);
  17.533 +}
  17.534 +
  17.535 +static void
  17.536 +update_record(struct pws3_record *pws3_record, struct record *record)
  17.537 +{
  17.538 +	struct pws3_field *title_field;
  17.539 +	struct pws3_field *group_field;
  17.540 +	struct pws3_field *username_field;
  17.541 +	struct pws3_field *password_field;
  17.542 +	struct pws3_field *notes_field;
  17.543 +	struct pws3_field *url_field;
  17.544 +
  17.545 +	if (record->title != NULL) {
  17.546 +		title_field = pws3_field_create(0, PWS3_RECORD_FIELD_TITLE);
  17.547 +		if (title_field == NULL) {
  17.548 +			err(1, "pws3_record_field_create");
  17.549 +		}
  17.550 +		if (pws3_field_set_text(title_field,
  17.551 +		    record->title) != 0) {
  17.552 +			err(1, "pws3_field_set_text");
  17.553 +		}
  17.554 +		pws3_record_set_field(pws3_record, title_field);
  17.555 +	}
  17.556 +	if (record->group != NULL) {
  17.557 +		group_field = pws3_field_create(0, PWS3_RECORD_FIELD_GROUP);
  17.558 +		if (group_field == NULL) {
  17.559 +			err(1, "pws3_record_field_create");
  17.560 +		}
  17.561 +		if (pws3_field_set_text(group_field,
  17.562 +		    record->group) != 0) {
  17.563 +			err(1, "pws3_field_set_text");
  17.564 +		}
  17.565 +		pws3_record_set_field(pws3_record, group_field);
  17.566 +	}
  17.567 +	if (record->username != NULL) {
  17.568 +		username_field = pws3_field_create(0,
  17.569 +		    PWS3_RECORD_FIELD_USERNAME);
  17.570 +		if (username_field == NULL) {
  17.571 +			err(1, "pws3_record_field_create");
  17.572 +		}
  17.573 +		if (pws3_field_set_text(username_field,
  17.574 +		    record->username) != 0) {
  17.575 +			err(1, "pws3_field_set_text");
  17.576 +		}
  17.577 +		pws3_record_set_field(pws3_record, username_field);
  17.578 +	}
  17.579 +	if (record->password != NULL) {
  17.580 +		password_field = pws3_field_create(0,
  17.581 +		    PWS3_RECORD_FIELD_PASSWORD);
  17.582 +		if (password_field == NULL) {
  17.583 +			err(1, "pws3_record_field_create");
  17.584 +		}
  17.585 +		if (pws3_field_set_text(password_field,
  17.586 +		    record->password) != 0) {
  17.587 +			err(1, "pws3_field_set_text");
  17.588 +		}
  17.589 +		pws3_record_set_field(pws3_record, password_field);
  17.590 +	}
  17.591 +	if (record->notes != NULL) {
  17.592 +		notes_field = pws3_field_create(0, PWS3_RECORD_FIELD_NOTES);
  17.593 +		if (notes_field == NULL) {
  17.594 +			err(1, "pws3_record_field_create");
  17.595 +		}
  17.596 +		if (pws3_field_set_text(notes_field, record->notes) != 0) {
  17.597 +			err(1, "pws3_field_set_text");
  17.598 +		}
  17.599 +		pws3_record_set_field(pws3_record, notes_field);
  17.600 +	}
  17.601 +	if (record->url != NULL) {
  17.602 +		url_field = pws3_field_create(0, PWS3_RECORD_FIELD_URL);
  17.603 +		if (url_field == NULL) {
  17.604 +			err(1, "pws3_record_field_create");
  17.605 +		}
  17.606 +		if (pws3_field_set_text(url_field, record->url) != 0) {
  17.607 +			err(1, "pws3_field_set_text");
  17.608 +		}
  17.609 +		pws3_record_set_field(pws3_record, url_field);
  17.610 +	}
  17.611 +}
  17.612 +
  17.613 +int
  17.614 +pwfile_create_record(struct pwm_ctx *ctx, struct record *record)
  17.615 +{
  17.616 +	struct pws3_record *pws3_record;
  17.617 +	const unsigned char *uuid;
  17.618 +	struct record_id_entry *entry;
  17.619 +
  17.620 +	pws3_record = pws3_record_create();
  17.621 +	if (pws3_record == NULL) {
  17.622 +		err(1, "pws3_record_create");
  17.623 +	}
  17.624 +	update_record(pws3_record, record);
  17.625 +	pws3_file_insert_record(ctx->file, pws3_record);
  17.626 +
  17.627 +	uuid = pws3_field_get_uuid(pws3_record_get_field(pws3_record,
  17.628 +	    PWS3_RECORD_FIELD_UUID));
  17.629 +	entry = xmalloc(sizeof (struct record_id_entry));
  17.630 +	entry->id = ctx->next_id++;
  17.631 +	memcpy(entry->uuid, uuid, sizeof (entry->uuid));
  17.632 +	RB_INSERT(record_id_tree, ctx->record_id_tree, entry);
  17.633 +
  17.634 +	return (0);
  17.635 +}
  17.636 +
  17.637 +int
  17.638 +pwfile_modify_record(struct pwm_ctx *ctx, unsigned int id,
  17.639 +    struct record *record)
  17.640 +{
  17.641 +	const unsigned char *uuid;
  17.642 +
  17.643 +	uuid = record_id_tree_get_uuid(ctx->record_id_tree, id);
  17.644 +	if (uuid == NULL) {
  17.645 +		return (-1);
  17.646 +	}
  17.647 +
  17.648 +	update_record(pws3_file_get_record(ctx->file, uuid), record);
  17.649 +
  17.650 +	return (0);
  17.651 +}
  17.652 +
  17.653 +int
  17.654 +pwfile_remove_record(struct pwm_ctx *ctx, unsigned int id)
  17.655 +{
  17.656 +	const unsigned char *uuid;
  17.657 +	struct record_id_entry *entry;
  17.658 +
  17.659 +	uuid = record_id_tree_get_uuid(ctx->record_id_tree, id);
  17.660 +	if (uuid == NULL) {
  17.661 +		return (-1);
  17.662 +	}
  17.663 +
  17.664 +	pws3_record_destroy(pws3_file_remove_record(ctx->file, uuid));
  17.665 +
  17.666 +	entry = RB_FIND(record_id_tree, ctx->record_id_tree,
  17.667 +	    &(struct record_id_entry){ .id = id });
  17.668 +	free(RB_REMOVE(record_id_tree, ctx->record_id_tree, entry));
  17.669 +
  17.670 +	return (0);
  17.671 +}
  17.672 +
  17.673 +int
  17.674 +pwfile_create_group(struct pwm_ctx *ctx, const char *group)
  17.675 +{
  17.676 +	struct pws3_record *pws3_record;
  17.677 +	struct pws3_field *group_field;
  17.678 +	struct pws3_field *empty_group_field;
  17.679 +
  17.680 +	/* check for a record in the given group */
  17.681 +	for (pws3_record = pws3_file_first_record(ctx->file);
  17.682 +	    pws3_record != NULL; pws3_record = pws3_file_next_record(ctx->file,
  17.683 +	    pws3_record)) {
  17.684 +		group_field = pws3_record_get_field(pws3_record,
  17.685 +		    PWS3_RECORD_FIELD_GROUP);
  17.686 +		if ((group_field != NULL) &&
  17.687 +		    (strcmp(group, pws3_field_get_text(group_field)) == 0)) {
  17.688 +			return (-1);
  17.689 +		}
  17.690 +	}
  17.691 +
  17.692 +	empty_group_field = pws3_field_create(1,
  17.693 +	    PWS3_HEADER_FIELD_EMPTY_GROUPS);
  17.694 +	if (empty_group_field == NULL) {
  17.695 +		err(1, "pws3_field_create");
  17.696 +	}
  17.697 +	if (pws3_field_set_text(empty_group_field, group) != 0) {
  17.698 +		err(1, "pws3_field_set_text");
  17.699 +	}
  17.700 +	pws3_file_insert_empty_group(ctx->file, empty_group_field);
  17.701 +
  17.702 +	return (0);
  17.703 +}
  17.704 +
  17.705 +int
  17.706 +pwfile_remove_group(struct pwm_ctx *ctx, const char *group)
  17.707 +{
  17.708 +	struct pws3_field *empty_group_field;
  17.709 +
  17.710 +	empty_group_field = pws3_file_remove_empty_group(ctx->file, group);
  17.711 +	if (empty_group_field != NULL) {
  17.712 +		return (-1);
  17.713 +	}
  17.714 +	pws3_field_destroy(empty_group_field);
  17.715 +
  17.716 +	return (0);
  17.717 +}
  17.718 +
  17.719 +struct record *
  17.720 +pwfile_get_record(struct pwm_ctx *ctx, unsigned int id)
  17.721 +{
  17.722 +	struct record	*record;
  17.723 +	const unsigned char *uuid;
  17.724 +	struct pws3_record *pws3_record;
  17.725 +	struct pws3_field *title_field;
  17.726 +	struct pws3_field *group_field;
  17.727 +	struct pws3_field *username_field;
  17.728 +	struct pws3_field *password_field;
  17.729 +	struct pws3_field *notes_field;
  17.730 +	struct pws3_field *url_field;
  17.731 +
  17.732 +	uuid = record_id_tree_get_uuid(ctx->record_id_tree, id);
  17.733 +	if (uuid == NULL) {
  17.734 +		return (NULL);
  17.735 +	}
  17.736 +	pws3_record = pws3_file_get_record(ctx->file, uuid);
  17.737 +
  17.738 +	record = xmalloc(sizeof (struct record));
  17.739 +
  17.740 +	title_field = pws3_record_get_field(pws3_record,
  17.741 +	    PWS3_RECORD_FIELD_TITLE);
  17.742 +	record->title = (title_field != NULL) ?
  17.743 +	    xstrdup(pws3_field_get_text(title_field)) : NULL;
  17.744 +
  17.745 +	group_field = pws3_record_get_field(pws3_record,
  17.746 +	    PWS3_RECORD_FIELD_GROUP);
  17.747 +	record->group = (group_field != NULL) ?
  17.748 +	    xstrdup(pws3_field_get_text(group_field)) : NULL;
  17.749 +
  17.750 +	username_field = pws3_record_get_field(pws3_record,
  17.751 +	    PWS3_RECORD_FIELD_USERNAME);
  17.752 +	record->username = (username_field != NULL) ?
  17.753 +	    xstrdup(pws3_field_get_text(username_field)) : NULL;
  17.754 +
  17.755 +	password_field = pws3_record_get_field(pws3_record,
  17.756 +	    PWS3_RECORD_FIELD_PASSWORD);
  17.757 +	record->password = (password_field != NULL) ?
  17.758 +	    xstrdup(pws3_field_get_text(password_field)) : NULL;
  17.759 +
  17.760 +	notes_field = pws3_record_get_field(pws3_record,
  17.761 +	    PWS3_RECORD_FIELD_NOTES);
  17.762 +	record->notes = (notes_field != NULL) ?
  17.763 +	    xstrdup(pws3_field_get_text(notes_field)) : NULL;
  17.764 +
  17.765 +	url_field = pws3_record_get_field(pws3_record, PWS3_RECORD_FIELD_URL);
  17.766 +	record->url = (url_field != NULL) ?
  17.767 +	    xstrdup(pws3_field_get_text(url_field)) : NULL;
  17.768 +
  17.769 +	return (record);
  17.770 +}
  17.771 +
  17.772 +void
  17.773 +pwfile_destroy_record(struct record *record)
  17.774 +{
  17.775 +	if (record == NULL) {
  17.776 +		return;
  17.777 +	}
  17.778 +
  17.779 +	free(record->title);
  17.780 +	free(record->group);
  17.781 +	free(record->username);
  17.782 +	free(record->password);
  17.783 +	free(record->notes);
  17.784 +	free(record->url);
  17.785 +	free(record);
  17.786 +}
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/pwfile.h	Thu Jan 19 22:39:51 2017 +0100
    18.3 @@ -0,0 +1,78 @@
    18.4 +/*
    18.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    18.6 + *
    18.7 + * Permission is hereby granted, free of charge, to any person obtaining
    18.8 + * a copy of this software and associated documentation files (the
    18.9 + * "Software"), to deal in the Software without restriction, including
   18.10 + * without limitation the rights to use, copy, modify, merge, publish,
   18.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   18.12 + * permit persons to whom the Software is furnished to do so, subject to
   18.13 + * the following conditions:
   18.14 + *
   18.15 + * The above copyright notice and this permission notice shall be included
   18.16 + * in all copies or substantial portions of the Software.
   18.17 + *
   18.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   18.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   18.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   18.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   18.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   18.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   18.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   18.25 + */
   18.26 +
   18.27 +#ifndef	PWFILE_H
   18.28 +#define	PWFILE_H
   18.29 +
   18.30 +#include "pwm.h"
   18.31 +
   18.32 +enum item_type {
   18.33 +	ITEM_TYPE_RECORD,
   18.34 +	ITEM_TYPE_GROUP
   18.35 +};
   18.36 +
   18.37 +union list_item {
   18.38 +	struct any_item {
   18.39 +		enum item_type	type;
   18.40 +		char		*group;
   18.41 +	} any;
   18.42 +	struct record_item {
   18.43 +		enum item_type	type;
   18.44 +		char		*group;
   18.45 +		char		*title;
   18.46 +		unsigned int	id;
   18.47 +		unsigned char	uuid[PWS3_UUID_SIZE];
   18.48 +	} record;
   18.49 +	struct group_item {
   18.50 +		enum item_type	type;
   18.51 +		char		*group;
   18.52 +	} group;
   18.53 +};
   18.54 +
   18.55 +struct record_id_tree;
   18.56 +
   18.57 +struct record {
   18.58 +	char	*title;
   18.59 +	char	*group;
   18.60 +	char	*username;
   18.61 +	char	*password;
   18.62 +	char	*notes;
   18.63 +	char	*url;
   18.64 +};
   18.65 +
   18.66 +void		pwfile_init(struct pwm_ctx *ctx);
   18.67 +void		pwfile_destroy(struct pwm_ctx *);
   18.68 +int		pwfile_read_file(struct pwm_ctx *, FILE *);
   18.69 +int		pwfile_write_file(struct pwm_ctx *);
   18.70 +union list_item ** pwfile_create_list(struct pwm_ctx *);
   18.71 +void		pwfile_destroy_list(union list_item **);
   18.72 +int		pwfile_create_record(struct pwm_ctx *, struct record *);
   18.73 +int		pwfile_modify_record(struct pwm_ctx *, unsigned int,
   18.74 +    struct record *);
   18.75 +int		pwfile_remove_record(struct pwm_ctx *, unsigned int);
   18.76 +int		pwfile_create_group(struct pwm_ctx *, const char *);
   18.77 +int		pwfile_remove_group(struct pwm_ctx *, const char *);
   18.78 +struct record *	pwfile_get_record(struct pwm_ctx *, unsigned int);
   18.79 +void		pwfile_destroy_record(struct record *);
   18.80 +
   18.81 +#endif /* !PWFILE_H */
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/pwm.c	Thu Jan 19 22:39:51 2017 +0100
    19.3 @@ -0,0 +1,366 @@
    19.4 +/*
    19.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    19.6 + *
    19.7 + * Permission is hereby granted, free of charge, to any person obtaining
    19.8 + * a copy of this software and associated documentation files (the
    19.9 + * "Software"), to deal in the Software without restriction, including
   19.10 + * without limitation the rights to use, copy, modify, merge, publish,
   19.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   19.12 + * permit persons to whom the Software is furnished to do so, subject to
   19.13 + * the following conditions:
   19.14 + *
   19.15 + * The above copyright notice and this permission notice shall be included
   19.16 + * in all copies or substantial portions of the Software.
   19.17 + *
   19.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   19.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   19.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   19.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   19.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   19.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   19.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   19.25 + */
   19.26 +
   19.27 +#include "compat.h"
   19.28 +
   19.29 +#ifdef	HAVE_ERR_H
   19.30 +#include <err.h>
   19.31 +#endif /* HAVE_ERR_H */
   19.32 +#include <errno.h>
   19.33 +#include <langinfo.h>
   19.34 +#include <locale.h>
   19.35 +#include <pwd.h>
   19.36 +#ifdef	HAVE_READPASSPHRASE_H
   19.37 +#include <readpassphrase.h>
   19.38 +#endif /* READPASSPHRASE_H */
   19.39 +#include <stdlib.h>
   19.40 +#include <stdio.h>
   19.41 +#include <string.h>
   19.42 +#include <pwd.h>
   19.43 +#include <sys/stat.h>
   19.44 +#include <time.h>
   19.45 +#include <unistd.h>
   19.46 +
   19.47 +#include "pwm.h"
   19.48 +#include "cmd.h"
   19.49 +#include "pwfile.h"
   19.50 +#include "tok.h"
   19.51 +#include "util.h"
   19.52 +
   19.53 +#ifndef	PWM_LINE_MAX
   19.54 +#define	PWM_LINE_MAX	16384
   19.55 +#endif /* !PWM_LINE_MAX */
   19.56 +
   19.57 +static void
   19.58 +usage(void)
   19.59 +{
   19.60 +	fprintf(stderr, "usage: %s [-P file] [filename]\n", getprogname());
   19.61 +}
   19.62 +
   19.63 +static int
   19.64 +run_input_loop(struct pwm_ctx *ctx, int is_interactive)
   19.65 +{
   19.66 +	int		retval = -1;
   19.67 +	char		buf[PWM_LINE_MAX];
   19.68 +	int		c;
   19.69 +	int		argc = 0;
   19.70 +	char		**argv = NULL;
   19.71 +	struct cmd	*cmd;
   19.72 +	int		i;
   19.73 +
   19.74 +	for (;;) {
   19.75 +		if (fgets(buf, (int)sizeof (buf), stdin) == NULL) {
   19.76 +			if (ferror(stdin)) {
   19.77 +				/* error */
   19.78 +				warn("failed to read command");
   19.79 +				goto out;
   19.80 +			} else if (feof(stdin)) {
   19.81 +				/* EOF */
   19.82 +				break;
   19.83 +			}
   19.84 +		}
   19.85 +		if ((buf[strlen(buf) - 1] != '\n') && !feof(stdin)) {
   19.86 +			/* line was truncated */
   19.87 +			fprintf(stderr, "line too long\n");
   19.88 +			if (is_interactive) {
   19.89 +				/* skip input to next newline */
   19.90 +				do {
   19.91 +					errno = 0;
   19.92 +					c = fgetc(stdin);
   19.93 +					if ((c == EOF) && (errno != 0)) {
   19.94 +						warn("failed to read command");
   19.95 +						goto out;
   19.96 +					}
   19.97 +				} while ((c != '\n') && (c != EOF));
   19.98 +			} else {
   19.99 +				/* fatal error in non-interactive mode */
  19.100 +				goto out;
  19.101 +			}
  19.102 +		}
  19.103 +
  19.104 +		/* tokenize line */
  19.105 +		switch(tok_tokenize(buf, &argc, &argv)) {
  19.106 +		case TOK_ERR_SYSTEM_ERROR:
  19.107 +			err(1, "tok_tokenize");
  19.108 +		case TOK_ERR_UNTERMINATED_QUOTE:
  19.109 +			fprintf(stderr, "unterminated quote\n");
  19.110 +			if (!is_interactive) {
  19.111 +				goto out;
  19.112 +			}
  19.113 +			goto next;
  19.114 +		case TOK_ERR_TRAILING_BACKSLASH:
  19.115 +			fprintf(stderr, "trailing backslash\n");
  19.116 +			if (!is_interactive) {
  19.117 +				goto out;
  19.118 +			}
  19.119 +			goto next;
  19.120 +		}
  19.121 +
  19.122 +		if (argc == 0) {
  19.123 +			/* empty line */
  19.124 +			goto next;
  19.125 +		}
  19.126 +
  19.127 +		/* find and execute the command */
  19.128 +		cmd = cmd_match(argv[0]);
  19.129 +		if (cmd == NULL) {
  19.130 +			fprintf(stderr, "unknown command: %s\n", argv[0]);
  19.131 +			if (is_interactive) {
  19.132 +				goto next;
  19.133 +			} else {
  19.134 +				goto out;
  19.135 +			}
  19.136 +		}
  19.137 +		switch (cmd->cmd_func(ctx, argc, argv)) {
  19.138 +		case CMD_OK:
  19.139 +			break;
  19.140 +		case CMD_USAGE:
  19.141 +			fprintf(stderr, "usage: %s\n", cmd->usage);
  19.142 +		case CMD_ERR:	/* FALLTHROUGH */
  19.143 +			if (!is_interactive) {
  19.144 +				goto out;
  19.145 +			}
  19.146 +			break;
  19.147 +		case CMD_QUIT:
  19.148 +			goto quit;
  19.149 +		}
  19.150 +
  19.151 +next:
  19.152 +		for (i = 0; i < argc; i++) {
  19.153 +			free(argv[i]);
  19.154 +		}
  19.155 +		free(argv);
  19.156 +		argc = 0;
  19.157 +		argv = NULL;
  19.158 +	}
  19.159 +
  19.160 +quit:
  19.161 +	retval = 0;
  19.162 +
  19.163 +out:
  19.164 +	for (i = 0; i < argc; i++) {
  19.165 +		free(argv[i]);
  19.166 +	}
  19.167 +	free(argv);
  19.168 +
  19.169 +	return (retval);
  19.170 +}
  19.171 +
  19.172 +static int
  19.173 +read_password_from_file(const char *filename, char *password,
  19.174 +    size_t password_size)
  19.175 +{
  19.176 +	int	retval = -1;
  19.177 +	char	*buf = NULL;
  19.178 +	FILE	*fp = NULL;
  19.179 +	size_t	password_len = 0;
  19.180 +
  19.181 +	buf = xmalloc(password_size);
  19.182 +
  19.183 +	fp = fopen(filename, "r");
  19.184 +	if (fp == NULL) {
  19.185 +		warn("failed to open master password file \"%s\"", filename);
  19.186 +		goto out;
  19.187 +	}
  19.188 +	if (fgets(buf, password_size, fp) == NULL) {
  19.189 +		/* read error or empty file */
  19.190 +		if (ferror(fp)) {
  19.191 +			/* read error */
  19.192 +			warn("failed to read master password from \"%s\"",
  19.193 +			    filename);
  19.194 +		}
  19.195 +		goto out;
  19.196 +	}
  19.197 +	password_len = strlen(buf);
  19.198 +	if (buf[password_len - 1] == '\n') {
  19.199 +		/* strip trailing newline */
  19.200 +		password_len--;
  19.201 +		if (password_len == 0) {
  19.202 +			/* first line is empty */
  19.203 +			goto out;
  19.204 +		}
  19.205 +	} else if (!feof(fp)) {
  19.206 +		/* the first line was truncated, password is too long */
  19.207 +		goto out;
  19.208 +	}
  19.209 +	memcpy(password, buf, password_size);
  19.210 +	retval = 0;
  19.211 +
  19.212 +out:
  19.213 +	password[password_len] = '\0';
  19.214 +
  19.215 +	if (fp != NULL) {
  19.216 +		fclose(fp);
  19.217 +	}
  19.218 +	free(buf);
  19.219 +
  19.220 +	return (retval);
  19.221 +}
  19.222 +
  19.223 +int
  19.224 +main(int argc, char *argv[])
  19.225 +{
  19.226 +	int		status = EXIT_FAILURE;
  19.227 +	char		*locale;
  19.228 +	int		is_interactive;
  19.229 +	int		errflag = 0;
  19.230 +	int		c;
  19.231 +	const char	*master_password_filename = NULL;
  19.232 +	struct pwm_ctx	ctx = { 0 };
  19.233 +	struct passwd	*passwd;
  19.234 +	char		*pwm_dirname = NULL;
  19.235 +	FILE		*fp = NULL;
  19.236 +	char		password_buf[PWS3_MAX_PASSWORD_LEN + 1];
  19.237 +
  19.238 +	setprogname(argv[0]);
  19.239 +
  19.240 +	/* set up locale and check for UTF-8 codeset */
  19.241 +	locale = setlocale(LC_ALL, "");
  19.242 +	if (locale == NULL) {
  19.243 +		errx(1, "invalid locale");
  19.244 +	}
  19.245 +	if ((strcasecmp(nl_langinfo(CODESET), "UTF-8") != 0) &&
  19.246 +	    (strcasecmp(nl_langinfo(CODESET), "UTF8") != 0)) {
  19.247 +		fprintf(stderr, "pwm requires a locale with UTF-8 character "
  19.248 +		    "encoding.\n");
  19.249 +		goto out;
  19.250 +	}
  19.251 +
  19.252 +	/* timestamps are processed as UTC */
  19.253 +	if (setenv("TZ", "", 1) != 0) {
  19.254 +		goto out;
  19.255 +	}
  19.256 +	tzset();
  19.257 +
  19.258 +	/* initialize libpws */
  19.259 +	if (pws_init() != 0) {
  19.260 +		goto out;
  19.261 +	}
  19.262 +
  19.263 +	is_interactive = isatty(STDIN_FILENO);
  19.264 +
  19.265 +	while (!errflag && (c = getopt(argc, argv, "P:h")) != -1) {
  19.266 +		switch (c) {
  19.267 +		case 'P':
  19.268 +			master_password_filename = optarg;
  19.269 +			break;
  19.270 +		case 'h':
  19.271 +			usage();
  19.272 +			status = EXIT_SUCCESS;
  19.273 +			goto out;
  19.274 +		default:
  19.275 +			errflag = 1;
  19.276 +		}
  19.277 +	}
  19.278 +	if (errflag || (optind + 1 < argc)) {
  19.279 +		usage();
  19.280 +		status = EXIT_USAGE;
  19.281 +		goto out;
  19.282 +	}
  19.283 +
  19.284 +	if (optind == argc) {
  19.285 +		passwd = getpwuid(getuid());
  19.286 +		if (passwd == NULL) {
  19.287 +			err(1, "getpwuid");
  19.288 +		}
  19.289 +		xasprintf(&pwm_dirname, "%s/.pwm", passwd->pw_dir);
  19.290 +		xasprintf(&ctx.filename, "%s/pwm.psafe3", pwm_dirname);
  19.291 +
  19.292 +		/* create ~/.pwm directory if necessary */
  19.293 +		if ((mkdir(pwm_dirname, S_IRWXU) != 0) && (errno != EEXIST)) {
  19.294 +			warn("failed to create directory \"%s\"", pwm_dirname);
  19.295 +			goto out;
  19.296 +		}
  19.297 +	} else if (optind + 1 == argc) {
  19.298 +		ctx.filename = xstrdup(argv[optind++]);
  19.299 +	} else {
  19.300 +		usage();
  19.301 +		status = EXIT_USAGE;
  19.302 +		goto out;
  19.303 +	}
  19.304 +
  19.305 +	if (is_interactive) {
  19.306 +		printf("pwm version %s\n", VERSION);
  19.307 +	} else if (master_password_filename == NULL) {
  19.308 +		fprintf(stderr, "master password file must be specified when "
  19.309 +		    "running non-interactively\n");
  19.310 +		goto out;
  19.311 +	}
  19.312 +
  19.313 +	pwfile_init(&ctx);
  19.314 +
  19.315 +	fp = fopen(ctx.filename, "r");
  19.316 +	if ((fp == NULL) && (errno != ENOENT)) {
  19.317 +		warn("failed to open password database");
  19.318 +		goto out;
  19.319 +	}
  19.320 +	/* obtain master password */
  19.321 +	if (master_password_filename != NULL) {
  19.322 +		if (read_password_from_file(master_password_filename,
  19.323 +		    ctx.password, sizeof (ctx.password)) != 0) {
  19.324 +			fprintf(stderr, "malformed password database\n");
  19.325 +			goto out;
  19.326 +		}
  19.327 +	} else {
  19.328 +		if (readpassphrase("Enter password: ", ctx.password,
  19.329 +		    sizeof (ctx.password), RPP_ECHO_OFF | RPP_REQUIRE_TTY) ==
  19.330 +		    NULL) {
  19.331 +			err(1, "readpassphrase");
  19.332 +		}
  19.333 +		if (ctx.password[0] == '\0') {
  19.334 +			fprintf(stderr, "password must not be empty\n");
  19.335 +			goto out;
  19.336 +		}
  19.337 +		if (fp == NULL) {
  19.338 +			if (readpassphrase("Confirm password: ", password_buf,
  19.339 +			    sizeof (password_buf),
  19.340 +			    RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
  19.341 +				err(1, "readpassphrase");
  19.342 +			}
  19.343 +			if (strcmp(ctx.password, password_buf) != 0) {
  19.344 +				fprintf(stderr, "passwords do not match\n");
  19.345 +				goto out;
  19.346 +			}
  19.347 +		}
  19.348 +	}
  19.349 +	if (fp != NULL) {
  19.350 +		if (pwfile_read_file(&ctx, fp) != 0) {
  19.351 +			goto out;
  19.352 +		}
  19.353 +		fclose(fp);
  19.354 +		fp = NULL;
  19.355 +	}
  19.356 +
  19.357 +	/* run main input loop */
  19.358 +	status = (run_input_loop(&ctx, is_interactive) != 0);
  19.359 +
  19.360 +out:
  19.361 +	pwfile_destroy(&ctx);
  19.362 +	if (fp != NULL) {
  19.363 +		fclose(fp);
  19.364 +	}
  19.365 +	free(ctx.filename);
  19.366 +	free(pwm_dirname);
  19.367 +
  19.368 +	exit(status);
  19.369 +}
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/pwm.h	Thu Jan 19 22:39:51 2017 +0100
    20.3 @@ -0,0 +1,37 @@
    20.4 +/*
    20.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    20.6 + *
    20.7 + * Permission is hereby granted, free of charge, to any person obtaining
    20.8 + * a copy of this software and associated documentation files (the
    20.9 + * "Software"), to deal in the Software without restriction, including
   20.10 + * without limitation the rights to use, copy, modify, merge, publish,
   20.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   20.12 + * permit persons to whom the Software is furnished to do so, subject to
   20.13 + * the following conditions:
   20.14 + *
   20.15 + * The above copyright notice and this permission notice shall be included
   20.16 + * in all copies or substantial portions of the Software.
   20.17 + *
   20.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   20.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   20.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   20.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   20.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   20.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   20.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   20.25 + */
   20.26 +
   20.27 +#ifndef	PWM_H
   20.28 +#define	PWM_H
   20.29 +
   20.30 +#include <pws.h>
   20.31 +
   20.32 +struct pwm_ctx {
   20.33 +	char		*filename;
   20.34 +	struct pws3_file *file;
   20.35 +	struct record_id_tree *record_id_tree;
   20.36 +	unsigned int	next_id;
   20.37 +	char		password[PWS3_MAX_PASSWORD_LEN + 1];
   20.38 +};
   20.39 +
   20.40 +#endif /* PWM_H */
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/tok.c	Thu Jan 19 22:39:51 2017 +0100
    21.3 @@ -0,0 +1,246 @@
    21.4 +/*
    21.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    21.6 + *
    21.7 + * Permission is hereby granted, free of charge, to any person obtaining
    21.8 + * a copy of this software and associated documentation files (the
    21.9 + * "Software"), to deal in the Software without restriction, including
   21.10 + * without limitation the rights to use, copy, modify, merge, publish,
   21.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   21.12 + * permit persons to whom the Software is furnished to do so, subject to
   21.13 + * the following conditions:
   21.14 + *
   21.15 + * The above copyright notice and this permission notice shall be included
   21.16 + * in all copies or substantial portions of the Software.
   21.17 + *
   21.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   21.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   21.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   21.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   21.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   21.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   21.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   21.25 + */
   21.26 +
   21.27 +#include "compat.h"
   21.28 +
   21.29 +#include <errno.h>
   21.30 +#include <stdio.h>
   21.31 +#include <stdlib.h>
   21.32 +#include <string.h>
   21.33 +
   21.34 +#include "tok.h"
   21.35 +
   21.36 +enum tok_states {
   21.37 +	STATE_INITIAL,
   21.38 +	STATE_IN_WORD,
   21.39 +	STATE_IN_QUOTE,
   21.40 +	STATE_IN_WORD_ESCAPE,
   21.41 +	STATE_IN_QUOTE_ESCAPE
   21.42 +};
   21.43 +
   21.44 +static inline int
   21.45 +strbuf_appendc(char **bufp, size_t *buf_sizep, int c)
   21.46 +{
   21.47 +	char	*buf = *bufp;
   21.48 +	size_t	buf_size = *buf_sizep;
   21.49 +	size_t	len;
   21.50 +
   21.51 +	len = ((buf != NULL) && (c >= 0)) ? strlen(buf) : 0;
   21.52 +
   21.53 +	/* allocate buffer if *bufp is NULL and *buf_sizep is 0 */
   21.54 +	if (buf_size < len + (c >= 0) + 1) {
   21.55 +		buf_size = (buf_size * 2 > BUFSIZ) ? buf_size * 2 : BUFSIZ;
   21.56 +		buf = realloc(buf, buf_size);
   21.57 +		if (buf == NULL) {
   21.58 +			return (-1);
   21.59 +		}
   21.60 +	}
   21.61 +
   21.62 +	/* append character to string buffer or reset buffer if c is -1 */
   21.63 +	if (c >= 0) {
   21.64 +		buf[len++] = c;
   21.65 +	}
   21.66 +	buf[len] = '\0';
   21.67 +
   21.68 +	*bufp = buf;
   21.69 +	*buf_sizep = buf_size;
   21.70 +
   21.71 +	return (0);
   21.72 +}
   21.73 +
   21.74 +enum tok_err
   21.75 +tok_tokenize(const char *s, int *tokencp, char ***tokenvp)
   21.76 +{
   21.77 +	int		retval = TOK_ERR_SYSTEM_ERROR;
   21.78 +	int		saved_errno = 0;
   21.79 +	char		**tokenv;
   21.80 +	size_t		tokenc = 0;
   21.81 +	const char	*p = s;
   21.82 +	enum tok_states	state = STATE_INITIAL;
   21.83 +	char		quote;
   21.84 +	char		*buf = NULL;
   21.85 +	size_t		buf_size = 0;
   21.86 +	char		*token;
   21.87 +	size_t		i;
   21.88 +
   21.89 +	/*
   21.90 +	 * allocate maximum number of tokens including the terminating NULL
   21.91 +	 * pointer: ceil(length / 2) + 1
   21.92 +	 */
   21.93 +	tokenv = malloc(((strlen(s) + 2 - 1) / 2 + 1) * sizeof (char *));
   21.94 +	if (tokenv == NULL) {
   21.95 +		saved_errno = errno;
   21.96 +		goto out;
   21.97 +	}
   21.98 +	tokenv[0] = NULL;
   21.99 +
  21.100 +	for (;;) {
  21.101 +		switch (state) {
  21.102 +		case STATE_INITIAL:
  21.103 +			switch (*p) {
  21.104 +			case ' ':	/* FALLTHROUGH */
  21.105 +			case '\t':	/* FALLTHROUGH */
  21.106 +			case '\n':
  21.107 +				/* skip initial whitespace */
  21.108 +				break;
  21.109 +			case '"':	/* FALLTHROUGH */
  21.110 +			case '\'':
  21.111 +				/* start quoted part of token */
  21.112 +				state = STATE_IN_QUOTE;
  21.113 +				quote = *p;
  21.114 +				if (strbuf_appendc(&buf, &buf_size, -1) != 0) {
  21.115 +					saved_errno = errno;
  21.116 +					goto out;
  21.117 +				}
  21.118 +				break;
  21.119 +			case '\\':
  21.120 +				/* start token with a backslash escape */
  21.121 +				state = STATE_IN_WORD_ESCAPE;
  21.122 +				if (strbuf_appendc(&buf, &buf_size, -1) != 0) {
  21.123 +					saved_errno = errno;
  21.124 +					goto out;
  21.125 +				}
  21.126 +				break;
  21.127 +			case '\0':
  21.128 +				/* end of input */
  21.129 +				retval = 0;
  21.130 +				goto out;
  21.131 +			default:
  21.132 +				/* start token with a word */
  21.133 +				state = STATE_IN_WORD;
  21.134 +				if (strbuf_appendc(&buf, &buf_size, -1) != 0) {
  21.135 +					saved_errno = errno;
  21.136 +					goto out;
  21.137 +				}
  21.138 +				if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
  21.139 +					saved_errno = errno;
  21.140 +					goto out;
  21.141 +				}
  21.142 +			}
  21.143 +			break;
  21.144 +		case STATE_IN_WORD:
  21.145 +			switch (*p) {
  21.146 +			case ' ':	/* FALLTHROUGH */
  21.147 +			case '\t':	/* FALLTHROUGH */
  21.148 +			case '\n':	/* FALLTHROUGH */
  21.149 +			case '\0':
  21.150 +				/* end of token */
  21.151 +				token = strdup(buf);
  21.152 +				if (token == NULL) {
  21.153 +					saved_errno = errno;
  21.154 +					goto out;
  21.155 +				}
  21.156 +				tokenv[tokenc++] = token;
  21.157 +				tokenv[tokenc] = NULL;
  21.158 +				if (*p == '\0') {
  21.159 +					retval = 0;
  21.160 +					goto out;
  21.161 +				}
  21.162 +				state = STATE_INITIAL;
  21.163 +				break;
  21.164 +			case '"':	/* FALLTHROUGH */
  21.165 +			case '\'':
  21.166 +				/* start quoted part of token */
  21.167 +				state = STATE_IN_QUOTE;
  21.168 +				quote = *p;
  21.169 +				break;
  21.170 +			case '\\':
  21.171 +				/* start backslash escape */
  21.172 +				state = STATE_IN_WORD_ESCAPE;
  21.173 +				break;
  21.174 +			default:
  21.175 +				/* regular character */
  21.176 +				if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
  21.177 +					saved_errno = errno;
  21.178 +					goto out;
  21.179 +				}
  21.180 +			}
  21.181 +			break;
  21.182 +		case STATE_IN_QUOTE:
  21.183 +			switch (*p) {
  21.184 +			case '"':	/* FALLTHROUGH */
  21.185 +			case '\'':
  21.186 +				if (*p == quote) {
  21.187 +					/* end quoted part of token */
  21.188 +					state = STATE_IN_WORD;
  21.189 +				} else {
  21.190 +					/* quote quote character */
  21.191 +					if (strbuf_appendc(&buf, &buf_size,
  21.192 +					    *p) != 0) {
  21.193 +						saved_errno = errno;
  21.194 +						goto out;
  21.195 +					}
  21.196 +				}
  21.197 +				break;
  21.198 +			case '\\':
  21.199 +				/* start quoted backslash escape */
  21.200 +				state = STATE_IN_QUOTE_ESCAPE;
  21.201 +				break;
  21.202 +			case '\0':
  21.203 +				/* unclosed quote */
  21.204 +				retval = TOK_ERR_UNTERMINATED_QUOTE;
  21.205 +				goto out;
  21.206 +			default:
  21.207 +				/* regular character */
  21.208 +				if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
  21.209 +					saved_errno = errno;
  21.210 +					goto out;
  21.211 +				}
  21.212 +			}
  21.213 +			break;
  21.214 +		case STATE_IN_WORD_ESCAPE:	/* FALLTHROUGH */
  21.215 +		case STATE_IN_QUOTE_ESCAPE:
  21.216 +			if (*p == '\0') {
  21.217 +				/* trailing backslash */
  21.218 +				retval = TOK_ERR_TRAILING_BACKSLASH;
  21.219 +				goto out;
  21.220 +			}
  21.221 +			/* escaped character */
  21.222 +			state = (state == STATE_IN_WORD_ESCAPE) ?
  21.223 +			    STATE_IN_WORD : STATE_IN_QUOTE;
  21.224 +			if (strbuf_appendc(&buf, &buf_size, *p) != 0) {
  21.225 +				saved_errno = errno;
  21.226 +				goto out;
  21.227 +			}
  21.228 +			break;
  21.229 +		}
  21.230 +		p++;
  21.231 +	}
  21.232 +
  21.233 +out:
  21.234 +	if (retval < 0) {
  21.235 +		for (i = 0; i < tokenc; i++) {
  21.236 +			free(tokenv[i]);
  21.237 +		}
  21.238 +		free(tokenv);
  21.239 +	} else {
  21.240 +		*tokencp = tokenc;
  21.241 +		*tokenvp = realloc(tokenv, (tokenc + 1) * sizeof (char *));
  21.242 +	}
  21.243 +	free(buf);
  21.244 +	if (retval < 0) {
  21.245 +		errno = saved_errno;
  21.246 +	}
  21.247 +
  21.248 +	return (retval);
  21.249 +}
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/tok.h	Thu Jan 19 22:39:51 2017 +0100
    22.3 @@ -0,0 +1,35 @@
    22.4 +/*
    22.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    22.6 + *
    22.7 + * Permission is hereby granted, free of charge, to any person obtaining
    22.8 + * a copy of this software and associated documentation files (the
    22.9 + * "Software"), to deal in the Software without restriction, including
   22.10 + * without limitation the rights to use, copy, modify, merge, publish,
   22.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   22.12 + * permit persons to whom the Software is furnished to do so, subject to
   22.13 + * the following conditions:
   22.14 + *
   22.15 + * The above copyright notice and this permission notice shall be included
   22.16 + * in all copies or substantial portions of the Software.
   22.17 + *
   22.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   22.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   22.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   22.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   22.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   22.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   22.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   22.25 + */
   22.26 +
   22.27 +#ifndef	TOK_H
   22.28 +#define	TOK_H
   22.29 +
   22.30 +enum tok_err {
   22.31 +	TOK_ERR_SYSTEM_ERROR = -1,
   22.32 +	TOK_ERR_UNTERMINATED_QUOTE = -2,
   22.33 +	TOK_ERR_TRAILING_BACKSLASH = -3
   22.34 +};
   22.35 +
   22.36 +enum tok_err	tok_tokenize(const char *, int *, char ***);
   22.37 +
   22.38 +#endif /* !TOK_H */
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/util.c	Thu Jan 19 22:39:51 2017 +0100
    23.3 @@ -0,0 +1,83 @@
    23.4 +/*
    23.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    23.6 + *
    23.7 + * Permission is hereby granted, free of charge, to any person obtaining
    23.8 + * a copy of this software and associated documentation files (the
    23.9 + * "Software"), to deal in the Software without restriction, including
   23.10 + * without limitation the rights to use, copy, modify, merge, publish,
   23.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   23.12 + * permit persons to whom the Software is furnished to do so, subject to
   23.13 + * the following conditions:
   23.14 + *
   23.15 + * The above copyright notice and this permission notice shall be included
   23.16 + * in all copies or substantial portions of the Software.
   23.17 + *
   23.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   23.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   23.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   23.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   23.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   23.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   23.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   23.25 + */
   23.26 +
   23.27 +#include "compat.h"
   23.28 +
   23.29 +#ifdef	HAVE_ERR_H
   23.30 +#include <err.h>
   23.31 +#endif /* HAVE_ERR_H */
   23.32 +#include <stdio.h>
   23.33 +#include <string.h>
   23.34 +
   23.35 +#include "util.h"
   23.36 +
   23.37 +void *
   23.38 +xmalloc(size_t size)
   23.39 +{
   23.40 +	void	*ptr;
   23.41 +
   23.42 +	ptr = malloc(size);
   23.43 +	if (ptr == NULL) {
   23.44 +		err(1, "malloc");
   23.45 +	}
   23.46 +
   23.47 +	return (ptr);
   23.48 +}
   23.49 +
   23.50 +void *
   23.51 +xrealloc(void *ptr, size_t size)
   23.52 +{
   23.53 +	ptr = realloc(ptr, size);
   23.54 +	if (ptr == NULL) {
   23.55 +		err(1, "realloc");
   23.56 +	}
   23.57 +
   23.58 +	return (ptr);
   23.59 +}
   23.60 +
   23.61 +char *
   23.62 +xstrdup(const char *str)
   23.63 +{
   23.64 +	char	*new;
   23.65 +
   23.66 +	new = strdup(str);
   23.67 +	if (new == NULL) {
   23.68 +		err(1, "strdup");
   23.69 +	}
   23.70 +
   23.71 +	return (new);
   23.72 +}
   23.73 +
   23.74 +char *
   23.75 +xasprintf(char **strp, const char *fmt, ...)
   23.76 +{
   23.77 +	va_list	args;
   23.78 +
   23.79 +	va_start(args, fmt);
   23.80 +	if (vasprintf(strp, fmt, args) < 0) {
   23.81 +		err(1, "vasprintf");
   23.82 +	}
   23.83 +	va_end(args);
   23.84 +
   23.85 +	return (*strp);
   23.86 +}
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/util.h	Thu Jan 19 22:39:51 2017 +0100
    24.3 @@ -0,0 +1,39 @@
    24.4 +/*
    24.5 + * Copyright (C) 2016 Guido Berhoerster <guido+pwm@berhoerster.name>
    24.6 + *
    24.7 + * Permission is hereby granted, free of charge, to any person obtaining
    24.8 + * a copy of this software and associated documentation files (the
    24.9 + * "Software"), to deal in the Software without restriction, including
   24.10 + * without limitation the rights to use, copy, modify, merge, publish,
   24.11 + * distribute, sublicense, and/or sell copies of the Software, and to
   24.12 + * permit persons to whom the Software is furnished to do so, subject to
   24.13 + * the following conditions:
   24.14 + *
   24.15 + * The above copyright notice and this permission notice shall be included
   24.16 + * in all copies or substantial portions of the Software.
   24.17 + *
   24.18 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   24.19 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   24.20 + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   24.21 + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   24.22 + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   24.23 + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   24.24 + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   24.25 + */
   24.26 +
   24.27 +#ifndef	UTIL_H
   24.28 +#define	UTIL_H
   24.29 +
   24.30 +#include <stdlib.h>
   24.31 +
   24.32 +#define	EXIT_USAGE	2
   24.33 +
   24.34 +#define	COUNTOF(x)	((sizeof (x)/sizeof (0[x])) / ((size_t)(!(sizeof (x) % \
   24.35 +    sizeof (0[x])))))
   24.36 +
   24.37 +void *	xmalloc(size_t);
   24.38 +void *	xrealloc(void *, size_t);
   24.39 +char *	xstrdup(const char *);
   24.40 +char *	xasprintf(char **, const char *, ...);
   24.41 +
   24.42 +#endif /* UTIL_H */