projects/pwm

changeset 17:a08ef0674d8e

Page long output in interactive mode
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Sat Aug 12 10:41:52 2017 +0200 (2017-08-12)
parents a07665727c19
children 1e39a251cbe9
files Makefile cmd.c compat.h compat/queue.h pager.c pager.h pwm.1.xml
line diff
     1.1 --- a/Makefile	Tue Aug 08 10:47:04 2017 +0200
     1.2 +++ b/Makefile	Sat Aug 12 10:41:52 2017 +0200
     1.3 @@ -81,6 +81,7 @@
     1.4    HAVE_ASPRINTF ?=	1
     1.5    HAVE_ERR_H ?=		1
     1.6    HAVE_GETRANDOM ?=	0
     1.7 +  HAVE_SYS_QUEUE_H ?=	0
     1.8    HAVE_READPASSPHRASE_H ?= 0
     1.9    HAVE_SETPROGNAME ?=	0
    1.10    HAVE_SYS_TREE_H ?=	0
    1.11 @@ -89,6 +90,7 @@
    1.12    HAVE_ASPRINTF ?=	1
    1.13    HAVE_ERR_H ?=		1
    1.14    HAVE_GETRANDOM ?=	0
    1.15 +  HAVE_SYS_QUEUE_H ?=	1
    1.16    HAVE_READPASSPHRASE_H ?= 1
    1.17    HAVE_SETPROGNAME ?=	1
    1.18    HAVE_SYS_TREE_H ?=	1
    1.19 @@ -97,6 +99,7 @@
    1.20    HAVE_ASPRINTF ?=	1
    1.21    HAVE_ERR_H ?=		1
    1.22    HAVE_GETRANDOM ?=	0
    1.23 +  HAVE_SYS_QUEUE_H ?=	1
    1.24    HAVE_READPASSPHRASE_H ?= 0
    1.25    HAVE_SYS_TREE_H ?=	1
    1.26    HAVE_SETPROGNAME ?=	1
    1.27 @@ -105,6 +108,7 @@
    1.28    HAVE_ASPRINTF ?=	1
    1.29    HAVE_ERR_H ?=		1
    1.30    HAVE_GETRANDOM ?=	0
    1.31 +  HAVE_SYS_QUEUE_H ?=	1
    1.32    HAVE_READPASSPHRASE_H ?= 1
    1.33    HAVE_SYS_TREE_H ?=	1
    1.34    HAVE_SETPROGNAME ?=	1
    1.35 @@ -120,6 +124,7 @@
    1.36      HAVE_ERR_H ?=	1
    1.37      HAVE_GETRANDOM ?=	1
    1.38    endif
    1.39 +  HAVE_SYS_QUEUE_H ?=	0
    1.40    HAVE_READPASSPHRASE_H ?= 0
    1.41    HAVE_SYS_TREE_H ?=	0
    1.42    HAVE_SETPROGNAME ?=	0
    1.43 @@ -128,12 +133,14 @@
    1.44    HAVE_ASPRINTF ?=	0
    1.45    HAVE_ERR_H ?=		0
    1.46    HAVE_GETRANDOM ?=	0
    1.47 +  HAVE_SYS_QUEUE_H ?=	0
    1.48    HAVE_READPASSPHRASE_H ?= 0
    1.49    HAVE_SETPROGNAME ?=	0
    1.50    HAVE_SYS_TREE_H ?=	0
    1.51  endif
    1.52  
    1.53  OBJS =	cmd.o \
    1.54 +	pager.o \
    1.55  	pw.o \
    1.56  	pwfile.o \
    1.57  	pwm.o \
     2.1 --- a/cmd.c	Tue Aug 08 10:47:04 2017 +0200
     2.2 +++ b/cmd.c	Sat Aug 12 10:41:52 2017 +0200
     2.3 @@ -39,6 +39,7 @@
     2.4  #include <unistd.h>
     2.5  
     2.6  #include "cmd.h"
     2.7 +#include "pager.h"
     2.8  #include "pw.h"
     2.9  #include "pwfile.h"
    2.10  #include "util.h"
    2.11 @@ -236,6 +237,7 @@
    2.12  cmd_info(struct pwm_ctx *ctx, int argc, char *argv[])
    2.13  {
    2.14  	struct metadata	*metadata;
    2.15 +	struct pager	*pager;
    2.16  	struct tm	*tm;
    2.17  	char		timebuf[TIME_SIZE];
    2.18  
    2.19 @@ -244,19 +246,23 @@
    2.20  	}
    2.21  
    2.22  	metadata = pwfile_get_metadata(ctx);
    2.23 -	printf("Format:      0x%04x\n", metadata->version);
    2.24 +
    2.25 +	pager = pager_create(stdout);
    2.26 +	pager_printf(pager, "Format:      0x%04x\n", metadata->version);
    2.27  	if (metadata->user != NULL) {
    2.28 -		printf("User:        %s\n", metadata->user);
    2.29 +		pager_printf(pager, "User:        %s\n", metadata->user);
    2.30  	}
    2.31  	if (metadata->user != NULL) {
    2.32 -		printf("Host:        %s\n", metadata->host);
    2.33 +		pager_printf(pager, "Host:        %s\n", metadata->host);
    2.34  	}
    2.35  	if (metadata->user != NULL) {
    2.36 -		printf("Application: %s\n", metadata->application);
    2.37 +		pager_printf(pager, "Application: %s\n", metadata->application);
    2.38  	}
    2.39  	tm = gmtime(&metadata->timestamp);
    2.40  	strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm);
    2.41 -	printf("Last Saved:  %s\n", timebuf);
    2.42 +	pager_printf(pager, "Last Saved:  %s\n", timebuf);
    2.43 +	pager_show(pager);
    2.44 +	pager_destroy(pager);
    2.45  
    2.46  	pwfile_destroy_metadata(metadata);
    2.47  
    2.48 @@ -279,6 +285,7 @@
    2.49  	int		errcode;
    2.50  	char		*errbuf;
    2.51  	size_t		errbuf_size;
    2.52 +	struct pager	*pager = NULL;
    2.53  	union list_item	**list = NULL;
    2.54  	size_t		j;
    2.55  	struct record	*record;
    2.56 @@ -334,10 +341,11 @@
    2.57  		}
    2.58  	}
    2.59  
    2.60 +	pager = pager_create(stdout);
    2.61  	list = pwfile_create_list(ctx);
    2.62  	for (j = 0; list[j] != NULL; j++) {
    2.63  		if (list[j]->any.type == ITEM_TYPE_GROUP) {
    2.64 -			printf("[%s]\n", list[j]->group.group);
    2.65 +			pager_printf(pager, "[%s]\n", list[j]->group.group);
    2.66  		} else {
    2.67  			record = pwfile_get_record(ctx, list[j]->record.id);
    2.68  			if (((group_re == NULL) || (regexec(group_re,
    2.69 @@ -350,16 +358,21 @@
    2.70  			    record->notes, 0, NULL, 0) == 0)) &&
    2.71  			    ((url_re == NULL) || (regexec(url_re,
    2.72  			    record->url, 0, NULL, 0) == 0))) {
    2.73 -				printf("%4u %s\n", list[j]->record.id,
    2.74 +				pager_printf(pager, "%4u %s\n",
    2.75 +				    list[j]->record.id,
    2.76  				    (list[j]->record.title != NULL) ?
    2.77  				    list[j]->record.title : "");
    2.78  			}
    2.79  			pwfile_destroy_record(record);
    2.80  		}
    2.81  	}
    2.82 +	pager_show(pager);
    2.83 +
    2.84  	retval = CMD_OK;
    2.85  
    2.86  out:
    2.87 +	pager_destroy(pager);
    2.88 +
    2.89  	if (group_re != NULL) {
    2.90  		regfree(group_re);
    2.91  		free(group_re);
    2.92 @@ -383,6 +396,7 @@
    2.93  
    2.94  	pwfile_destroy_list(list);
    2.95  
    2.96 +
    2.97  	return (retval);
    2.98  }
    2.99  
   2.100 @@ -656,76 +670,58 @@
   2.101  	return (CMD_OK);
   2.102  }
   2.103  
   2.104 -static int
   2.105 -print_field(const char *label, const char *value, int show_label, FILE *fp)
   2.106 -{
   2.107 -	fprintf(fp, "%s%s\n", show_label ? label : "", (value != NULL) ?
   2.108 -	    value : "");
   2.109 -	if (ferror(fp)) {
   2.110 -		warn("fprintf");
   2.111 -		return (-1);
   2.112 -	}
   2.113 -	return (0);
   2.114 -}
   2.115 -
   2.116  static void
   2.117  print_record(struct record *record, int fields[], int show_labels, FILE *fp)
   2.118  {
   2.119 +	struct pager	*pager;
   2.120  	struct tm	*tm;
   2.121  	char		timebuf[TIME_SIZE];
   2.122  
   2.123 +	pager = pager_create(fp);
   2.124  	if (fields[FIELD_TITLE]) {
   2.125 -		if (print_field(field_labels[FIELD_TITLE], record->title,
   2.126 -		    show_labels, fp) != 0) {
   2.127 -			return;
   2.128 -		}
   2.129 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.130 +		    field_labels[FIELD_TITLE] : "", (record->title != NULL) ?
   2.131 +		    record->title : "");
   2.132  	}
   2.133  	if (fields[FIELD_GROUP]) {
   2.134 -		if (print_field(field_labels[FIELD_GROUP], record->group,
   2.135 -		    show_labels, fp)) {
   2.136 -			return;
   2.137 -		}
   2.138 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.139 +		    field_labels[FIELD_GROUP] : "", (record->group != NULL) ?
   2.140 +		    record->group : "");
   2.141  	}
   2.142  	if (fields[FIELD_USERNAME]) {
   2.143 -		if (print_field(field_labels[FIELD_USERNAME], record->username,
   2.144 -		    show_labels, fp)) {
   2.145 -			return;
   2.146 -		}
   2.147 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.148 +		    field_labels[FIELD_USERNAME] : "",
   2.149 +		    (record->username != NULL) ?  record->username : "");
   2.150  	}
   2.151  	if (fields[FIELD_PASSWORD]) {
   2.152 -		if (print_field(field_labels[FIELD_PASSWORD], record->password,
   2.153 -		    show_labels, fp)) {
   2.154 -			return;
   2.155 -		}
   2.156 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.157 +		    field_labels[FIELD_PASSWORD] : "",
   2.158 +		    (record->password != NULL) ?  record->password : "");
   2.159  	}
   2.160  	if (fields[FIELD_NOTES]) {
   2.161 -		if (print_field(field_labels[FIELD_NOTES], record->notes,
   2.162 -		    show_labels, fp)) {
   2.163 -			return;
   2.164 -		}
   2.165 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.166 +		    field_labels[FIELD_NOTES] : "", (record->notes != NULL) ?
   2.167 +		    record->notes : "");
   2.168  	}
   2.169  	if (fields[FIELD_URL]) {
   2.170 -		if (print_field(field_labels[FIELD_URL], record->url,
   2.171 -		    show_labels, fp)) {
   2.172 -			return;
   2.173 -		}
   2.174 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.175 +		    field_labels[FIELD_URL] : "", (record->url != NULL) ?
   2.176 +		    record->url : "");
   2.177  	}
   2.178  	if (fields[FIELD_CTIME]) {
   2.179  		tm = gmtime(&record->ctime);
   2.180  		strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm);
   2.181 -		if (print_field(field_labels[FIELD_CTIME], timebuf,
   2.182 -		    show_labels, fp)) {
   2.183 -			return;
   2.184 -		}
   2.185 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.186 +		    field_labels[FIELD_CTIME] : "", timebuf);
   2.187  	}
   2.188  	if (fields[FIELD_MTIME]) {
   2.189  		tm = gmtime(&record->mtime);
   2.190  		strftime(timebuf, sizeof (timebuf), TIME_FORMAT, tm);
   2.191 -		if (print_field(field_labels[FIELD_MTIME], timebuf,
   2.192 -		    show_labels, fp)) {
   2.193 -			return;
   2.194 -		}
   2.195 +		pager_printf(pager, "%s%s\n", show_labels ?
   2.196 +		    field_labels[FIELD_MTIME] : "", timebuf);
   2.197  	}
   2.198 +	pager_show(pager);
   2.199 +	pager_destroy(pager);
   2.200  }
   2.201  
   2.202  static enum cmd_return
   2.203 @@ -909,27 +905,31 @@
   2.204  static enum cmd_return
   2.205  cmd_help(struct pwm_ctx *ctx, int argc, char *argv[])
   2.206  {
   2.207 +	struct pager	*pager;
   2.208  	struct cmd	*cmd;
   2.209  
   2.210  	if (argc > 2) {
   2.211  		return (CMD_USAGE);
   2.212  	}
   2.213  
   2.214 +	pager = pager_create(stdout);
   2.215  	if (argc == 2) {
   2.216  		for (cmd = cmds; cmd->cmd_func != NULL; cmd++) {
   2.217  			if ((strcmp(argv[1], cmd->abbrev_cmd) == 0) ||
   2.218  			    (strcmp(argv[1], cmd->full_cmd) == 0)) {
   2.219 -				printf("%s\n", cmd->usage);
   2.220 +				pager_printf(pager, "%s\n", cmd->usage);
   2.221  				break;
   2.222  			}
   2.223  		}
   2.224  	} else {
   2.225  		printf("Commands:\n");
   2.226  		for (cmd = cmds; cmd->cmd_func != NULL; cmd++) {
   2.227 -			printf("%-2s %-16s %s\n", cmd->abbrev_cmd,
   2.228 +			pager_printf(pager, "%-2s %-16s %s\n", cmd->abbrev_cmd,
   2.229  			    cmd->full_cmd, cmd->description);
   2.230  		}
   2.231  	}
   2.232 +	pager_show(pager);
   2.233 +	pager_destroy(pager);
   2.234  
   2.235  	return (CMD_OK);
   2.236  }
     3.1 --- a/compat.h	Tue Aug 08 10:47:04 2017 +0200
     3.2 +++ b/compat.h	Sat Aug 12 10:41:52 2017 +0200
     3.3 @@ -39,6 +39,10 @@
     3.4  #include "compat/getentropy.h"
     3.5  #endif /* !HAVE_GETENTROPY */
     3.6  
     3.7 +#ifndef	HAVE_SYS_QUEUE_H
     3.8 +#include "compat/queue.h"
     3.9 +#endif /* !HAVE_SYS_QUEUE_H */
    3.10 +
    3.11  #ifndef	HAVE_READPASSPHRASE_H
    3.12  #include "compat/readpassphrase.h"
    3.13  #endif /* !HAVE_READPASSPHRASE_H */
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/compat/queue.h	Sat Aug 12 10:41:52 2017 +0200
     4.3 @@ -0,0 +1,535 @@
     4.4 +/*	$OpenBSD: queue.h,v 1.44 2016/09/09 20:31:46 millert Exp $	*/
     4.5 +/*	$NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $	*/
     4.6 +
     4.7 +/*
     4.8 + * Copyright (c) 1991, 1993
     4.9 + *	The Regents of the University of California.  All rights reserved.
    4.10 + *
    4.11 + * Redistribution and use in source and binary forms, with or without
    4.12 + * modification, are permitted provided that the following conditions
    4.13 + * are met:
    4.14 + * 1. Redistributions of source code must retain the above copyright
    4.15 + *    notice, this list of conditions and the following disclaimer.
    4.16 + * 2. Redistributions in binary form must reproduce the above copyright
    4.17 + *    notice, this list of conditions and the following disclaimer in the
    4.18 + *    documentation and/or other materials provided with the distribution.
    4.19 + * 3. Neither the name of the University nor the names of its contributors
    4.20 + *    may be used to endorse or promote products derived from this software
    4.21 + *    without specific prior written permission.
    4.22 + *
    4.23 + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
    4.24 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    4.25 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    4.26 + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
    4.27 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    4.28 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    4.29 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    4.30 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    4.31 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    4.32 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    4.33 + * SUCH DAMAGE.
    4.34 + *
    4.35 + *	@(#)queue.h	8.5 (Berkeley) 8/20/94
    4.36 + */
    4.37 +
    4.38 +#ifndef	_SYS_QUEUE_H_
    4.39 +#define	_SYS_QUEUE_H_
    4.40 +
    4.41 +#include <stddef.h>
    4.42 +
    4.43 +/*
    4.44 + * This file defines five types of data structures: singly-linked lists,
    4.45 + * lists, simple queues, tail queues and XOR simple queues.
    4.46 + *
    4.47 + *
    4.48 + * A singly-linked list is headed by a single forward pointer. The elements
    4.49 + * are singly linked for minimum space and pointer manipulation overhead at
    4.50 + * the expense of O(n) removal for arbitrary elements. New elements can be
    4.51 + * added to the list after an existing element or at the head of the list.
    4.52 + * Elements being removed from the head of the list should use the explicit
    4.53 + * macro for this purpose for optimum efficiency. A singly-linked list may
    4.54 + * only be traversed in the forward direction.  Singly-linked lists are ideal
    4.55 + * for applications with large datasets and few or no removals or for
    4.56 + * implementing a LIFO queue.
    4.57 + *
    4.58 + * A list is headed by a single forward pointer (or an array of forward
    4.59 + * pointers for a hash table header). The elements are doubly linked
    4.60 + * so that an arbitrary element can be removed without a need to
    4.61 + * traverse the list. New elements can be added to the list before
    4.62 + * or after an existing element or at the head of the list. A list
    4.63 + * may only be traversed in the forward direction.
    4.64 + *
    4.65 + * A simple queue is headed by a pair of pointers, one to the head of the
    4.66 + * list and the other to the tail of the list. The elements are singly
    4.67 + * linked to save space, so elements can only be removed from the
    4.68 + * head of the list. New elements can be added to the list before or after
    4.69 + * an existing element, at the head of the list, or at the end of the
    4.70 + * list. A simple queue may only be traversed in the forward direction.
    4.71 + *
    4.72 + * A tail queue is headed by a pair of pointers, one to the head of the
    4.73 + * list and the other to the tail of the list. The elements are doubly
    4.74 + * linked so that an arbitrary element can be removed without a need to
    4.75 + * traverse the list. New elements can be added to the list before or
    4.76 + * after an existing element, at the head of the list, or at the end of
    4.77 + * the list. A tail queue may be traversed in either direction.
    4.78 + *
    4.79 + * An XOR simple queue is used in the same way as a regular simple queue.
    4.80 + * The difference is that the head structure also includes a "cookie" that
    4.81 + * is XOR'd with the queue pointer (first, last or next) to generate the
    4.82 + * real pointer value.
    4.83 + *
    4.84 + * For details on the use of these macros, see the queue(3) manual page.
    4.85 + */
    4.86 +
    4.87 +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
    4.88 +#define _Q_INVALIDATE(a) (a) = ((void *)-1)
    4.89 +#else
    4.90 +#define _Q_INVALIDATE(a)
    4.91 +#endif
    4.92 +
    4.93 +/*
    4.94 + * Singly-linked List definitions.
    4.95 + */
    4.96 +#define SLIST_HEAD(name, type)						\
    4.97 +struct name {								\
    4.98 +	struct type *slh_first;	/* first element */			\
    4.99 +}
   4.100 +
   4.101 +#define	SLIST_HEAD_INITIALIZER(head)					\
   4.102 +	{ NULL }
   4.103 +
   4.104 +#define SLIST_ENTRY(type)						\
   4.105 +struct {								\
   4.106 +	struct type *sle_next;	/* next element */			\
   4.107 +}
   4.108 +
   4.109 +/*
   4.110 + * Singly-linked List access methods.
   4.111 + */
   4.112 +#define	SLIST_FIRST(head)	((head)->slh_first)
   4.113 +#define	SLIST_END(head)		NULL
   4.114 +#define	SLIST_EMPTY(head)	(SLIST_FIRST(head) == SLIST_END(head))
   4.115 +#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
   4.116 +
   4.117 +#define	SLIST_FOREACH(var, head, field)					\
   4.118 +	for((var) = SLIST_FIRST(head);					\
   4.119 +	    (var) != SLIST_END(head);					\
   4.120 +	    (var) = SLIST_NEXT(var, field))
   4.121 +
   4.122 +#define	SLIST_FOREACH_SAFE(var, head, field, tvar)			\
   4.123 +	for ((var) = SLIST_FIRST(head);				\
   4.124 +	    (var) && ((tvar) = SLIST_NEXT(var, field), 1);		\
   4.125 +	    (var) = (tvar))
   4.126 +
   4.127 +/*
   4.128 + * Singly-linked List functions.
   4.129 + */
   4.130 +#define	SLIST_INIT(head) {						\
   4.131 +	SLIST_FIRST(head) = SLIST_END(head);				\
   4.132 +}
   4.133 +
   4.134 +#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
   4.135 +	(elm)->field.sle_next = (slistelm)->field.sle_next;		\
   4.136 +	(slistelm)->field.sle_next = (elm);				\
   4.137 +} while (0)
   4.138 +
   4.139 +#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
   4.140 +	(elm)->field.sle_next = (head)->slh_first;			\
   4.141 +	(head)->slh_first = (elm);					\
   4.142 +} while (0)
   4.143 +
   4.144 +#define	SLIST_REMOVE_AFTER(elm, field) do {				\
   4.145 +	(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next;	\
   4.146 +} while (0)
   4.147 +
   4.148 +#define	SLIST_REMOVE_HEAD(head, field) do {				\
   4.149 +	(head)->slh_first = (head)->slh_first->field.sle_next;		\
   4.150 +} while (0)
   4.151 +
   4.152 +#define SLIST_REMOVE(head, elm, type, field) do {			\
   4.153 +	if ((head)->slh_first == (elm)) {				\
   4.154 +		SLIST_REMOVE_HEAD((head), field);			\
   4.155 +	} else {							\
   4.156 +		struct type *curelm = (head)->slh_first;		\
   4.157 +									\
   4.158 +		while (curelm->field.sle_next != (elm))			\
   4.159 +			curelm = curelm->field.sle_next;		\
   4.160 +		curelm->field.sle_next =				\
   4.161 +		    curelm->field.sle_next->field.sle_next;		\
   4.162 +	}								\
   4.163 +	_Q_INVALIDATE((elm)->field.sle_next);				\
   4.164 +} while (0)
   4.165 +
   4.166 +/*
   4.167 + * List definitions.
   4.168 + */
   4.169 +#define LIST_HEAD(name, type)						\
   4.170 +struct name {								\
   4.171 +	struct type *lh_first;	/* first element */			\
   4.172 +}
   4.173 +
   4.174 +#define LIST_HEAD_INITIALIZER(head)					\
   4.175 +	{ NULL }
   4.176 +
   4.177 +#define LIST_ENTRY(type)						\
   4.178 +struct {								\
   4.179 +	struct type *le_next;	/* next element */			\
   4.180 +	struct type **le_prev;	/* address of previous next element */	\
   4.181 +}
   4.182 +
   4.183 +/*
   4.184 + * List access methods.
   4.185 + */
   4.186 +#define	LIST_FIRST(head)		((head)->lh_first)
   4.187 +#define	LIST_END(head)			NULL
   4.188 +#define	LIST_EMPTY(head)		(LIST_FIRST(head) == LIST_END(head))
   4.189 +#define	LIST_NEXT(elm, field)		((elm)->field.le_next)
   4.190 +
   4.191 +#define LIST_FOREACH(var, head, field)					\
   4.192 +	for((var) = LIST_FIRST(head);					\
   4.193 +	    (var)!= LIST_END(head);					\
   4.194 +	    (var) = LIST_NEXT(var, field))
   4.195 +
   4.196 +#define	LIST_FOREACH_SAFE(var, head, field, tvar)			\
   4.197 +	for ((var) = LIST_FIRST(head);				\
   4.198 +	    (var) && ((tvar) = LIST_NEXT(var, field), 1);		\
   4.199 +	    (var) = (tvar))
   4.200 +
   4.201 +/*
   4.202 + * List functions.
   4.203 + */
   4.204 +#define	LIST_INIT(head) do {						\
   4.205 +	LIST_FIRST(head) = LIST_END(head);				\
   4.206 +} while (0)
   4.207 +
   4.208 +#define LIST_INSERT_AFTER(listelm, elm, field) do {			\
   4.209 +	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
   4.210 +		(listelm)->field.le_next->field.le_prev =		\
   4.211 +		    &(elm)->field.le_next;				\
   4.212 +	(listelm)->field.le_next = (elm);				\
   4.213 +	(elm)->field.le_prev = &(listelm)->field.le_next;		\
   4.214 +} while (0)
   4.215 +
   4.216 +#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
   4.217 +	(elm)->field.le_prev = (listelm)->field.le_prev;		\
   4.218 +	(elm)->field.le_next = (listelm);				\
   4.219 +	*(listelm)->field.le_prev = (elm);				\
   4.220 +	(listelm)->field.le_prev = &(elm)->field.le_next;		\
   4.221 +} while (0)
   4.222 +
   4.223 +#define LIST_INSERT_HEAD(head, elm, field) do {				\
   4.224 +	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
   4.225 +		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
   4.226 +	(head)->lh_first = (elm);					\
   4.227 +	(elm)->field.le_prev = &(head)->lh_first;			\
   4.228 +} while (0)
   4.229 +
   4.230 +#define LIST_REMOVE(elm, field) do {					\
   4.231 +	if ((elm)->field.le_next != NULL)				\
   4.232 +		(elm)->field.le_next->field.le_prev =			\
   4.233 +		    (elm)->field.le_prev;				\
   4.234 +	*(elm)->field.le_prev = (elm)->field.le_next;			\
   4.235 +	_Q_INVALIDATE((elm)->field.le_prev);				\
   4.236 +	_Q_INVALIDATE((elm)->field.le_next);				\
   4.237 +} while (0)
   4.238 +
   4.239 +#define LIST_REPLACE(elm, elm2, field) do {				\
   4.240 +	if (((elm2)->field.le_next = (elm)->field.le_next) != NULL)	\
   4.241 +		(elm2)->field.le_next->field.le_prev =			\
   4.242 +		    &(elm2)->field.le_next;				\
   4.243 +	(elm2)->field.le_prev = (elm)->field.le_prev;			\
   4.244 +	*(elm2)->field.le_prev = (elm2);				\
   4.245 +	_Q_INVALIDATE((elm)->field.le_prev);				\
   4.246 +	_Q_INVALIDATE((elm)->field.le_next);				\
   4.247 +} while (0)
   4.248 +
   4.249 +/*
   4.250 + * Simple queue definitions.
   4.251 + */
   4.252 +#define SIMPLEQ_HEAD(name, type)					\
   4.253 +struct name {								\
   4.254 +	struct type *sqh_first;	/* first element */			\
   4.255 +	struct type **sqh_last;	/* addr of last next element */		\
   4.256 +}
   4.257 +
   4.258 +#define SIMPLEQ_HEAD_INITIALIZER(head)					\
   4.259 +	{ NULL, &(head).sqh_first }
   4.260 +
   4.261 +#define SIMPLEQ_ENTRY(type)						\
   4.262 +struct {								\
   4.263 +	struct type *sqe_next;	/* next element */			\
   4.264 +}
   4.265 +
   4.266 +/*
   4.267 + * Simple queue access methods.
   4.268 + */
   4.269 +#define	SIMPLEQ_FIRST(head)	    ((head)->sqh_first)
   4.270 +#define	SIMPLEQ_END(head)	    NULL
   4.271 +#define	SIMPLEQ_EMPTY(head)	    (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
   4.272 +#define	SIMPLEQ_NEXT(elm, field)    ((elm)->field.sqe_next)
   4.273 +
   4.274 +#define SIMPLEQ_FOREACH(var, head, field)				\
   4.275 +	for((var) = SIMPLEQ_FIRST(head);				\
   4.276 +	    (var) != SIMPLEQ_END(head);					\
   4.277 +	    (var) = SIMPLEQ_NEXT(var, field))
   4.278 +
   4.279 +#define	SIMPLEQ_FOREACH_SAFE(var, head, field, tvar)			\
   4.280 +	for ((var) = SIMPLEQ_FIRST(head);				\
   4.281 +	    (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1);		\
   4.282 +	    (var) = (tvar))
   4.283 +
   4.284 +/*
   4.285 + * Simple queue functions.
   4.286 + */
   4.287 +#define	SIMPLEQ_INIT(head) do {						\
   4.288 +	(head)->sqh_first = NULL;					\
   4.289 +	(head)->sqh_last = &(head)->sqh_first;				\
   4.290 +} while (0)
   4.291 +
   4.292 +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
   4.293 +	if (((elm)->field.sqe_next = (head)->sqh_first) == NULL)	\
   4.294 +		(head)->sqh_last = &(elm)->field.sqe_next;		\
   4.295 +	(head)->sqh_first = (elm);					\
   4.296 +} while (0)
   4.297 +
   4.298 +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
   4.299 +	(elm)->field.sqe_next = NULL;					\
   4.300 +	*(head)->sqh_last = (elm);					\
   4.301 +	(head)->sqh_last = &(elm)->field.sqe_next;			\
   4.302 +} while (0)
   4.303 +
   4.304 +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
   4.305 +	if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
   4.306 +		(head)->sqh_last = &(elm)->field.sqe_next;		\
   4.307 +	(listelm)->field.sqe_next = (elm);				\
   4.308 +} while (0)
   4.309 +
   4.310 +#define SIMPLEQ_REMOVE_HEAD(head, field) do {			\
   4.311 +	if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
   4.312 +		(head)->sqh_last = &(head)->sqh_first;			\
   4.313 +} while (0)
   4.314 +
   4.315 +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do {			\
   4.316 +	if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
   4.317 +	    == NULL)							\
   4.318 +		(head)->sqh_last = &(elm)->field.sqe_next;		\
   4.319 +} while (0)
   4.320 +
   4.321 +#define SIMPLEQ_CONCAT(head1, head2) do {				\
   4.322 +	if (!SIMPLEQ_EMPTY((head2))) {					\
   4.323 +		*(head1)->sqh_last = (head2)->sqh_first;		\
   4.324 +		(head1)->sqh_last = (head2)->sqh_last;			\
   4.325 +		SIMPLEQ_INIT((head2));					\
   4.326 +	}								\
   4.327 +} while (0)
   4.328 +
   4.329 +/*
   4.330 + * XOR Simple queue definitions.
   4.331 + */
   4.332 +#define XSIMPLEQ_HEAD(name, type)					\
   4.333 +struct name {								\
   4.334 +	struct type *sqx_first;	/* first element */			\
   4.335 +	struct type **sqx_last;	/* addr of last next element */		\
   4.336 +	unsigned long sqx_cookie;					\
   4.337 +}
   4.338 +
   4.339 +#define XSIMPLEQ_ENTRY(type)						\
   4.340 +struct {								\
   4.341 +	struct type *sqx_next;	/* next element */			\
   4.342 +}
   4.343 +
   4.344 +/*
   4.345 + * XOR Simple queue access methods.
   4.346 + */
   4.347 +#define XSIMPLEQ_XOR(head, ptr)	    ((__typeof(ptr))((head)->sqx_cookie ^ \
   4.348 +					(unsigned long)(ptr)))
   4.349 +#define	XSIMPLEQ_FIRST(head)	    XSIMPLEQ_XOR(head, ((head)->sqx_first))
   4.350 +#define	XSIMPLEQ_END(head)	    NULL
   4.351 +#define	XSIMPLEQ_EMPTY(head)	    (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
   4.352 +#define	XSIMPLEQ_NEXT(head, elm, field)    XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
   4.353 +
   4.354 +
   4.355 +#define XSIMPLEQ_FOREACH(var, head, field)				\
   4.356 +	for ((var) = XSIMPLEQ_FIRST(head);				\
   4.357 +	    (var) != XSIMPLEQ_END(head);				\
   4.358 +	    (var) = XSIMPLEQ_NEXT(head, var, field))
   4.359 +
   4.360 +#define	XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar)			\
   4.361 +	for ((var) = XSIMPLEQ_FIRST(head);				\
   4.362 +	    (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1);	\
   4.363 +	    (var) = (tvar))
   4.364 +
   4.365 +/*
   4.366 + * XOR Simple queue functions.
   4.367 + */
   4.368 +#define	XSIMPLEQ_INIT(head) do {					\
   4.369 +	arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
   4.370 +	(head)->sqx_first = XSIMPLEQ_XOR(head, NULL);			\
   4.371 +	(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first);	\
   4.372 +} while (0)
   4.373 +
   4.374 +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do {			\
   4.375 +	if (((elm)->field.sqx_next = (head)->sqx_first) ==		\
   4.376 +	    XSIMPLEQ_XOR(head, NULL))					\
   4.377 +		(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
   4.378 +	(head)->sqx_first = XSIMPLEQ_XOR(head, (elm));			\
   4.379 +} while (0)
   4.380 +
   4.381 +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do {			\
   4.382 +	(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL);		\
   4.383 +	*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
   4.384 +	(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);	\
   4.385 +} while (0)
   4.386 +
   4.387 +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
   4.388 +	if (((elm)->field.sqx_next = (listelm)->field.sqx_next) ==	\
   4.389 +	    XSIMPLEQ_XOR(head, NULL))					\
   4.390 +		(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
   4.391 +	(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm));		\
   4.392 +} while (0)
   4.393 +
   4.394 +#define XSIMPLEQ_REMOVE_HEAD(head, field) do {				\
   4.395 +	if (((head)->sqx_first = XSIMPLEQ_XOR(head,			\
   4.396 +	    (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
   4.397 +		(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
   4.398 +} while (0)
   4.399 +
   4.400 +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do {			\
   4.401 +	if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head,			\
   4.402 +	    (elm)->field.sqx_next)->field.sqx_next)			\
   4.403 +	    == XSIMPLEQ_XOR(head, NULL))				\
   4.404 +		(head)->sqx_last = 					\
   4.405 +		    XSIMPLEQ_XOR(head, &(elm)->field.sqx_next);		\
   4.406 +} while (0)
   4.407 +
   4.408 +
   4.409 +/*
   4.410 + * Tail queue definitions.
   4.411 + */
   4.412 +#define TAILQ_HEAD(name, type)						\
   4.413 +struct name {								\
   4.414 +	struct type *tqh_first;	/* first element */			\
   4.415 +	struct type **tqh_last;	/* addr of last next element */		\
   4.416 +}
   4.417 +
   4.418 +#define TAILQ_HEAD_INITIALIZER(head)					\
   4.419 +	{ NULL, &(head).tqh_first }
   4.420 +
   4.421 +#define TAILQ_ENTRY(type)						\
   4.422 +struct {								\
   4.423 +	struct type *tqe_next;	/* next element */			\
   4.424 +	struct type **tqe_prev;	/* address of previous next element */	\
   4.425 +}
   4.426 +
   4.427 +/*
   4.428 + * Tail queue access methods.
   4.429 + */
   4.430 +#define	TAILQ_FIRST(head)		((head)->tqh_first)
   4.431 +#define	TAILQ_END(head)			NULL
   4.432 +#define	TAILQ_NEXT(elm, field)		((elm)->field.tqe_next)
   4.433 +#define TAILQ_LAST(head, headname)					\
   4.434 +	(*(((struct headname *)((head)->tqh_last))->tqh_last))
   4.435 +/* XXX */
   4.436 +#define TAILQ_PREV(elm, headname, field)				\
   4.437 +	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
   4.438 +#define	TAILQ_EMPTY(head)						\
   4.439 +	(TAILQ_FIRST(head) == TAILQ_END(head))
   4.440 +
   4.441 +#define TAILQ_FOREACH(var, head, field)					\
   4.442 +	for((var) = TAILQ_FIRST(head);					\
   4.443 +	    (var) != TAILQ_END(head);					\
   4.444 +	    (var) = TAILQ_NEXT(var, field))
   4.445 +
   4.446 +#define	TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
   4.447 +	for ((var) = TAILQ_FIRST(head);					\
   4.448 +	    (var) != TAILQ_END(head) &&					\
   4.449 +	    ((tvar) = TAILQ_NEXT(var, field), 1);			\
   4.450 +	    (var) = (tvar))
   4.451 +
   4.452 +
   4.453 +#define TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
   4.454 +	for((var) = TAILQ_LAST(head, headname);				\
   4.455 +	    (var) != TAILQ_END(head);					\
   4.456 +	    (var) = TAILQ_PREV(var, headname, field))
   4.457 +
   4.458 +#define	TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar)	\
   4.459 +	for ((var) = TAILQ_LAST(head, headname);			\
   4.460 +	    (var) != TAILQ_END(head) &&					\
   4.461 +	    ((tvar) = TAILQ_PREV(var, headname, field), 1);		\
   4.462 +	    (var) = (tvar))
   4.463 +
   4.464 +/*
   4.465 + * Tail queue functions.
   4.466 + */
   4.467 +#define	TAILQ_INIT(head) do {						\
   4.468 +	(head)->tqh_first = NULL;					\
   4.469 +	(head)->tqh_last = &(head)->tqh_first;				\
   4.470 +} while (0)
   4.471 +
   4.472 +#define TAILQ_INSERT_HEAD(head, elm, field) do {			\
   4.473 +	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
   4.474 +		(head)->tqh_first->field.tqe_prev =			\
   4.475 +		    &(elm)->field.tqe_next;				\
   4.476 +	else								\
   4.477 +		(head)->tqh_last = &(elm)->field.tqe_next;		\
   4.478 +	(head)->tqh_first = (elm);					\
   4.479 +	(elm)->field.tqe_prev = &(head)->tqh_first;			\
   4.480 +} while (0)
   4.481 +
   4.482 +#define TAILQ_INSERT_TAIL(head, elm, field) do {			\
   4.483 +	(elm)->field.tqe_next = NULL;					\
   4.484 +	(elm)->field.tqe_prev = (head)->tqh_last;			\
   4.485 +	*(head)->tqh_last = (elm);					\
   4.486 +	(head)->tqh_last = &(elm)->field.tqe_next;			\
   4.487 +} while (0)
   4.488 +
   4.489 +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
   4.490 +	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
   4.491 +		(elm)->field.tqe_next->field.tqe_prev =			\
   4.492 +		    &(elm)->field.tqe_next;				\
   4.493 +	else								\
   4.494 +		(head)->tqh_last = &(elm)->field.tqe_next;		\
   4.495 +	(listelm)->field.tqe_next = (elm);				\
   4.496 +	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
   4.497 +} while (0)
   4.498 +
   4.499 +#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
   4.500 +	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
   4.501 +	(elm)->field.tqe_next = (listelm);				\
   4.502 +	*(listelm)->field.tqe_prev = (elm);				\
   4.503 +	(listelm)->field.tqe_prev = &(elm)->field.tqe_next;		\
   4.504 +} while (0)
   4.505 +
   4.506 +#define TAILQ_REMOVE(head, elm, field) do {				\
   4.507 +	if (((elm)->field.tqe_next) != NULL)				\
   4.508 +		(elm)->field.tqe_next->field.tqe_prev =			\
   4.509 +		    (elm)->field.tqe_prev;				\
   4.510 +	else								\
   4.511 +		(head)->tqh_last = (elm)->field.tqe_prev;		\
   4.512 +	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
   4.513 +	_Q_INVALIDATE((elm)->field.tqe_prev);				\
   4.514 +	_Q_INVALIDATE((elm)->field.tqe_next);				\
   4.515 +} while (0)
   4.516 +
   4.517 +#define TAILQ_REPLACE(head, elm, elm2, field) do {			\
   4.518 +	if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)	\
   4.519 +		(elm2)->field.tqe_next->field.tqe_prev =		\
   4.520 +		    &(elm2)->field.tqe_next;				\
   4.521 +	else								\
   4.522 +		(head)->tqh_last = &(elm2)->field.tqe_next;		\
   4.523 +	(elm2)->field.tqe_prev = (elm)->field.tqe_prev;			\
   4.524 +	*(elm2)->field.tqe_prev = (elm2);				\
   4.525 +	_Q_INVALIDATE((elm)->field.tqe_prev);				\
   4.526 +	_Q_INVALIDATE((elm)->field.tqe_next);				\
   4.527 +} while (0)
   4.528 +
   4.529 +#define TAILQ_CONCAT(head1, head2, field) do {				\
   4.530 +	if (!TAILQ_EMPTY(head2)) {					\
   4.531 +		*(head1)->tqh_last = (head2)->tqh_first;		\
   4.532 +		(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;	\
   4.533 +		(head1)->tqh_last = (head2)->tqh_last;			\
   4.534 +		TAILQ_INIT((head2));					\
   4.535 +	}								\
   4.536 +} while (0)
   4.537 +
   4.538 +#endif	/* !_SYS_QUEUE_H_ */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/pager.c	Sat Aug 12 10:41:52 2017 +0200
     5.3 @@ -0,0 +1,400 @@
     5.4 +/*
     5.5 + * Copyright (C) 2017 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 "compat.h"
    5.28 +
    5.29 +#ifdef	HAVE_ERR_H
    5.30 +#include <err.h>
    5.31 +#endif /* HAVE_ERR_H */
    5.32 +#include <errno.h>
    5.33 +#include <fcntl.h>
    5.34 +#include <limits.h>
    5.35 +#include <signal.h>
    5.36 +#include <stdio.h>
    5.37 +#include <string.h>
    5.38 +#include <sys/ioctl.h>
    5.39 +#ifdef	HAVE_SYS_QUEUE_H
    5.40 +#include <sys/queue.h>
    5.41 +#endif /* HAVE_SYS_QUEUE_H */
    5.42 +#include <sys/stat.h>
    5.43 +#include <sys/types.h>
    5.44 +#include <termios.h>
    5.45 +#include <unistd.h>
    5.46 +#include <wchar.h>
    5.47 +
    5.48 +#include "pwm.h"
    5.49 +#include "pager.h"
    5.50 +#include "util.h"
    5.51 +
    5.52 +#ifndef	TCSASOFT
    5.53 +#define	TCSASOFT 0
    5.54 +#endif /* !TCSASOFT */
    5.55 +
    5.56 +#ifndef	_NSIG
    5.57 +#ifdef NSIG
    5.58 +#define	_NSIG NSIG
    5.59 +#else /* NSIG */
    5.60 +#define	_NSIG 128
    5.61 +#endif /* NSIG */
    5.62 +#endif /* !_NSIG */
    5.63 +
    5.64 +#ifndef	_PATH_TTY
    5.65 +#define	_PATH_TTY	"/dev/tty"
    5.66 +#endif
    5.67 +
    5.68 +struct pager {
    5.69 +	TAILQ_HEAD(lines_head, lines_entry) lines_head;
    5.70 +	FILE		*fp;
    5.71 +	size_t		buf_size;
    5.72 +	char		*buf;
    5.73 +};
    5.74 +
    5.75 +struct lines_entry {
    5.76 +	TAILQ_ENTRY(lines_entry) entry;
    5.77 +	char		*line;
    5.78 +};
    5.79 +
    5.80 +static volatile sig_atomic_t signals[_NSIG];
    5.81 +
    5.82 +static void
    5.83 +sig_handler(int signo)
    5.84 +{
    5.85 +	signals[signo] = 1;
    5.86 +}
    5.87 +
    5.88 +static int
    5.89 +getch_prompt(const char *prompt)
    5.90 +{
    5.91 +	int		retval = -1;
    5.92 +	int		signo;
    5.93 +	int		fd;
    5.94 +	struct termios	saved_term;
    5.95 +	struct termios	term;
    5.96 +	struct sigaction sa;
    5.97 +	struct sigaction saved_sa_hup;
    5.98 +	struct sigaction saved_sa_int;
    5.99 +	struct sigaction saved_sa_quit;
   5.100 +	struct sigaction saved_sa_pipe;
   5.101 +	struct sigaction saved_sa_alrm;
   5.102 +	struct sigaction saved_sa_term;
   5.103 +	struct sigaction saved_sa_tstp;
   5.104 +	struct sigaction saved_sa_ttin;
   5.105 +	struct sigaction saved_sa_ttou;
   5.106 +	char		c;
   5.107 +	int		need_restart = 0;
   5.108 +
   5.109 +	printf("%s", prompt);
   5.110 +	fflush(stdout);
   5.111 +
   5.112 +restart:
   5.113 +	for (signo = 0; signo < _NSIG; signo++) {
   5.114 +		signals[signo] = 0;
   5.115 +	}
   5.116 +
   5.117 +	fd = open(_PATH_TTY, O_RDONLY);
   5.118 +	if (fd < 0) {
   5.119 +		return (-1);
   5.120 +	}
   5.121 +
   5.122 +	if (tcgetattr(fd, &saved_term) != 0) {
   5.123 +		close(fd);
   5.124 +		return (-1);
   5.125 +	}
   5.126 +	memcpy(&term, &saved_term, sizeof (struct termios));
   5.127 +
   5.128 +	/* enter raw mode and turn echo off */
   5.129 +	term.c_lflag &= ~(ICANON | ECHO);
   5.130 +	while ((tcsetattr(fd, TCSAFLUSH | TCSASOFT, &term) < 0) &&
   5.131 +	    (errno != EINTR)) {
   5.132 +		continue;
   5.133 +	}
   5.134 +
   5.135 +	/* install temporary signal handler */
   5.136 +	sigemptyset(&sa.sa_mask);
   5.137 +	sa.sa_flags = 0;
   5.138 +	sa.sa_handler = sig_handler;
   5.139 +	sigaction(SIGHUP, &sa, &saved_sa_hup);
   5.140 +	sigaction(SIGINT, &sa, &saved_sa_int);
   5.141 +	sigaction(SIGQUIT, &sa, &saved_sa_quit);
   5.142 +	sigaction(SIGPIPE, &sa, &saved_sa_pipe);
   5.143 +	sigaction(SIGALRM, &sa, &saved_sa_alrm);
   5.144 +	sigaction(SIGTERM, &sa, &saved_sa_term);
   5.145 +	sigaction(SIGTSTP, &sa, &saved_sa_tstp);
   5.146 +	sigaction(SIGTTIN, &sa, &saved_sa_ttin);
   5.147 +	sigaction(SIGTTOU, &sa, &saved_sa_ttou);
   5.148 +
   5.149 +	/* read key */
   5.150 +	if (read(fd, &c, 1) == sizeof (c)) {
   5.151 +		retval = c;
   5.152 +	}
   5.153 +
   5.154 +	/* restore previous mode and echo setting */
   5.155 +	do {
   5.156 +		while ((tcsetattr(fd, TCSAFLUSH | TCSASOFT,
   5.157 +		    &saved_term) < 0) && (errno != EINTR)) {
   5.158 +			continue;
   5.159 +		}
   5.160 +	} while ((tcgetattr(fd, &term) == 0) &&
   5.161 +	    (term.c_lflag != saved_term.c_lflag));
   5.162 +
   5.163 +	/* restore previous signal handlers */
   5.164 +	sigaction(SIGHUP, &saved_sa_hup, &sa);
   5.165 +	sigaction(SIGINT, &saved_sa_int, &sa);
   5.166 +	sigaction(SIGQUIT, &saved_sa_quit, &sa);
   5.167 +	sigaction(SIGPIPE, &saved_sa_pipe, &sa);
   5.168 +	sigaction(SIGALRM, &saved_sa_alrm, &sa);
   5.169 +	sigaction(SIGTERM, &saved_sa_term, &sa);
   5.170 +	sigaction(SIGTSTP, &saved_sa_tstp, &sa);
   5.171 +	sigaction(SIGTTIN, &saved_sa_ttin, &sa);
   5.172 +	sigaction(SIGTTOU, &saved_sa_ttou, &sa);
   5.173 +
   5.174 +	/* erase prompt */
   5.175 +	printf("\r%*s\r", (int)strlen(prompt), "");
   5.176 +	fflush(stdout);
   5.177 +
   5.178 +	while ((close(fd) < 0) && (errno != EINTR)) {
   5.179 +		continue;
   5.180 +	}
   5.181 +
   5.182 +	/* re-raise signals received */
   5.183 +	for (signo = 0; signo < _NSIG; signo++) {
   5.184 +		if (signals[signo]) {
   5.185 +			raise(signo);
   5.186 +			if ((signo == SIGTSTP) || (signo == SIGTTIN) ||
   5.187 +			    (signo == SIGTTOU)) {
   5.188 +				need_restart = 1;
   5.189 +			}
   5.190 +		}
   5.191 +	}
   5.192 +	if (need_restart) {
   5.193 +		goto restart;
   5.194 +	}
   5.195 +
   5.196 +	return (retval);
   5.197 +}
   5.198 +
   5.199 +struct pager *
   5.200 +pager_create(FILE *fp)
   5.201 +{
   5.202 +	struct pager	*pager;
   5.203 +
   5.204 +	pager = xmalloc(sizeof (struct pager));
   5.205 +	TAILQ_INIT(&pager->lines_head);
   5.206 +	pager->fp = fp;
   5.207 +	pager->buf_size = BUFSIZ;
   5.208 +	pager->buf = xmalloc(BUFSIZ);
   5.209 +
   5.210 +	return (pager);
   5.211 +}
   5.212 +
   5.213 +void
   5.214 +pager_destroy(struct pager *pager)
   5.215 +{
   5.216 +	struct lines_entry *entry;
   5.217 +	struct lines_entry *entry_tmp;
   5.218 +
   5.219 +	if (pager == NULL) {
   5.220 +		return;
   5.221 +	}
   5.222 +
   5.223 +	TAILQ_FOREACH_SAFE(entry, &pager->lines_head, entry, entry_tmp) {
   5.224 +		TAILQ_REMOVE(&pager->lines_head, entry, entry);
   5.225 +		free(entry->line);
   5.226 +		free(entry);
   5.227 +	}
   5.228 +	free(pager->buf);
   5.229 +	free(pager);
   5.230 +}
   5.231 +
   5.232 +int
   5.233 +pager_vprintf(struct pager *pager, const char *fmt, va_list args)
   5.234 +{
   5.235 +	int		len;
   5.236 +	va_list		args_new;
   5.237 +	char		*p;
   5.238 +	size_t		line_len;
   5.239 +	struct lines_entry *entry;
   5.240 +	size_t		line_offset;
   5.241 +
   5.242 +	va_copy(args_new, args);
   5.243 +
   5.244 +	/* format multibyte string in buffer */
   5.245 +	len = vsnprintf(NULL, 0, fmt, args);
   5.246 +	if (len < 0) {
   5.247 +		err(1, "vsnprintf");
   5.248 +	}
   5.249 +
   5.250 +	if (pager->buf_size < (size_t)len + 1) {
   5.251 +		pager->buf_size = len + 1;
   5.252 +		pager->buf = xrealloc(pager->buf, pager->buf_size);
   5.253 +	}
   5.254 +
   5.255 +	len = vsnprintf(pager->buf, pager->buf_size, fmt, args_new);
   5.256 +	if (len < 0) {
   5.257 +		err(1, "vsnprintf");
   5.258 +	}
   5.259 +
   5.260 +	/* split buffer into lines */
   5.261 +	for (p = pager->buf; *p != '\0'; p += line_len) {
   5.262 +		line_len = strcspn(p, "\n");
   5.263 +		if (p[line_len] == '\n') {
   5.264 +			line_len++;
   5.265 +		}
   5.266 +
   5.267 +		entry = TAILQ_LAST(&pager->lines_head, lines_head);
   5.268 +		line_offset = (entry != NULL) ? strlen(entry->line) : 0;
   5.269 +		if ((entry != NULL) && (entry->line[line_offset - 1] != '\n')) {
   5.270 +			/*
   5.271 +			 * append to the last line if it doesn't end with a
   5.272 +			 * newline
   5.273 +			 */
   5.274 +			entry->line = xrealloc(entry->line, line_offset +
   5.275 +			    line_len + 1);
   5.276 +			memcpy(entry->line + line_offset, p, line_len);
   5.277 +			entry->line[line_offset + line_len] = '\0';
   5.278 +		} else {
   5.279 +			entry = xmalloc(sizeof (struct lines_entry));
   5.280 +			entry->line = xmalloc(line_len + 1);
   5.281 +			memcpy(entry->line, p, line_len);
   5.282 +			entry->line[line_len] = '\0';
   5.283 +			TAILQ_INSERT_TAIL(&pager->lines_head, entry, entry);
   5.284 +		}
   5.285 +	}
   5.286 +
   5.287 +	va_end(args_new);
   5.288 +
   5.289 +	return (len);
   5.290 +}
   5.291 +
   5.292 +int
   5.293 +pager_printf(struct pager *pager, const char *fmt, ...)
   5.294 +{
   5.295 +	int	len;
   5.296 +	va_list	args;
   5.297 +
   5.298 +	va_start(args, fmt);
   5.299 +	len = pager_vprintf(pager, fmt, args);
   5.300 +	va_end(args);
   5.301 +
   5.302 +	return (len);
   5.303 +}
   5.304 +
   5.305 +static unsigned int
   5.306 +mbsnprint(unsigned int cols, const char *s, FILE *fp)
   5.307 +{
   5.308 +	const char	*p = s;
   5.309 +	unsigned int	col = 0;
   5.310 +	int		mb_len;
   5.311 +	wchar_t		wc;
   5.312 +	int		width;
   5.313 +	char		mb_buf[MB_LEN_MAX];
   5.314 +
   5.315 +	while ((*p != '\n') && (*p != '\0') && (col < cols)) {
   5.316 +		mb_len = mbtowc(&wc, p, MB_CUR_MAX);
   5.317 +		if (mb_len != -1) {
   5.318 +			width = wcwidth(wc);
   5.319 +			if (width != -1) {
   5.320 +				if (snprintf(mb_buf, sizeof (mb_buf), "%.*s",
   5.321 +				    mb_len, p) != mb_len) {
   5.322 +					err(1, "snprintf");
   5.323 +				}
   5.324 +			} else {
   5.325 +				/*
   5.326 +				 * non-printable character, print
   5.327 +				 * replacement character
   5.328 +				 */
   5.329 +				width = 1;
   5.330 +				if (snprintf(mb_buf, sizeof (mb_buf),
   5.331 +				    "\357\277\275") != 3) {
   5.332 +					err(1, "snprintf");
   5.333 +				}
   5.334 +			}
   5.335 +		} else {
   5.336 +			/*
   5.337 +			 * decoding failed, reset state and skip one
   5.338 +			 * byte
   5.339 +			 */
   5.340 +			mbtowc(NULL, NULL, MB_CUR_MAX);
   5.341 +			mb_len = 1;
   5.342 +
   5.343 +			/* print replacement character */
   5.344 +			width = 1;
   5.345 +			if (snprintf(mb_buf, sizeof (mb_buf),
   5.346 +			    "\357\277\275") != 3) {
   5.347 +				err(1, "snprintf");
   5.348 +			}
   5.349 +		}
   5.350 +
   5.351 +		p += mb_len;
   5.352 +		col += width;
   5.353 +		if (col <= cols) {
   5.354 +			if (fputs(mb_buf, fp) == EOF) {
   5.355 +				err(1, "fputs");
   5.356 +			}
   5.357 +		}
   5.358 +	}
   5.359 +
   5.360 +	fputc('\n', fp);
   5.361 +	fflush(fp);
   5.362 +
   5.363 +	return (col);
   5.364 +}
   5.365 +
   5.366 +void
   5.367 +pager_show(struct pager *pager)
   5.368 +{
   5.369 +	int		is_interactive;
   5.370 +	unsigned int	rows = 24;
   5.371 +	unsigned int	cols = 80;
   5.372 +#ifdef	TIOCGWINSZ
   5.373 +	struct winsize	ws;
   5.374 +#endif /* TIOCGWINSZ */
   5.375 +	unsigned int	row = 0;
   5.376 +	struct lines_entry *entry;
   5.377 +
   5.378 +	is_interactive = (isatty(STDIN_FILENO) && (pager->fp == stdout));
   5.379 +
   5.380 +#ifdef	TIOCGWINSZ
   5.381 +	if (is_interactive) {
   5.382 +		/* determine terminal size */
   5.383 +		if (ioctl(fileno(pager->fp), TIOCGWINSZ, &ws) == 0) {
   5.384 +			rows = (ws.ws_row > 0) ? ws.ws_row : rows;
   5.385 +			cols = (ws.ws_col > 0) ? ws.ws_col : cols;
   5.386 +		}
   5.387 +	}
   5.388 +#endif /* TIOCGWINSZ */
   5.389 +
   5.390 +	TAILQ_FOREACH(entry, &pager->lines_head, entry) {
   5.391 +		if (is_interactive) {
   5.392 +			mbsnprint(cols, entry->line, pager->fp);
   5.393 +			row++;
   5.394 +			if (row == rows - 1) {
   5.395 +				getch_prompt("--More--");
   5.396 +				row = 0;
   5.397 +			}
   5.398 +		} else {
   5.399 +			fprintf(pager->fp, "%s", entry->line);
   5.400 +			fflush(pager->fp);
   5.401 +		}
   5.402 +	}
   5.403 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/pager.h	Sat Aug 12 10:41:52 2017 +0200
     6.3 @@ -0,0 +1,38 @@
     6.4 +/*
     6.5 + * Copyright (C) 2017 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 +
    6.28 +#ifndef	PAGER_H
    6.29 +#define	PAGER_H
    6.30 +
    6.31 +#include <stdarg.h>
    6.32 +
    6.33 +struct pager;
    6.34 +
    6.35 +struct pager *	pager_create(FILE *fp);
    6.36 +void		pager_destroy(struct pager *);
    6.37 +int		pager_vprintf(struct pager *, const char *, va_list);
    6.38 +int		pager_printf(struct pager *, const char *, ...);
    6.39 +void		pager_show(struct pager *);
    6.40 +
    6.41 +#endif /* !PAGER_H */
     7.1 --- a/pwm.1.xml	Tue Aug 08 10:47:04 2017 +0200
     7.2 +++ b/pwm.1.xml	Sat Aug 12 10:41:52 2017 +0200
     7.3 @@ -34,7 +34,7 @@
     7.4        <email>guido+pwm@berhoerster.name</email>
     7.5        <personblurb/>
     7.6      </author>
     7.7 -    <date>8 August, 2017</date>
     7.8 +    <date>12 August, 2017</date>
     7.9    </info>
    7.10    <refmeta>
    7.11      <refentrytitle>pwm</refentrytitle>
    7.12 @@ -85,6 +85,9 @@
    7.13        more space characters and the field's verbatim content to the standard
    7.14        output stream. Field content may contain newlines, non-printable and/or
    7.15        control characters.</para>
    7.16 +      <para>If running in interactive mode, the <command>list</command>,
    7.17 +      <command>show</command> and <command>info</command> will display
    7.18 +      the results on a page-by-page basis using an internal pager.</para>
    7.19        <para>The <command>pipe</command> prints the verbatim field content to the
    7.20        standard input stream of the given command.</para>
    7.21        <para>Error messages are printed to the standard error stream.</para>