sacc

sacc(omys), simple console gopher client (mirror)
git clone https://git.parazyd.org/sacc
Log | Files | Refs | LICENSE

ui_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 }