Mercurial > projects > pwm
view pager.c @ 21:ee4d36c85287
Make EOF in interactive mode equivalent to the q command
author | Guido Berhoerster <guido+pwm@berhoerster.name> |
---|---|
date | Wed, 06 Sep 2017 16:41:58 +0200 |
parents | 5c6155c8e9b6 |
children |
line wrap: on
line source
/* * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name> * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "compat.h" #ifdef HAVE_ERR_H #include <err.h> #endif /* HAVE_ERR_H */ #include <errno.h> #include <fcntl.h> #include <limits.h> #include <signal.h> #include <stdio.h> #include <string.h> #include <sys/ioctl.h> #ifdef HAVE_SYS_QUEUE_H #include <sys/queue.h> #endif /* HAVE_SYS_QUEUE_H */ #include <sys/stat.h> #include <sys/types.h> #include <termios.h> #include <unistd.h> #include <wchar.h> #include "pwm.h" #include "pager.h" #include "util.h" struct pager { TAILQ_HEAD(lines_head, lines_entry) lines_head; int fd; size_t buf_size; char *buf; }; struct lines_entry { TAILQ_ENTRY(lines_entry) entry; char *line; }; struct pager * pager_create(int fd) { struct pager *pager; pager = xmalloc(sizeof (struct pager)); TAILQ_INIT(&pager->lines_head); pager->fd = fd; pager->buf_size = BUFSIZ; pager->buf = xmalloc(BUFSIZ); return (pager); } void pager_destroy(struct pager *pager) { struct lines_entry *entry; struct lines_entry *entry_tmp; if (pager == NULL) { return; } TAILQ_FOREACH_SAFE(entry, &pager->lines_head, entry, entry_tmp) { TAILQ_REMOVE(&pager->lines_head, entry, entry); free(entry->line); free(entry); } free(pager->buf); free(pager); } int pager_vprintf(struct pager *pager, const char *fmt, va_list args) { int len; va_list args_new; char *p; size_t line_len; struct lines_entry *entry; size_t line_offset; va_copy(args_new, args); /* format multibyte string in buffer */ len = vsnprintf(NULL, 0, fmt, args); if (len < 0) { err(1, "vsnprintf"); } if (pager->buf_size < (size_t)len + 1) { pager->buf_size = len + 1; pager->buf = xrealloc(pager->buf, pager->buf_size); } len = vsnprintf(pager->buf, pager->buf_size, fmt, args_new); if (len < 0) { err(1, "vsnprintf"); } /* split buffer into lines */ for (p = pager->buf; *p != '\0'; p += line_len) { line_len = strcspn(p, "\n"); if (p[line_len] == '\n') { line_len++; } entry = TAILQ_LAST(&pager->lines_head, lines_head); line_offset = (entry != NULL) ? strlen(entry->line) : 0; if ((entry != NULL) && (entry->line[line_offset - 1] != '\n')) { /* * append to the last line if it doesn't end with a * newline */ entry->line = xrealloc(entry->line, line_offset + line_len + 1); memcpy(entry->line + line_offset, p, line_len); entry->line[line_offset + line_len] = '\0'; } else { entry = xmalloc(sizeof (struct lines_entry)); entry->line = xmalloc(line_len + 1); memcpy(entry->line, p, line_len); entry->line[line_len] = '\0'; TAILQ_INSERT_TAIL(&pager->lines_head, entry, entry); } } va_end(args_new); return (len); } int pager_printf(struct pager *pager, const char *fmt, ...) { int len; va_list args; va_start(args, fmt); len = pager_vprintf(pager, fmt, args); va_end(args); return (len); } static int mbsnprint(unsigned int cols, const char *s, int fd) { int retval; const char *p = s; unsigned int col = 0; int mb_len; wchar_t wc; int width; char mb_buf[MB_LEN_MAX]; while ((*p != '\n') && (*p != '\0') && (col < cols)) { mb_len = mbtowc(&wc, p, MB_CUR_MAX); if (mb_len != -1) { width = wcwidth(wc); if (width != -1) { if (snprintf(mb_buf, sizeof (mb_buf), "%.*s", mb_len, p) != mb_len) { err(1, "snprintf"); } } else { /* * non-printable character, print * replacement character */ width = 1; if (snprintf(mb_buf, sizeof (mb_buf), "\357\277\275") != 3) { err(1, "snprintf"); } } } else { /* * decoding failed, reset state and skip one * byte */ mbtowc(NULL, NULL, MB_CUR_MAX); mb_len = 1; /* print replacement character */ width = 1; if (snprintf(mb_buf, sizeof (mb_buf), "\357\277\275") != 3) { err(1, "snprintf"); } } p += mb_len; col += width; if (col <= cols) { retval = io_dputs(fd, mb_buf); if (retval != IO_OK) { return (retval); } } } retval = io_dputs(fd, "\n"); return (retval); } enum io_status pager_show(struct pager *pager) { int retval = IO_OK; int is_interactive; unsigned int rows = 24; unsigned int cols = 80; #ifdef TIOCGWINSZ struct winsize ws; #endif /* TIOCGWINSZ */ unsigned int row = 0; struct lines_entry *entry; int c; is_interactive = (isatty(STDIN_FILENO) && (pager->fd == STDOUT_FILENO)); #ifdef TIOCGWINSZ if (is_interactive) { /* determine terminal size */ if (ioctl(pager->fd, TIOCGWINSZ, &ws) == 0) { rows = (ws.ws_row > 0) ? ws.ws_row : rows; cols = (ws.ws_col > 0) ? ws.ws_col : cols; } } #endif /* TIOCGWINSZ */ TAILQ_FOREACH(entry, &pager->lines_head, entry) { if (is_interactive) { retval = mbsnprint(cols, entry->line, pager->fd); if (retval != IO_OK) { goto out; } row++; if ((TAILQ_NEXT(entry, entry) != NULL) && (row == rows - 1)) { /* prompt for keypress */ retval = io_get_char("--More--", &c); if (retval != IO_OK) { goto out; } row = 0; } } else { retval = io_dputs(pager->fd, entry->line); if (retval != IO_OK) { goto out; } } } out: return (retval); }