projects/pwm

view pager.c @ 26:5bdea77d0c1d

Add pwm-clip utility for setting the X11 CLIPBOARD selection
author Guido Berhoerster <guido+pwm@berhoerster.name>
date Thu Sep 21 09:45:59 2017 +0200 (2017-09-21)
parents 1e39a251cbe9
children
line source
1 /*
2 * Copyright (C) 2017 Guido Berhoerster <guido+pwm@berhoerster.name>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
24 #include "compat.h"
26 #ifdef HAVE_ERR_H
27 #include <err.h>
28 #endif /* HAVE_ERR_H */
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <limits.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/ioctl.h>
36 #ifdef HAVE_SYS_QUEUE_H
37 #include <sys/queue.h>
38 #endif /* HAVE_SYS_QUEUE_H */
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <termios.h>
42 #include <unistd.h>
43 #include <wchar.h>
45 #include "pwm.h"
46 #include "pager.h"
47 #include "util.h"
49 struct pager {
50 TAILQ_HEAD(lines_head, lines_entry) lines_head;
51 int fd;
52 size_t buf_size;
53 char *buf;
54 };
56 struct lines_entry {
57 TAILQ_ENTRY(lines_entry) entry;
58 char *line;
59 };
61 struct pager *
62 pager_create(int fd)
63 {
64 struct pager *pager;
66 pager = xmalloc(sizeof (struct pager));
67 TAILQ_INIT(&pager->lines_head);
68 pager->fd = fd;
69 pager->buf_size = BUFSIZ;
70 pager->buf = xmalloc(BUFSIZ);
72 return (pager);
73 }
75 void
76 pager_destroy(struct pager *pager)
77 {
78 struct lines_entry *entry;
79 struct lines_entry *entry_tmp;
81 if (pager == NULL) {
82 return;
83 }
85 TAILQ_FOREACH_SAFE(entry, &pager->lines_head, entry, entry_tmp) {
86 TAILQ_REMOVE(&pager->lines_head, entry, entry);
87 free(entry->line);
88 free(entry);
89 }
90 free(pager->buf);
91 free(pager);
92 }
94 int
95 pager_vprintf(struct pager *pager, const char *fmt, va_list args)
96 {
97 int len;
98 va_list args_new;
99 char *p;
100 size_t line_len;
101 struct lines_entry *entry;
102 size_t line_offset;
104 va_copy(args_new, args);
106 /* format multibyte string in buffer */
107 len = vsnprintf(NULL, 0, fmt, args);
108 if (len < 0) {
109 err(1, "vsnprintf");
110 }
112 if (pager->buf_size < (size_t)len + 1) {
113 pager->buf_size = len + 1;
114 pager->buf = xrealloc(pager->buf, pager->buf_size);
115 }
117 len = vsnprintf(pager->buf, pager->buf_size, fmt, args_new);
118 if (len < 0) {
119 err(1, "vsnprintf");
120 }
122 /* split buffer into lines */
123 for (p = pager->buf; *p != '\0'; p += line_len) {
124 line_len = strcspn(p, "\n");
125 if (p[line_len] == '\n') {
126 line_len++;
127 }
129 entry = TAILQ_LAST(&pager->lines_head, lines_head);
130 line_offset = (entry != NULL) ? strlen(entry->line) : 0;
131 if ((entry != NULL) && (entry->line[line_offset - 1] != '\n')) {
132 /*
133 * append to the last line if it doesn't end with a
134 * newline
135 */
136 entry->line = xrealloc(entry->line, line_offset +
137 line_len + 1);
138 memcpy(entry->line + line_offset, p, line_len);
139 entry->line[line_offset + line_len] = '\0';
140 } else {
141 entry = xmalloc(sizeof (struct lines_entry));
142 entry->line = xmalloc(line_len + 1);
143 memcpy(entry->line, p, line_len);
144 entry->line[line_len] = '\0';
145 TAILQ_INSERT_TAIL(&pager->lines_head, entry, entry);
146 }
147 }
149 va_end(args_new);
151 return (len);
152 }
154 int
155 pager_printf(struct pager *pager, const char *fmt, ...)
156 {
157 int len;
158 va_list args;
160 va_start(args, fmt);
161 len = pager_vprintf(pager, fmt, args);
162 va_end(args);
164 return (len);
165 }
167 static int
168 mbsnprint(unsigned int cols, const char *s, int fd)
169 {
170 int retval;
171 const char *p = s;
172 unsigned int col = 0;
173 int mb_len;
174 wchar_t wc;
175 int width;
176 char mb_buf[MB_LEN_MAX];
178 while ((*p != '\n') && (*p != '\0') && (col < cols)) {
179 mb_len = mbtowc(&wc, p, MB_CUR_MAX);
180 if (mb_len != -1) {
181 width = wcwidth(wc);
182 if (width != -1) {
183 if (snprintf(mb_buf, sizeof (mb_buf), "%.*s",
184 mb_len, p) != mb_len) {
185 err(1, "snprintf");
186 }
187 } else {
188 /*
189 * non-printable character, print
190 * replacement character
191 */
192 width = 1;
193 if (snprintf(mb_buf, sizeof (mb_buf),
194 "\357\277\275") != 3) {
195 err(1, "snprintf");
196 }
197 }
198 } else {
199 /*
200 * decoding failed, reset state and skip one
201 * byte
202 */
203 mbtowc(NULL, NULL, MB_CUR_MAX);
204 mb_len = 1;
206 /* print replacement character */
207 width = 1;
208 if (snprintf(mb_buf, sizeof (mb_buf),
209 "\357\277\275") != 3) {
210 err(1, "snprintf");
211 }
212 }
214 p += mb_len;
215 col += width;
216 if (col <= cols) {
217 retval = io_dputs(fd, mb_buf);
218 if (retval != IO_OK) {
219 return (retval);
220 }
221 }
222 }
224 retval = io_dputs(fd, "\n");
226 return (retval);
227 }
229 enum io_status
230 pager_show(struct pager *pager)
231 {
232 int retval = IO_OK;
233 int is_interactive;
234 unsigned int rows = 24;
235 unsigned int cols = 80;
236 #ifdef TIOCGWINSZ
237 struct winsize ws;
238 #endif /* TIOCGWINSZ */
239 unsigned int row = 0;
240 struct lines_entry *entry;
241 int c;
243 is_interactive = (isatty(STDIN_FILENO) && (pager->fd == STDOUT_FILENO));
245 #ifdef TIOCGWINSZ
246 if (is_interactive) {
247 /* determine terminal size */
248 if (ioctl(pager->fd, TIOCGWINSZ, &ws) == 0) {
249 rows = (ws.ws_row > 0) ? ws.ws_row : rows;
250 cols = (ws.ws_col > 0) ? ws.ws_col : cols;
251 }
252 }
253 #endif /* TIOCGWINSZ */
255 TAILQ_FOREACH(entry, &pager->lines_head, entry) {
256 if (is_interactive) {
257 retval = mbsnprint(cols, entry->line, pager->fd);
258 if (retval != IO_OK) {
259 goto out;
260 }
261 row++;
262 if ((TAILQ_NEXT(entry, entry) != NULL) &&
263 (row == rows - 1)) {
264 /* prompt for keypress */
265 retval = io_get_char("--More--", &c);
266 if (retval != IO_OK) {
267 goto out;
268 }
269 row = 0;
270 }
271 } else {
272 retval = io_dputs(pager->fd, entry->line);
273 if (retval != IO_OK) {
274 goto out;
275 }
276 }
277 }
279 out:
280 return (retval);
281 }