t* sacc + cursorline and uri preview
URI git clone git://git.codevoid.de/sacc-sdk
DIR Log
DIR Files
DIR Refs
DIR LICENSE
---
tui_ti.c (12954B)
---
1 #include <stdarg.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <term.h>
6 #include <termios.h>
7 #include <unistd.h>
8 #include <sys/types.h>
9
10 #include "config.h"
11 #include "common.h"
12
13 #define C(c) #c
14 #define S(c) C(c)
15
16 #define SELECTION "\x1B[31m"
17 #define RESET "\x1B[0m"
18
19 static char bufout[256];
20 static struct termios tsave;
21 static struct termios tsacc;
22 static Item *curentry;
23
24 void
25 uisetup(void)
26 {
27 tcgetattr(0, &tsave);
28 tsacc = tsave;
29 tsacc.c_lflag &= ~(ECHO|ICANON);
30 tcsetattr(0, TCSANOW, &tsacc);
31
32 setupterm(NULL, 1, NULL);
33 putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
34 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
35 putp(tparm(change_scroll_region, 0, lines-2, 0, 0, 0, 0, 0, 0, 0));
36 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
37 fflush(stdout);
38 }
39
40 void
41 uicleanup(void)
42 {
43 putp(tparm(change_scroll_region, 0, lines-1, 0, 0, 0, 0, 0, 0, 0));
44 putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
45 tcsetattr(0, TCSANOW, &tsave);
46 fflush(stdout);
47 }
48
49 char *
50 uiprompt(char *fmt, ...)
51 {
52 va_list ap;
53 char *input = NULL;
54 size_t n;
55 ssize_t r;
56
57 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
58
59 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
60 putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0));
61 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
62
63 va_start(ap, fmt);
64 if (vsnprintf(bufout, sizeof(bufout), fmt, ap) >= sizeof(bufout))
65 bufout[sizeof(bufout)-1] = '\0';
66 va_end(ap);
67 n = mbsprint(bufout, columns);
68
69 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
70 if (n < columns)
71 printf("%*s", (int)(columns - n), " ");
72
73 putp(tparm(cursor_address, lines-1, n, 0, 0, 0, 0, 0, 0, 0));
74
75 tsacc.c_lflag |= (ECHO|ICANON);
76 tcsetattr(0, TCSANOW, &tsacc);
77 fflush(stdout);
78
79 n = 0;
80 r = getline(&input, &n, stdin);
81
82 tsacc.c_lflag &= ~(ECHO|ICANON);
83 tcsetattr(0, TCSANOW, &tsacc);
84 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
85 fflush(stdout);
86
87 if (r < 0) {
88 clearerr(stdin);
89 clear(&input);
90 } else if (input[r - 1] == '\n') {
91 input[--r] = '\0';
92 }
93
94 return input;
95 }
96
97 static void
98 printitem(Item *item)
99 {
100 if (snprintf(bufout, sizeof(bufout), "%s %s", typedisplay(item->type),
101 item->username) >= sizeof(bufout))
102 bufout[sizeof(bufout)-1] = '\0';
103 mbsprint(bufout, columns);
104 putchar('\r');
105 }
106
107 static Item *
108 help(Item *entry)
109 {
110 static Item item = {
111 .type = '0',
112 .raw = "Commands:\n"
113 "Down, " S(_key_lndown) ": move one line down.\n"
114 "Up, " S(_key_lnup) ": move one line up.\n"
115 "PgDown, " S(_key_pgdown) ": move one page down.\n"
116 "PgUp, " S(_key_pgup) ": move one page up.\n"
117 "Home, " S(_key_home) ": move to top of the page.\n"
118 "End, " S(_key_end) ": move to end of the page.\n"
119 "Right, " S(_key_pgnext) ": view highlighted item.\n"
120 "Left, " S(_key_pgprev) ": view previous item.\n"
121 S(_key_search) ": search current page.\n"
122 S(_key_searchnext) ": search string forward.\n"
123 S(_key_searchprev) ": search string backward.\n"
124 S(_key_uri) ": print item uri.\n"
125 S(_key_help) ": show this help.\n"
126 "^D, " S(_key_quit) ": exit sacc.\n"
127 };
128
129 item.entry = entry;
130
131 return &item;
132 }
133
134 void
135 uistatus(char *fmt, ...)
136 {
137 va_list ap;
138 size_t n;
139
140 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
141
142 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
143 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
144
145 va_start(ap, fmt);
146 n = vsnprintf(bufout, sizeof(bufout), fmt, ap);
147 va_end(ap);
148
149 if (n < sizeof(bufout)-1) {
150 n += snprintf(bufout + n, sizeof(bufout) - n,
151 " [Press a key to continue \xe2\x98\x83]");
152 }
153 if (n >= sizeof(bufout))
154 bufout[sizeof(bufout)-1] = '\0';
155
156 n = mbsprint(bufout, columns);
157 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
158 if (n < columns)
159 printf("%*s", (int)(columns - n), " ");
160
161 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
162 fflush(stdout);
163
164 getchar();
165 }
166
167 static void
168 displaystatus(Item *item)
169 {
170 Dir *dir = item->dat;
171 char *fmt;
172 size_t n, nitems = dir ? dir->nitems : 0;
173 unsigned long long printoff = dir ? dir->printoff : 0;
174
175 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
176
177 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
178 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
179 fmt = (strcmp(item->port, "70") && strcmp(item->port, "gopher")) ?
180 "%1$3lld%%| %2$s:%5$s/%3$c%4$s" : "%3lld%%| %s/%c%s";
181 if (snprintf(bufout, sizeof(bufout), fmt,
182 (printoff + lines-1 >= nitems) ? 100 :
183 (printoff + lines-1) * 100 / nitems,
184 item->host, item->type, item->selector, item->port)
185 >= sizeof(bufout))
186 bufout[sizeof(bufout)-1] = '\0';
187 n = mbsprint(bufout, columns);
188 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
189 if (n < columns)
190 printf("%*s", (int)(columns - n), " ");
191
192 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
193 fflush(stdout);
194 }
195
196 static void
197 displayuri(Item *item)
198 {
199 char *fmt;
200 size_t n;
201
202 if (item->type == 0 || item->type == 'i')
203 return;
204
205 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
206
207 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
208 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
209 switch (item->type) {
210 case '8':
211 n = snprintf(bufout, sizeof(bufout), "telnet://%s@%s:%s",
212 item->selector, item->host, item->port);
213 break;
214 case 'h':
215 n = snprintf(bufout, sizeof(bufout), "%s",
216 (item->selector + 4));
217 break;
218 case 'T':
219 n = snprintf(bufout, sizeof(bufout), "tn3270://%s@%s:%s",
220 item->selector, item->host, item->port);
221 break;
222 default:
223 fmt = strcmp(item->port, "70") ?
224 "gopher://%1$s:%4$s/%2$c%3$s" :
225 "gopher://%s/%c%s";
226 n = snprintf(bufout, sizeof(bufout), fmt,
227 item->host, item->type,
228 item->selector, item->port);
229 break;
230 }
231
232 if (n >= sizeof(bufout))
233 bufout[sizeof(bufout)-1] = '\0';
234
235 n = mbsprint(bufout, columns);
236 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
237 if (n < columns)
238 printf("%*s", (int)(columns - n), " ");
239
240 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
241 fflush(stdout);
242 }
243
244 void
245 uidisplay(Item *entry)
246 {
247 Item *items;
248 Dir *dir;
249 size_t i, curln, lastln, nitems, printoff;
250
251 if (!entry ||
252 !(entry->type == '1' || entry->type == '+' || entry->type == '7'))
253 return;
254
255 curentry = entry;
256
257 putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
258 displaystatus(entry);
259
260 if (!(dir = entry->dat))
261 return;
262
263 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
264
265 items = dir->items;
266 nitems = dir->nitems;
267 printoff = dir->printoff;
268 curln = dir->curline;
269 lastln = printoff + lines-1; /* one off for status bar */
270
271 for (i = printoff; i < nitems && i < lastln; ++i) {
272 if (i != printoff)
273 putp(tparm(cursor_down, 0, 0, 0, 0, 0, 0, 0, 0, 0));
274 if (i == curln) {
275 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
276 putp(tparm(SELECTION));
277 }
278 printitem(&items[i]);
279 putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0, 0, 0));
280 if (i == curln)
281 putp(tparm(RESET));
282 }
283
284 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
285 fflush(stdout);
286 }
287
288 static void
289 movecurline(Item *item, int l)
290 {
291 Dir *dir = item->dat;
292 size_t nitems;
293 ssize_t curline, offline;
294 int plines = lines-2;
295
296 if (dir == NULL)
297 return;
298
299 curline = dir->curline + l;
300 nitems = dir->nitems;
301 if (curline < 0 || curline >= nitems)
302 return;
303
304 printitem(&dir->items[dir->curline]);
305 dir->curline = curline;
306
307 if (l > 0) {
308 offline = dir->printoff + lines-1;
309 if (curline - dir->printoff >= plines / 2 && offline < nitems) {
310 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
311
312 putp(tparm(cursor_address, plines,
313 0, 0, 0, 0, 0, 0, 0, 0));
314 putp(tparm(scroll_forward, 0, 0, 0, 0, 0, 0, 0, 0, 0));
315 printitem(&dir->items[offline]);
316
317 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
318 dir->printoff += l;
319 }
320 } else {
321 offline = dir->printoff + l;
322 if (curline - offline <= plines / 2 && offline >= 0) {
323 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
324
325 putp(tparm(cursor_address, 0, 0, 0, 0, 0, 0, 0, 0, 0));
326 putp(tparm(scroll_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0));
327 printitem(&dir->items[offline]);
328 putchar('\n');
329
330 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
331 dir->printoff += l;
332 }
333 }
334
335 putp(tparm(cursor_address, curline - dir->printoff,
336 0, 0, 0, 0, 0, 0, 0, 0));
337 putp(tparm(SELECTION));
338 printitem(&dir->items[curline]);
339 putp(tparm(RESET));
340 if (dir->items[dir->curline].type == 'i') {
341 displaystatus(item);
342 } else {
343 displayuri(&dir->items[dir->curline]);
344 }
345 fflush(stdout);
346 }
347
348 static void
349 jumptoline(Item *entry, ssize_t line, int absolute)
350 {
351 Dir *dir = entry->dat;
352 size_t lastitem;
353 int lastpagetop, plines = lines-2;
354
355 if (!dir)
356 return;
357 lastitem = dir->nitems-1;
358
359 if (line < 0)
360 line = 0;
361 if (line > lastitem)
362 line = lastitem;
363
364 if (dir->curline == line)
365 return;
366
367 if (lastitem <= plines) { /* all items fit on one page */
368 dir->curline = line;
369 } else if (line == 0) { /* jump to top */
370 if (absolute || dir->curline > plines || dir->printoff == 0)
371 dir->curline = 0;
372 dir->printoff = 0;
373 } else if (line + plines < lastitem) { /* jump before last page */
374 dir->curline = line;
375 dir->printoff = line;
376 } else { /* jump within the last page */
377 lastpagetop = lastitem - plines;
378 if (dir->printoff == lastpagetop || absolute)
379 dir->curline = line;
380 else if (dir->curline < lastpagetop)
381 dir->curline = lastpagetop;
382 dir->printoff = lastpagetop;
383 }
384
385 uidisplay(entry);
386 return;
387 }
388
389 void
390 searchinline(const char *searchstr, Item *entry, int pos)
391 {
392 Dir *dir;
393 int i;
394
395 if (!searchstr || !(dir = entry->dat))
396 return;
397
398 if (pos > 0) {
399 for (i = dir->curline + 1; i < dir->nitems; ++i) {
400 if (strcasestr(dir->items[i].username, searchstr)) {
401 jumptoline(entry, i, 1);
402 break;
403 }
404 }
405 } else {
406 for (i = dir->curline - 1; i > -1; --i) {
407 if (strcasestr(dir->items[i].username, searchstr)) {
408 jumptoline(entry, i, 1);
409 break;
410 }
411 }
412 }
413 }
414
415 static ssize_t
416 nearentry(Item *entry, int direction)
417 {
418 Dir *dir = entry->dat;
419 size_t item, lastitem;
420
421 if (!dir)
422 return -1;
423 lastitem = dir->nitems;
424 item = dir->curline + direction;
425
426 for (; item < lastitem; item += direction) {
427 if (dir->items[item].type != 'i')
428 return item;
429 }
430
431 return dir->curline;
432 }
433
434 Item *
435 uiselectitem(Item *entry)
436 {
437 Dir *dir;
438 char *searchstr = NULL;
439 int plines = lines-2;
440
441 if (!entry || !(dir = entry->dat))
442 return NULL;
443
444 for (;;) {
445 switch (getchar()) {
446 case 0x1b: /* ESC */
447 switch (getchar()) {
448 case 0x1b:
449 goto quit;
450 case '[':
451 break;
452 default:
453 continue;
454 }
455 switch (getchar()) {
456 case '4':
457 if (getchar() != '~')
458 continue;
459 goto end;
460 case '5':
461 if (getchar() != '~')
462 continue;
463 goto pgup;
464 case '6':
465 if (getchar() != '~')
466 continue;
467 goto pgdown;
468 case 'A':
469 goto lnup;
470 case 'B':
471 goto lndown;
472 case 'C':
473 goto pgnext;
474 case 'D':
475 goto pgprev;
476 case 'H':
477 goto home;
478 case 0x1b:
479 goto quit;
480 }
481 continue;
482 case _key_pgprev:
483 pgprev:
484 return entry->entry;
485 case _key_pgnext:
486 case '\n':
487 pgnext:
488 if (dir)
489 return &dir->items[dir->curline];
490 continue;
491 case _key_lndown:
492 lndown:
493 movecurline(entry, 1);
494 continue;
495 case _key_entrydown:
496 jumptoline(entry, nearentry(entry, 1), 1);
497 continue;
498 case _key_pgdown:
499 pgdown:
500 jumptoline(entry, dir->printoff + plines, 0);
501 continue;
502 case _key_end:
503 end:
504 jumptoline(entry, dir->nitems, 0);
505 continue;
506 case _key_lnup:
507 lnup:
508 movecurline(entry, -1);
509 continue;
510 case _key_entryup:
511 jumptoline(entry, nearentry(entry, -1), 1);
512 continue;
513 case _key_pgup:
514 pgup:
515 jumptoline(entry, dir->printoff - plines, 0);
516 continue;
517 case _key_home:
518 home:
519 jumptoline(entry, 0, 0);
520 continue;
521 case _key_search:
522 search:
523 free(searchstr);
524 if ((searchstr = uiprompt("Search for: ")) &&
525 searchstr[0])
526 goto searchnext;
527 clear(&searchstr);
528 continue;
529 case _key_searchnext:
530 searchnext:
531 searchinline(searchstr, entry, +1);
532 continue;
533 case _key_searchprev:
534 searchinline(searchstr, entry, -1);
535 continue;
536 case 0x04:
537 case _key_quit:
538 quit:
539 return NULL;
540 case _key_fetch:
541 fetch:
542 if (entry->raw)
543 continue;
544 return entry;
545 case _key_uri:
546 if (dir)
547 displayuri(&dir->items[dir->curline]);
548 continue;
549 case _key_help: /* FALLTHROUGH */
550 return help(entry);
551 default:
552 continue;
553 }
554 }
555 }
556
557 void
558 uisigwinch(int signal)
559 {
560 Dir *dir;
561
562 setupterm(NULL, 1, NULL);
563 putp(tparm(change_scroll_region, 0, lines-2, 0, 0, 0, 0, 0, 0, 0));
564
565 if (!curentry || !(dir = curentry->dat))
566 return;
567
568 if (dir->curline - dir->printoff > lines-2)
569 dir->curline = dir->printoff + lines-2;
570
571 uidisplay(curentry);
572 }