sacc

sacc (saccomys): simple gopher client.
Log | Files | Refs | LICENSE

ui_txt.c (6523B)


      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 	     "N = [1-9]...: browse item N.\n"
     51 	     "uN...: print item N uri.\n"
     52 	     "0: browse previous item.\n"
     53 	     "n: show next page.\n"
     54 	     "p: show previous page.\n"
     55 	     "t: go to the top of the page\n"
     56 	     "b: go to the bottom of the page\n"
     57 	     "/foo: search for string \"foo\"\n"
     58 	     "!: refetch failed item.\n"
     59 	     "^D, q: quit.\n"
     60 	     "h, ?: this help.");
     61 }
     62 
     63 static int
     64 ndigits(size_t n)
     65 {
     66 	return (n < 10) ? 1 : (n < 100) ? 2 : 3;
     67 }
     68 
     69 void
     70 uistatus(char *fmt, ...)
     71 {
     72 	va_list arg;
     73 	int n;
     74 
     75 	va_start(arg, fmt);
     76 	n = vsnprintf(bufout, sizeof(bufout), fmt, arg);
     77 	va_end(arg);
     78 
     79 	if (n < sizeof(bufout)-1) {
     80 		n += snprintf(bufout + n, sizeof(bufout) - n,
     81 		              " [Press Enter to continue \xe2\x98\x83]");
     82 	}
     83 	if (n >= sizeof(bufout))
     84 		bufout[sizeof(bufout)-1] = '\0';
     85 
     86 	mbsprint(bufout, columns);
     87 	fflush(stdout);
     88 
     89 	getchar();
     90 }
     91 
     92 static void
     93 printstatus(Item *item, char c)
     94 {
     95 	Dir *dir = item->dat;
     96 	char *fmt;
     97 	size_t nitems = dir ? dir->nitems : 0;
     98 	unsigned long long printoff = dir ? dir->printoff : 0;
     99 
    100 	fmt = (strcmp(item->port, "70") && strcmp(item->port, "gopher")) ?
    101 	      "%1$3lld%%%*2$3$c %4$s:%8$s/%5$c%6$s [%7$c]: " :
    102               "%3lld%%%*c %s/%c%s [%c]: ";
    103 	if (snprintf(bufout, sizeof(bufout), fmt,
    104 	             (printoff + lines-1 >= nitems) ? 100 :
    105 	             (printoff + lines) * 100 / nitems, ndigits(nitems)+2, '|',
    106 	             item->host, item->type, item->selector, c, item->port)
    107 	    >= sizeof(bufout))
    108 		bufout[sizeof(bufout)-1] = '\0';
    109 	mbsprint(bufout, columns);
    110 }
    111 
    112 char *
    113 uiprompt(char *fmt, ...)
    114 {
    115 	va_list ap;
    116 	char *input = NULL;
    117 	size_t n = 0;
    118 	ssize_t r;
    119 
    120 	va_start(ap, fmt);
    121 	if (vsnprintf(bufout, sizeof(bufout), fmt, ap) >= sizeof(bufout))
    122 		bufout[sizeof(bufout)-1] = '\0';
    123 	va_end(ap);
    124 
    125 	mbsprint(bufout, columns);
    126 	fflush(stdout);
    127 
    128 	if ((r = getline(&input, &n, stdin)) < 0) {
    129 		clearerr(stdin);
    130 		clear(&input);
    131 		putchar('\n');
    132 	} else if (input[r - 1] == '\n') {
    133 		input[--r] = '\0';
    134 	}
    135 
    136 	return input;
    137 }
    138 
    139 void
    140 uidisplay(Item *entry)
    141 {
    142 	Item *items;
    143 	Dir *dir;
    144 	size_t i, nlines, nitems;
    145 	int nd;
    146 
    147 	if (!entry ||
    148 	    !(entry->type == '1' || entry->type == '+' || entry->type == '7') ||
    149 	    !(dir = entry->dat))
    150 		return;
    151 
    152 	curentry = entry;
    153 
    154 	items = dir->items;
    155 	nitems = dir->nitems;
    156 	nlines = dir->printoff + lines;
    157 	nd = ndigits(nitems);
    158 
    159 	for (i = dir->printoff; i < nitems && i < nlines; ++i) {
    160 		if (snprintf(bufout, sizeof(bufout), "%*zu %s %s",
    161 		             nd, i+1, typedisplay(items[i].type),
    162 		             items[i].username)
    163 		    >= sizeof(bufout))
    164 			bufout[sizeof(bufout)-1] = '\0';
    165 		mbsprint(bufout, columns);
    166 		putchar('\n');
    167 	}
    168 
    169 	fflush(stdout);
    170 }
    171 
    172 void
    173 printuri(Item *item, size_t i)
    174 {
    175 	char *fmt;
    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 		fmt = strcmp(item->port, "70") ?
    202 		      "%1$zu: %2$s: gopher://%3$s:%6$s/%4$c%5$s" :
    203 		      "%zu: %s: gopher://%s/%c%s";
    204 		n = snprintf(bufout, sizeof(bufout), fmt, i, item->username,
    205 		             item->host, item->type, item->selector, item->port);
    206 		break;
    207 	}
    208 
    209 	if (n >= sizeof(bufout))
    210 		bufout[sizeof(bufout)-1] = '\0';
    211 
    212 	mbsprint(bufout, columns);
    213 	putchar('\n');
    214 }
    215 
    216 void
    217 searchinline(const char *searchstr, Item *entry)
    218 {
    219 	Dir *dir;
    220 	size_t i;
    221 
    222 	if (!searchstr || !*searchstr || !(dir = entry->dat))
    223 		return;
    224 
    225 	for (i = 0; i < dir->nitems; ++i)
    226 		if (strstr(dir->items[i].username, searchstr))
    227 			printuri(&(dir->items[i]), i + 1);
    228 }
    229 
    230 Item *
    231 uiselectitem(Item *entry)
    232 {
    233 	Dir *dir;
    234 	char buf[BUFSIZ], *sstr, nl;
    235 	int item, nitems;
    236 
    237 	if (!entry || !(dir = entry->dat))
    238 		return NULL;
    239 
    240 	nitems = dir ? dir->nitems : 0;
    241 
    242 	for (;;) {
    243 		if (!cmd)
    244 			cmd = 'h';
    245 		printstatus(entry, cmd);
    246 		fflush(stdout);
    247 
    248 		if (!fgets(buf, sizeof(buf), stdin)) {
    249 			putchar('\n');
    250 			return NULL;
    251 		}
    252 		if (isdigit(*buf)) {
    253 			cmd = '\0';
    254 			nl = '\0';
    255 			if (sscanf(buf, "%d%c", &item, &nl) != 2 || nl != '\n')
    256 				item = -1;
    257 		} else if (!strcmp(buf+1, "\n")) {
    258 			item = -1;
    259 			cmd = *buf;
    260 		} else if (*buf == '/') {
    261 			for (sstr = buf+1; *sstr && *sstr != '\n'; ++sstr)
    262 			     ;
    263 			*sstr = '\0';
    264 			sstr = buf+1;
    265 			cmd = *buf;
    266 		} else if (isdigit(*(buf+1))) {
    267 			nl = '\0';
    268 			if (sscanf(buf+1, "%d%c", &item, &nl) != 2 || nl != '\n')
    269 				item = -1;
    270 			else
    271 				cmd = *buf;
    272 		}
    273 
    274 		switch (cmd) {
    275 		case '\0':
    276 			break;
    277 		case 'q':
    278 			return NULL;
    279 		case 'n':
    280 			if (lines < nitems - dir->printoff &&
    281 			    lines < (size_t)-1 - dir->printoff)
    282 				dir->printoff += lines;
    283 			return entry;
    284 		case 'p':
    285 			if (lines <= dir->printoff)
    286 				dir->printoff -= lines;
    287 			else
    288 				dir->printoff = 0;
    289 			return entry;
    290 		case 'b':
    291 			if (nitems > lines)
    292 				dir->printoff = nitems - lines;
    293 			else
    294 				dir->printoff = 0;
    295 			return entry;
    296 		case 't':
    297 			dir->printoff = 0;
    298 			return entry;
    299 		case '!':
    300 			if (entry->raw)
    301 				continue;
    302 			return entry;
    303 		case 'u':
    304 			if (item > 0 && item <= nitems)
    305 				printuri(&dir->items[item-1], item);
    306 			continue;
    307 		case '/':
    308 			if (*sstr)
    309 				searchinline(sstr, entry);
    310 			continue;
    311 		case 'h':
    312 		case '?':
    313 			help();
    314 			continue;
    315 		default:
    316 			cmd = 'h';
    317 			continue;
    318 		}
    319 
    320 		if (item >= 0 && item <= nitems)
    321 			break;
    322 	}
    323 
    324 	if (item > 0)
    325 		return &dir->items[item-1];
    326 
    327 	return entry->entry;
    328 }
    329 
    330 void
    331 uisigwinch(int signal)
    332 {
    333 	uisetup();
    334 	putchar('\n');
    335 	uidisplay(curentry);
    336 	printstatus(curentry, cmd);
    337 	fflush(stdout);
    338 }