tui_txt.c - sacc - sacc(omys), simple console gopher client (mirror)
HTML git clone https://git.parazyd.org/sacc
DIR Log
DIR Files
DIR Refs
DIR LICENSE
---
tui_txt.c (7156B)
---
1 #include <ctype.h>
2 #include <errno.h>
3 #include <stdarg.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <termios.h>
8 #include <sys/ioctl.h>
9 #include <sys/types.h>
10
11 #include "common.h"
12
13 static char bufout[256];
14 static Item *curentry;
15 static char cmd;
16 int lines, columns;
17
18 static void
19 viewsize(int *ln, int *col)
20 {
21 struct winsize ws;
22
23 if (ioctl(1, TIOCGWINSZ, &ws) < 0) {
24 die("Could not get terminal resolution: %s",
25 strerror(errno));
26 }
27
28 if (ln)
29 *ln = ws.ws_row-1; /* one off for status bar */
30 if (col)
31 *col = ws.ws_col;
32 }
33
34 void
35 uisetup(void)
36 {
37 viewsize(&lines, &columns);
38 }
39
40 void
41 uicleanup(void)
42 {
43 return;
44 }
45
46 void
47 help(void)
48 {
49 puts("Commands:\n"
50 "#: browse item number #.\n"
51 "U: print page URI.\n"
52 "u#: print item number # URI.\n"
53 "0: browse previous item.\n"
54 "n: show next page.\n"
55 "p: show previous page.\n"
56 "t: go to the top of the page\n"
57 "b: go to the bottom of the page\n"
58 "/str: search for string \"str\"\n"
59 "!: refetch failed item.\n"
60 "^D, q: quit.\n"
61 "h, ?: this help.");
62 }
63
64 static int
65 ndigits(size_t n)
66 {
67 return (n < 10) ? 1 : (n < 100) ? 2 : 3;
68 }
69
70 void
71 uistatus(char *fmt, ...)
72 {
73 va_list arg;
74 int n;
75
76 va_start(arg, fmt);
77 n = vsnprintf(bufout, sizeof(bufout), fmt, arg);
78 va_end(arg);
79
80 if (n < sizeof(bufout)-1) {
81 n += snprintf(bufout + n, sizeof(bufout) - n,
82 " [Press Enter to continue \xe2\x98\x83]");
83 }
84 if (n >= sizeof(bufout))
85 bufout[sizeof(bufout)-1] = '\0';
86
87 mbsprint(bufout, columns);
88 fflush(stdout);
89
90 getchar();
91 }
92
93 static void
94 printstatus(Item *item, char c)
95 {
96 Dir *dir = item->dat;
97 char *fmt;
98 size_t nitems = dir ? dir->nitems : 0;
99 unsigned long long printoff = dir ? dir->printoff : 0;
100
101 fmt = (strcmp(item->port, "70") && strcmp(item->port, "gopher")) ?
102 "%1$3lld%%%*2$3$c %4$s:%8$s/%5$c%6$s [%7$c]: " :
103 "%3lld%%%*c %s/%c%s [%c]: ";
104 if (snprintf(bufout, sizeof(bufout), fmt,
105 (printoff + lines-1 >= nitems) ? 100 :
106 (printoff + lines) * 100 / nitems, ndigits(nitems)+2, '|',
107 item->host, item->type, item->selector, c, item->port)
108 >= sizeof(bufout))
109 bufout[sizeof(bufout)-1] = '\0';
110 mbsprint(bufout, columns);
111 }
112
113 char *
114 uiprompt(char *fmt, ...)
115 {
116 va_list ap;
117 char *input = NULL;
118 size_t n = 0;
119 ssize_t r;
120
121 va_start(ap, fmt);
122 if (vsnprintf(bufout, sizeof(bufout), fmt, ap) >= sizeof(bufout))
123 bufout[sizeof(bufout)-1] = '\0';
124 va_end(ap);
125
126 mbsprint(bufout, columns);
127 fflush(stdout);
128
129 if ((r = getline(&input, &n, stdin)) < 0) {
130 clearerr(stdin);
131 clear(&input);
132 putchar('\n');
133 } else if (input[r - 1] == '\n') {
134 input[--r] = '\0';
135 }
136
137 return input;
138 }
139
140 void
141 uidisplay(Item *entry)
142 {
143 Item *items;
144 Dir *dir;
145 size_t i, nlines, nitems;
146 int nd;
147
148 if (!entry ||
149 !(entry->type == '1' || entry->type == '+' || entry->type == '7') ||
150 !(dir = entry->dat))
151 return;
152
153 curentry = entry;
154
155 items = dir->items;
156 nitems = dir->nitems;
157 nlines = dir->printoff + lines;
158 nd = ndigits(nitems);
159
160 for (i = dir->printoff; i < nitems && i < nlines; ++i) {
161 if (snprintf(bufout, sizeof(bufout), "%*zu %s %s",
162 nd, i+1, typedisplay(items[i].type),
163 items[i].username)
164 >= sizeof(bufout))
165 bufout[sizeof(bufout)-1] = '\0';
166 mbsprint(bufout, columns);
167 putchar('\n');
168 }
169
170 fflush(stdout);
171 }
172
173 void
174 printuri(Item *item, size_t i)
175 {
176 int n;
177
178 if (!item)
179 return;
180
181 switch (item->type) {
182 case 0:
183 return;
184 case '8':
185 n = snprintf(bufout, sizeof(bufout), "telnet://%s@%s:%s",
186 item->selector, item->host, item->port);
187 break;
188 case 'i':
189 n = snprintf(bufout, sizeof(bufout), "%zu: %s",
190 i, item->username);
191 break;
192 case 'h':
193 n = snprintf(bufout, sizeof(bufout), "%zu: %s: %s",
194 i, item->username, item->selector);
195 break;
196 case 'T':
197 n = snprintf(bufout, sizeof(bufout), "tn3270://%s@%s:%s",
198 item->selector, item->host, item->port);
199 break;
200 default:
201 n = snprintf(bufout, sizeof(bufout), "%zu: ", i);
202
203 if (n < sizeof(bufout) && *item->username) {
204 n += snprintf(bufout+n, sizeof(bufout)-n, "%s: ",
205 item->username);
206 }
207 if (n < sizeof(bufout)) {
208 n += snprintf(bufout+n, sizeof(bufout)-n, "gopher://%s",
209 item->host);
210 }
211 if (n < sizeof(bufout) && strcmp(item->port, "70")) {
212 n += snprintf(bufout+n, sizeof(bufout)-n, ":%s",
213 item->port);
214 }
215 if (n < sizeof(bufout)) {
216 n += snprintf(bufout+n, sizeof(bufout)-n, "/%c%s",
217 item->type, item->selector);
218 }
219 if (n < sizeof(bufout) && item->type == '7' && item->tag) {
220 n += snprintf(bufout+n, sizeof(bufout)-n, "%%09%s",
221 item->tag + strlen(item->selector));
222 }
223 break;
224 }
225
226 if (n >= sizeof(bufout))
227 bufout[sizeof(bufout)-1] = '\0';
228
229 mbsprint(bufout, columns);
230 putchar('\n');
231 }
232
233 void
234 searchinline(const char *searchstr, Item *entry)
235 {
236 Dir *dir;
237 size_t i;
238
239 if (!searchstr || !*searchstr || !(dir = entry->dat))
240 return;
241
242 for (i = 0; i < dir->nitems; ++i)
243 if (strcasestr(dir->items[i].username, searchstr))
244 printuri(&(dir->items[i]), i + 1);
245 }
246
247 Item *
248 uiselectitem(Item *entry)
249 {
250 Dir *dir;
251 char buf[BUFSIZ], *sstr, nl;
252 int item, nitems;
253
254 if (!entry || !(dir = entry->dat))
255 return NULL;
256
257 nitems = dir ? dir->nitems : 0;
258
259 for (;;) {
260 if (!cmd)
261 cmd = 'h';
262 printstatus(entry, cmd);
263 fflush(stdout);
264
265 if (!fgets(buf, sizeof(buf), stdin)) {
266 putchar('\n');
267 return NULL;
268 }
269 if (isdigit((unsigned char)*buf)) {
270 cmd = '\0';
271 nl = '\0';
272 if (sscanf(buf, "%d%c", &item, &nl) != 2 || nl != '\n')
273 item = -1;
274 } else if (!strcmp(buf+1, "\n")) {
275 item = -1;
276 cmd = *buf;
277 } else if (*buf == '/') {
278 for (sstr = buf+1; *sstr && *sstr != '\n'; ++sstr)
279 ;
280 *sstr = '\0';
281 sstr = buf+1;
282 cmd = *buf;
283 } else if (isdigit((unsigned char)*(buf+1))) {
284 nl = '\0';
285 if (sscanf(buf+1, "%d%c", &item, &nl) != 2 || nl != '\n')
286 item = -1;
287 else
288 cmd = *buf;
289 }
290
291 switch (cmd) {
292 case '\0':
293 break;
294 case 'q':
295 return NULL;
296 case 'n':
297 if (lines < nitems - dir->printoff &&
298 lines < (size_t)-1 - dir->printoff)
299 dir->printoff += lines;
300 return entry;
301 case 'p':
302 if (lines <= dir->printoff)
303 dir->printoff -= lines;
304 else
305 dir->printoff = 0;
306 return entry;
307 case 'b':
308 if (nitems > lines)
309 dir->printoff = nitems - lines;
310 else
311 dir->printoff = 0;
312 return entry;
313 case 't':
314 dir->printoff = 0;
315 return entry;
316 case '!':
317 if (entry->raw)
318 continue;
319 return entry;
320 case 'U':
321 printuri(entry, 0);
322 continue;
323 case 'u':
324 if (item > 0 && item <= nitems)
325 printuri(&dir->items[item-1], item);
326 continue;
327 case '/':
328 if (*sstr)
329 searchinline(sstr, entry);
330 continue;
331 case 'h':
332 case '?':
333 help();
334 continue;
335 default:
336 cmd = 'h';
337 continue;
338 }
339
340 if (item >= 0 && item <= nitems)
341 break;
342 }
343
344 if (item > 0)
345 return &dir->items[item-1];
346
347 return entry->entry;
348 }
349
350 void
351 uisigwinch(int signal)
352 {
353 uisetup();
354
355 if (!curentry)
356 return;
357
358 putchar('\n');
359 uidisplay(curentry);
360 printstatus(curentry, cmd);
361 fflush(stdout);
362 }