changeset 18:1e39a251cbe9

Use libtecla for interactive input
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Thu, 24 Aug 2017 13:10:56 +0200
parents a08ef0674d8e
children 5c6155c8e9b6
files Makefile cmd.c compat.h compat/readpassphrase.c compat/readpassphrase.h pager.c pwm.c pwm.h
diffstat 8 files changed, 147 insertions(+), 473 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat Aug 12 10:41:52 2017 +0200
+++ b/Makefile	Thu Aug 24 13:10:56 2017 +0200
@@ -82,7 +82,6 @@
   HAVE_ERR_H ?=		1
   HAVE_GETRANDOM ?=	0
   HAVE_SYS_QUEUE_H ?=	0
-  HAVE_READPASSPHRASE_H ?= 0
   HAVE_SETPROGNAME ?=	0
   HAVE_SYS_TREE_H ?=	0
 else ifneq ($(findstring $(OS_NAME),FreeBSD DragonFly),)
@@ -91,7 +90,6 @@
   HAVE_ERR_H ?=		1
   HAVE_GETRANDOM ?=	0
   HAVE_SYS_QUEUE_H ?=	1
-  HAVE_READPASSPHRASE_H ?= 1
   HAVE_SETPROGNAME ?=	1
   HAVE_SYS_TREE_H ?=	1
 else ifeq ($(OS_NAME),NetBSD)
@@ -100,7 +98,6 @@
   HAVE_ERR_H ?=		1
   HAVE_GETRANDOM ?=	0
   HAVE_SYS_QUEUE_H ?=	1
-  HAVE_READPASSPHRASE_H ?= 0
   HAVE_SYS_TREE_H ?=	1
   HAVE_SETPROGNAME ?=	1
 else ifeq ($(OS_NAME),OpenBSD)
@@ -109,7 +106,6 @@
   HAVE_ERR_H ?=		1
   HAVE_GETRANDOM ?=	0
   HAVE_SYS_QUEUE_H ?=	1
-  HAVE_READPASSPHRASE_H ?= 1
   HAVE_SYS_TREE_H ?=	1
   HAVE_SETPROGNAME ?=	1
 else ifeq ($(OS_NAME),SunOS)
@@ -125,7 +121,6 @@
     HAVE_GETRANDOM ?=	1
   endif
   HAVE_SYS_QUEUE_H ?=	0
-  HAVE_READPASSPHRASE_H ?= 0
   HAVE_SYS_TREE_H ?=	0
   HAVE_SETPROGNAME ?=	0
 else
@@ -134,7 +129,6 @@
   HAVE_ERR_H ?=		0
   HAVE_GETRANDOM ?=	0
   HAVE_SYS_QUEUE_H ?=	0
-  HAVE_READPASSPHRASE_H ?= 0
   HAVE_SETPROGNAME ?=	0
   HAVE_SYS_TREE_H ?=	0
 endif
@@ -157,7 +151,7 @@
 
 XCPPFLAGS =	-DPACKAGE=\"$(PACKAGE)\" \
 		-DVERSION=\"$(VERSION)\"
-LDLIBS =	-lpws -lnettle
+LDLIBS =	-ltecla -lcurses -lpws -lnettle
 ifeq ($(HAVE_ASPRINTF),1)
   XCPPFLAGS +=	-DHAVE_ASPRINTF
 else
@@ -177,11 +171,6 @@
 else
   OBJS +=	compat/err.o
 endif
-ifeq ($(HAVE_READPASSPHRASE_H),1)
-  XCPPFLAGS +=	-DHAVE_READPASSPHRASE_H
-else
-  OBJS +=	compat/readpassphrase.o
-endif
 ifeq ($(HAVE_SETPROGNAME),1)
   XCPPFLAGS +=	-DHAVE_SETPROGNAME
 else
--- a/cmd.c	Sat Aug 12 10:41:52 2017 +0200
+++ b/cmd.c	Thu Aug 24 13:10:56 2017 +0200
@@ -29,9 +29,6 @@
 #endif /* HAVE_ERR_H */
 #include <errno.h>
 #include <limits.h>
-#ifdef	HAVE_READPASSPHRASE_H
-#include <readpassphrase.h>
-#endif /* READPASSPHRASE_H */
 #include <regex.h>
 #include <stdlib.h>
 #include <string.h>
@@ -860,43 +857,24 @@
 static enum cmd_return
 cmd_changepassword(struct pwm_ctx *ctx, int argc, char *argv[])
 {
-	size_t	password_len;
-	char	password_buf[PWS3_MAX_PASSWORD_LEN + 1];
-	char	confirm_buf[PWS3_MAX_PASSWORD_LEN + 1];
+	size_t	len;
 
 	if (argc > 2) {
 		return (CMD_USAGE);
 	} else if (argc == 2) {
-		password_len = strlen(argv[1]);
-		if (password_len == 0) {
+		len = strlen(argv[1]);
+		if (len == 0) {
 			pwm_err(ctx, "password must not be empty");
 			return (CMD_ERR);
-		} else if (password_len + 1 > sizeof (ctx->password)) {
+		} else if (len + 1 > sizeof (ctx->password)) {
 			pwm_err(ctx, "password too long");
 			return (CMD_ERR);
 		}
-		memcpy(ctx->password, argv[1], password_len + 1);
+		memcpy(ctx->password, argv[1], len + 1);
 	} else {
-		if (readpassphrase("Enter password: ", password_buf,
-		    sizeof (password_buf), RPP_ECHO_OFF | RPP_REQUIRE_TTY) ==
-		    NULL) {
-			err(1, "readpassphrase");
-		}
-		password_len = strlen(password_buf);
-		if (password_len == 0) {
-			pwm_err(ctx, "password must not be empty");
+		if (pwm_read_password(ctx, 1) != 0) {
 			return (CMD_ERR);
 		}
-		if (readpassphrase("Confirm password: ", confirm_buf,
-		    sizeof (confirm_buf),
-		    RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
-			err(1, "readpassphrase");
-		}
-		if (strcmp(password_buf, confirm_buf) != 0) {
-			pwm_err(ctx, "passwords do not match");
-			return (CMD_ERR);
-		}
-		memcpy(ctx->password, password_buf, password_len + 1);
 	}
 
 	return (CMD_OK);
--- a/compat.h	Sat Aug 12 10:41:52 2017 +0200
+++ b/compat.h	Thu Aug 24 13:10:56 2017 +0200
@@ -43,10 +43,6 @@
 #include "compat/queue.h"
 #endif /* !HAVE_SYS_QUEUE_H */
 
-#ifndef	HAVE_READPASSPHRASE_H
-#include "compat/readpassphrase.h"
-#endif /* !HAVE_READPASSPHRASE_H */
-
 #ifndef	HAVE_SETPROGNAME
 #include "compat/setprogname.h"
 #endif /* !HAVE_SETPROGNAME */
--- a/compat/readpassphrase.c	Sat Aug 12 10:41:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-/*	$OpenBSD: readpassphrase.c,v 1.24 2013/11/24 23:51:29 deraadt Exp $	*/
-
-/*
- * Copyright (c) 2000-2002, 2007, 2010
- *	Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#ifdef	HAVE_PATHS_H
-#include <paths.h>
-#endif /* HAVE_PATHS_H */
-#include <pwd.h>
-#include <signal.h>
-#include <string.h>
-#include <termios.h>
-#include <unistd.h>
-#include "readpassphrase.h"
-
-#ifndef TCSASOFT
-#define TCSASOFT 0
-#endif /* !TCSASOFT */
-
-#ifndef	_NSIG
-#ifdef NSIG
-#define	_NSIG NSIG
-#else /* NSIG */
-#define	_NSIG 128
-#endif /* NSIG */
-#endif /* !_NSIG */
-
-#ifndef	_PATH_TTY
-#define	_PATH_TTY	"/dev/tty"
-#endif
-
-static volatile sig_atomic_t signo[_NSIG];
-
-static void handler(int);
-
-char *
-readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
-{
-	ssize_t nr;
-	int input, output, save_errno, i, need_restart;
-	char ch, *p, *end;
-	struct termios term, oterm;
-	struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
-	struct sigaction savetstp, savettin, savettou, savepipe;
-
-	/* I suppose we could alloc on demand in this case (XXX). */
-	if (bufsiz == 0) {
-		errno = EINVAL;
-		return(NULL);
-	}
-
-restart:
-	for (i = 0; i < _NSIG; i++)
-		signo[i] = 0;
-	nr = -1;
-	save_errno = 0;
-	need_restart = 0;
-	/*
-	 * Read and write to /dev/tty if available.  If not, read from
-	 * stdin and write to stderr unless a tty is required.
-	 */
-	if ((flags & RPP_STDIN) ||
-	    (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
-		if (flags & RPP_REQUIRE_TTY) {
-			errno = ENOTTY;
-			return(NULL);
-		}
-		input = STDIN_FILENO;
-		output = STDERR_FILENO;
-	}
-
-	/*
-	 * Turn off echo if possible.
-	 * If we are using a tty but are not the foreground pgrp this will
-	 * generate SIGTTOU, so do it *before* installing the signal handlers.
-	 */
-	if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
-		memcpy(&term, &oterm, sizeof(term));
-		if (!(flags & RPP_ECHO_ON))
-			term.c_lflag &= ~(ECHO | ECHONL);
-#ifdef VSTATUS
-		if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
-			term.c_cc[VSTATUS] = _POSIX_VDISABLE;
-#endif /* VSTATUS */
-		(void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
-	} else {
-		memset(&term, 0, sizeof(term));
-		term.c_lflag |= ECHO;
-		memset(&oterm, 0, sizeof(oterm));
-		oterm.c_lflag |= ECHO;
-	}
-
-	/*
-	 * Catch signals that would otherwise cause the user to end
-	 * up with echo turned off in the shell.  Don't worry about
-	 * things like SIGXCPU and SIGVTALRM for now.
-	 */
-	sigemptyset(&sa.sa_mask);
-	sa.sa_flags = 0;		/* don't restart system calls */
-	sa.sa_handler = handler;
-	(void)sigaction(SIGALRM, &sa, &savealrm);
-	(void)sigaction(SIGHUP, &sa, &savehup);
-	(void)sigaction(SIGINT, &sa, &saveint);
-	(void)sigaction(SIGPIPE, &sa, &savepipe);
-	(void)sigaction(SIGQUIT, &sa, &savequit);
-	(void)sigaction(SIGTERM, &sa, &saveterm);
-	(void)sigaction(SIGTSTP, &sa, &savetstp);
-	(void)sigaction(SIGTTIN, &sa, &savettin);
-	(void)sigaction(SIGTTOU, &sa, &savettou);
-
-	if (!(flags & RPP_STDIN))
-		(void)write(output, prompt, strlen(prompt));
-	end = buf + bufsiz - 1;
-	p = buf;
-	while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
-		if (p < end) {
-			if ((flags & RPP_SEVENBIT))
-				ch &= 0x7f;
-			if (isalpha((unsigned char)ch)) {
-				if ((flags & RPP_FORCELOWER))
-					ch = (char)tolower((unsigned char)ch);
-				if ((flags & RPP_FORCEUPPER))
-					ch = (char)toupper((unsigned char)ch);
-			}
-			*p++ = ch;
-		}
-	}
-	*p = '\0';
-	save_errno = errno;
-	if (!(term.c_lflag & ECHO))
-		(void)write(output, "\n", 1);
-
-	/* Restore old terminal settings and signals. */
-	if (memcmp(&term, &oterm, sizeof(term)) != 0) {
-		while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
-		    errno == EINTR && !signo[SIGTTOU])
-			continue;
-	}
-	(void)sigaction(SIGALRM, &savealrm, NULL);
-	(void)sigaction(SIGHUP, &savehup, NULL);
-	(void)sigaction(SIGINT, &saveint, NULL);
-	(void)sigaction(SIGQUIT, &savequit, NULL);
-	(void)sigaction(SIGPIPE, &savepipe, NULL);
-	(void)sigaction(SIGTERM, &saveterm, NULL);
-	(void)sigaction(SIGTSTP, &savetstp, NULL);
-	(void)sigaction(SIGTTIN, &savettin, NULL);
-	(void)sigaction(SIGTTOU, &savettou, NULL);
-	if (input != STDIN_FILENO)
-		(void)close(input);
-
-	/*
-	 * If we were interrupted by a signal, resend it to ourselves
-	 * now that we have restored the signal handlers.
-	 */
-	for (i = 0; i < _NSIG; i++) {
-		if (signo[i]) {
-			kill(getpid(), i);
-			switch (i) {
-			case SIGTSTP:
-			case SIGTTIN:
-			case SIGTTOU:
-				need_restart = 1;
-			}
-		}
-	}
-	if (need_restart)
-		goto restart;
-
-	if (save_errno)
-		errno = save_errno;
-	return(nr == -1 ? NULL : buf);
-}
-
-#if 0
-char *
-getpass(const char *prompt)
-{
-	static char buf[_PASSWORD_LEN + 1];
-
-	return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
-}
-#endif
-
-static void handler(int s)
-{
-
-	signo[s] = 1;
-}
--- a/compat/readpassphrase.h	Sat Aug 12 10:41:52 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*	$OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $	*/
-
-/*
- * Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Sponsored in part by the Defense Advanced Research Projects
- * Agency (DARPA) and Air Force Research Laboratory, Air Force
- * Materiel Command, USAF, under agreement number F39502-99-1-0512.
- */
-
-#ifndef _READPASSPHRASE_H_
-#define _READPASSPHRASE_H_
-
-#define RPP_ECHO_OFF    0x00		/* Turn off echo (default). */
-#define RPP_ECHO_ON     0x01		/* Leave echo on. */
-#define RPP_REQUIRE_TTY 0x02		/* Fail if there is no tty. */
-#define RPP_FORCELOWER  0x04		/* Force input to lower case. */
-#define RPP_FORCEUPPER  0x08		/* Force input to upper case. */
-#define RPP_SEVENBIT    0x10		/* Strip the high bit from input. */
-#define RPP_STDIN       0x20		/* Read from stdin, not /dev/tty */
-
-#include <sys/types.h>
-
-char * readpassphrase(const char *, char *, size_t, int);
-
-#endif /* !_READPASSPHRASE_H_ */
--- a/pager.c	Sat Aug 12 10:41:52 2017 +0200
+++ b/pager.c	Thu Aug 24 13:10:56 2017 +0200
@@ -28,6 +28,7 @@
 #endif /* HAVE_ERR_H */
 #include <errno.h>
 #include <fcntl.h>
+#include <libtecla.h>
 #include <limits.h>
 #include <signal.h>
 #include <stdio.h>
@@ -46,22 +47,6 @@
 #include "pager.h"
 #include "util.h"
 
-#ifndef	TCSASOFT
-#define	TCSASOFT 0
-#endif /* !TCSASOFT */
-
-#ifndef	_NSIG
-#ifdef NSIG
-#define	_NSIG NSIG
-#else /* NSIG */
-#define	_NSIG 128
-#endif /* NSIG */
-#endif /* !_NSIG */
-
-#ifndef	_PATH_TTY
-#define	_PATH_TTY	"/dev/tty"
-#endif
-
 struct pager {
 	TAILQ_HEAD(lines_head, lines_entry) lines_head;
 	FILE		*fp;
@@ -74,123 +59,22 @@
 	char		*line;
 };
 
-static volatile sig_atomic_t signals[_NSIG];
-
-static void
-sig_handler(int signo)
-{
-	signals[signo] = 1;
-}
-
 static int
-getch_prompt(const char *prompt)
+getch_prompt(GetLine *gl, const char *prompt)
 {
-	int		retval = -1;
-	int		signo;
-	int		fd;
-	struct termios	saved_term;
-	struct termios	term;
-	struct sigaction sa;
-	struct sigaction saved_sa_hup;
-	struct sigaction saved_sa_int;
-	struct sigaction saved_sa_quit;
-	struct sigaction saved_sa_pipe;
-	struct sigaction saved_sa_alrm;
-	struct sigaction saved_sa_term;
-	struct sigaction saved_sa_tstp;
-	struct sigaction saved_sa_ttin;
-	struct sigaction saved_sa_ttou;
-	char		c;
-	int		need_restart = 0;
-
-	printf("%s", prompt);
-	fflush(stdout);
-
-restart:
-	for (signo = 0; signo < _NSIG; signo++) {
-		signals[signo] = 0;
-	}
-
-	fd = open(_PATH_TTY, O_RDONLY);
-	if (fd < 0) {
-		return (-1);
-	}
+	int	c;
+	int	saved_echo_mode;
 
-	if (tcgetattr(fd, &saved_term) != 0) {
-		close(fd);
-		return (-1);
-	}
-	memcpy(&term, &saved_term, sizeof (struct termios));
-
-	/* enter raw mode and turn echo off */
-	term.c_lflag &= ~(ICANON | ECHO);
-	while ((tcsetattr(fd, TCSAFLUSH | TCSASOFT, &term) < 0) &&
-	    (errno != EINTR)) {
-		continue;
-	}
-
-	/* install temporary signal handler */
-	sigemptyset(&sa.sa_mask);
-	sa.sa_flags = 0;
-	sa.sa_handler = sig_handler;
-	sigaction(SIGHUP, &sa, &saved_sa_hup);
-	sigaction(SIGINT, &sa, &saved_sa_int);
-	sigaction(SIGQUIT, &sa, &saved_sa_quit);
-	sigaction(SIGPIPE, &sa, &saved_sa_pipe);
-	sigaction(SIGALRM, &sa, &saved_sa_alrm);
-	sigaction(SIGTERM, &sa, &saved_sa_term);
-	sigaction(SIGTSTP, &sa, &saved_sa_tstp);
-	sigaction(SIGTTIN, &sa, &saved_sa_ttin);
-	sigaction(SIGTTOU, &sa, &saved_sa_ttou);
-
-	/* read key */
-	if (read(fd, &c, 1) == sizeof (c)) {
-		retval = c;
-	}
-
-	/* restore previous mode and echo setting */
-	do {
-		while ((tcsetattr(fd, TCSAFLUSH | TCSASOFT,
-		    &saved_term) < 0) && (errno != EINTR)) {
-			continue;
-		}
-	} while ((tcgetattr(fd, &term) == 0) &&
-	    (term.c_lflag != saved_term.c_lflag));
-
-	/* restore previous signal handlers */
-	sigaction(SIGHUP, &saved_sa_hup, &sa);
-	sigaction(SIGINT, &saved_sa_int, &sa);
-	sigaction(SIGQUIT, &saved_sa_quit, &sa);
-	sigaction(SIGPIPE, &saved_sa_pipe, &sa);
-	sigaction(SIGALRM, &saved_sa_alrm, &sa);
-	sigaction(SIGTERM, &saved_sa_term, &sa);
-	sigaction(SIGTSTP, &saved_sa_tstp, &sa);
-	sigaction(SIGTTIN, &saved_sa_ttin, &sa);
-	sigaction(SIGTTOU, &saved_sa_ttou, &sa);
+	/* prompt with echo off */
+	saved_echo_mode = gl_echo_mode(gl, -1);
+	gl_echo_mode(gl, 0);
+	c = gl_query_char(gl, prompt, '\0');
+	gl_echo_mode(gl, saved_echo_mode);
 
 	/* erase prompt */
 	printf("\r%*s\r", (int)strlen(prompt), "");
-	fflush(stdout);
 
-	while ((close(fd) < 0) && (errno != EINTR)) {
-		continue;
-	}
-
-	/* re-raise signals received */
-	for (signo = 0; signo < _NSIG; signo++) {
-		if (signals[signo]) {
-			raise(signo);
-			if ((signo == SIGTSTP) || (signo == SIGTTIN) ||
-			    (signo == SIGTTOU)) {
-				need_restart = 1;
-			}
-		}
-	}
-	if (need_restart) {
-		goto restart;
-	}
-
-	return (retval);
+	return (c);
 }
 
 struct pager *
@@ -370,6 +254,7 @@
 	struct winsize	ws;
 #endif /* TIOCGWINSZ */
 	unsigned int	row = 0;
+	GetLine		*gl;
 	struct lines_entry *entry;
 
 	is_interactive = (isatty(STDIN_FILENO) && (pager->fp == stdout));
@@ -384,12 +269,18 @@
 	}
 #endif /* TIOCGWINSZ */
 
+	gl = new_GetLine(10, 0);
+	if (gl == NULL) {
+		err(1, "new_GetLine");
+	}
+
 	TAILQ_FOREACH(entry, &pager->lines_head, entry) {
 		if (is_interactive) {
 			mbsnprint(cols, entry->line, pager->fp);
 			row++;
-			if (row == rows - 1) {
-				getch_prompt("--More--");
+			if ((TAILQ_NEXT(entry, entry) != NULL) &&
+			    (row == rows - 1)) {
+				getch_prompt(gl, "--More--");
 				row = 0;
 			}
 		} else {
@@ -397,4 +288,6 @@
 			fflush(pager->fp);
 		}
 	}
+
+	del_GetLine(gl);
 }
--- a/pwm.c	Sat Aug 12 10:41:52 2017 +0200
+++ b/pwm.c	Thu Aug 24 13:10:56 2017 +0200
@@ -27,12 +27,10 @@
 #include <err.h>
 #endif /* HAVE_ERR_H */
 #include <errno.h>
+#include <fcntl.h>
 #include <langinfo.h>
 #include <locale.h>
 #include <pwd.h>
-#ifdef	HAVE_READPASSPHRASE_H
-#include <readpassphrase.h>
-#endif /* READPASSPHRASE_H */
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -47,9 +45,17 @@
 #include "tok.h"
 #include "util.h"
 
-#ifndef	PWM_LINE_MAX
-#define	PWM_LINE_MAX	16384
-#endif /* !PWM_LINE_MAX */
+#ifndef	PWM_HISTORY_ENTRIES_MAX
+#define	PWM_HISTORY_ENTRIES_MAX	1024
+#endif /* !PWM_HISTORY_MAX */
+
+#ifndef	PWM_HISTORY_LINES_MAX
+#define	PWM_HISTORY_LINES_MAX	256
+#endif /* !PWM_HISTORY_LINES_MAX */
+
+#ifndef	PWM_HISTORY_MAX
+#define	PWM_HISTORY_MAX	(PWM_HISTORY_LINES_MAX * PWM_LINE_MAX)
+#endif /* !PWM_HISTORY_MAX */
 
 static void
 usage(void)
@@ -76,49 +82,53 @@
 }
 
 static int
+complete_nothing(WordCompletion *cpl, void *data, const char *line,
+    int word_end)
+{
+	return (0);
+}
+
+static int
 run_input_loop(struct pwm_ctx *ctx, int is_interactive)
 {
 	int		retval = -1;
+	char		prompt[8 + 2 + 1];
+	GetLine		*gl = NULL;
 	char		buf[PWM_LINE_MAX];
+	char		*line = buf;
 	int		c;
 	int		argc = 0;
 	char		**argv = NULL;
 	struct cmd	*cmd;
 	int		i;
 
+	snprintf(prompt, sizeof (prompt), "%.*s> ", 8, getprogname());
+
+	/* initialize libtecla */
+	gl = new_GetLine(PWM_LINE_MAX, PWM_HISTORY_MAX);
+	if (gl == NULL) {
+		err(1, "new_GetLine");
+	}
+	gl_limit_history(gl, PWM_HISTORY_LINES_MAX);
+	/* disable default filename completion */
+	gl_customize_completion(gl, NULL, complete_nothing);
+
 	for (;;) {
 		cmd = NULL;
-		if (fgets(buf, (int)sizeof (buf), stdin) == NULL) {
-			if (ferror(stdin)) {
-				/* error */
-				warn("failed to read command");
-				goto out;
-			} else if (feof(stdin)) {
-				/* EOF */
+		line = gl_get_line(gl, prompt, NULL, -1);
+		if (line == NULL) {
+			switch (gl_return_status(gl)) {
+			case GLR_EOF:
 				break;
+			case GLR_ERROR:
+				warnx("gl_get_line: %s",
+				    gl_error_message(gl, NULL, 0));
 			}
-		}
-		if ((buf[strlen(buf) - 1] != '\n') && !feof(stdin)) {
-			/* line was truncated */
-			fprintf(stderr, "line too long\n");
-			if (is_interactive) {
-				/* skip input to next newline */
-				do {
-					errno = 0;
-					c = fgetc(stdin);
-					if ((c == EOF) && (errno != 0)) {
-						warn("failed to read command");
-						goto out;
-					}
-				} while ((c != '\n') && (c != EOF));
-			} else {
-				/* fatal error in non-interactive mode */
-				goto out;
-			}
+			break;
 		}
 
 		/* tokenize line */
-		switch(tok_tokenize(buf, &argc, &argv)) {
+		switch (tok_tokenize(line, &argc, &argv)) {
 		case TOK_ERR_SYSTEM_ERROR:
 			err(1, "tok_tokenize");
 		case TOK_ERR_UNTERMINATED_QUOTE:
@@ -183,6 +193,75 @@
 		free(argv[i]);
 	}
 	free(argv);
+	del_GetLine(gl);
+
+	return (retval);
+}
+
+int
+pwm_read_password(struct pwm_ctx *ctx, int is_new_password)
+{
+	int	retval = -1;
+	GetLine	*gl = NULL;
+	char	*line;
+	size_t	len;
+	char	password_buf[sizeof (ctx->password)] = { '\0' };
+
+	/* initialize libtecla */
+	gl = new_GetLine(sizeof (password_buf) - 1, 0);
+	if (gl == NULL) {
+		err(1, "new_GetLine");
+	}
+	/* disable default filename completion */
+	gl_customize_completion(gl, NULL, complete_nothing);
+	gl_echo_mode(gl, 0);
+
+	line = gl_get_line(gl, is_new_password ? "New password: " :
+	    "Password: ", NULL, -1);
+	putchar('\n');
+	if (line == NULL) {
+		if (gl_return_status(gl) == GLR_ERROR) {
+			errx(1, "gl_get_line: %s", gl_error_message(gl, NULL,
+			    0));
+		}
+		goto out;
+	}
+	len = strlen(line);
+	if ((len > 0) && (line[len - 1] == '\n')) {
+		line[--len] = '\0';
+	}
+	if (len == 0) {
+		fprintf(stderr, "password must not be empty\n");
+		goto out;
+	}
+	strcpy(password_buf, line);
+
+	/* confirm the entered password entered */
+	if (is_new_password) {
+		line = gl_get_line(gl, "Confirm password: ", NULL, -1);
+		putchar('\n');
+		if (line == NULL) {
+			if (gl_return_status(gl) == GLR_ERROR) {
+				errx(1, "gl_get_line: %s", gl_error_message(gl,
+				    NULL, 0));
+			}
+			goto out;
+		}
+		len = strlen(line);
+		if ((len > 0) && (line[len - 1] == '\n')) {
+			line[--len] = '\0';
+		}
+		if (strcmp(password_buf, line) != 0) {
+			fprintf(stderr, "passwords do not match\n");
+			goto out;
+		}
+	}
+
+	strcpy(ctx->password, password_buf);
+	retval = 0;
+
+out:
+	del_GetLine(gl);
 
 	return (retval);
 }
@@ -253,7 +332,6 @@
 	struct passwd	*passwd;
 	char		*pwm_dirname = NULL;
 	FILE		*fp = NULL;
-	char		password_buf[PWS3_MAX_PASSWORD_LEN + 1];
 
 	setprogname(argv[0]);
 
@@ -343,27 +421,8 @@
 		    ctx.password, sizeof (ctx.password)) != 0) {
 			goto out;
 		}
-	} else {
-		if (readpassphrase("Enter password: ", ctx.password,
-		    sizeof (ctx.password), RPP_ECHO_OFF | RPP_REQUIRE_TTY) ==
-		    NULL) {
-			err(1, "readpassphrase");
-		}
-		if (ctx.password[0] == '\0') {
-			fprintf(stderr, "password must not be empty\n");
-			goto out;
-		}
-		if (fp == NULL) {
-			if (readpassphrase("Confirm password: ", password_buf,
-			    sizeof (password_buf),
-			    RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) {
-				err(1, "readpassphrase");
-			}
-			if (strcmp(ctx.password, password_buf) != 0) {
-				fprintf(stderr, "passwords do not match\n");
-				goto out;
-			}
-		}
+	} else if (pwm_read_password(&ctx, (fp == NULL)) != 0) {
+		goto out;
 	}
 	if (fp != NULL) {
 		if (pwfile_read_file(&ctx, fp) != 0) {
--- a/pwm.h	Sat Aug 12 10:41:52 2017 +0200
+++ b/pwm.h	Thu Aug 24 13:10:56 2017 +0200
@@ -24,8 +24,13 @@
 #ifndef	PWM_H
 #define	PWM_H
 
+#include <libtecla.h>
 #include <pws.h>
 
+#ifndef	PWM_LINE_MAX
+#define	PWM_LINE_MAX	16384
+#endif /* !PWM_LINE_MAX */
+
 struct pwm_ctx {
 	const char	*prev_cmd;
 	char		*errmsg;
@@ -38,5 +43,6 @@
 };
 
 void	pwm_err(struct pwm_ctx *, char *, ...);
+int	pwm_read_password(struct pwm_ctx *, int);
 
 #endif /* PWM_H */