sacc

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

ui_txt.c (6251B)


      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 'i':
    185 		n = snprintf(bufout, sizeof(bufout), "%zu: %s",
    186 		             i, item->username);
    187 		break;
    188 	case 'h':
    189 		n = snprintf(bufout, sizeof(bufout), "%zu: %s: %s",
    190 		         i, item->username, item->selector);
    191 		break;
    192 	default:
    193 		fmt = strcmp(item->port, "70") ?
    194 		      "%1$zu: %2$s: gopher://%3$s:%6$s/%4$c%5$s" :
    195 		      "%zu: %s: gopher://%s/%c%s";
    196 		n = snprintf(bufout, sizeof(bufout), fmt, i, item->username,
    197 		             item->host, item->type, item->selector, item->port);
    198 		break;
    199 	}
    200 
    201 	if (n >= sizeof(bufout))
    202 		bufout[sizeof(bufout)-1] = '\0';
    203 
    204 	mbsprint(bufout, columns);
    205 	putchar('\n');
    206 }
    207 
    208 void
    209 searchinline(const char *searchstr, Item *entry)
    210 {
    211 	Dir *dir;
    212 	size_t i;
    213 
    214 	if (!searchstr || !*searchstr || !(dir = entry->dat))
    215 		return;
    216 
    217 	for (i = 0; i < dir->nitems; ++i)
    218 		if (strstr(dir->items[i].username, searchstr))
    219 			printuri(&(dir->items[i]), i + 1);
    220 }
    221 
    222 Item *
    223 uiselectitem(Item *entry)
    224 {
    225 	Dir *dir;
    226 	char buf[BUFSIZ], *sstr, nl;
    227 	int item, nitems;
    228 
    229 	if (!entry || !(dir = entry->dat))
    230 		return NULL;
    231 
    232 	nitems = dir ? dir->nitems : 0;
    233 
    234 	for (;;) {
    235 		if (!cmd)
    236 			cmd = 'h';
    237 		printstatus(entry, cmd);
    238 		fflush(stdout);
    239 
    240 		if (!fgets(buf, sizeof(buf), stdin)) {
    241 			putchar('\n');
    242 			return NULL;
    243 		}
    244 		if (isdigit(*buf)) {
    245 			cmd = '\0';
    246 			nl = '\0';
    247 			if (sscanf(buf, "%d%c", &item, &nl) != 2 || nl != '\n')
    248 				item = -1;
    249 		} else if (!strcmp(buf+1, "\n")) {
    250 			item = -1;
    251 			cmd = *buf;
    252 		} else if (*buf == '/') {
    253 			for (sstr = buf+1; *sstr && *sstr != '\n'; ++sstr)
    254 			     ;
    255 			*sstr = '\0';
    256 			sstr = buf+1;
    257 			cmd = *buf;
    258 		} else if (isdigit(*(buf+1))) {
    259 			nl = '\0';
    260 			if (sscanf(buf+1, "%d%c", &item, &nl) != 2 || nl != '\n')
    261 				item = -1;
    262 			else
    263 				cmd = *buf;
    264 		}
    265 
    266 		switch (cmd) {
    267 		case '\0':
    268 			break;
    269 		case 'q':
    270 			return NULL;
    271 		case 'n':
    272 			if (lines < nitems - dir->printoff &&
    273 			    lines < (size_t)-1 - dir->printoff)
    274 				dir->printoff += lines;
    275 			return entry;
    276 		case 'p':
    277 			if (lines <= dir->printoff)
    278 				dir->printoff -= lines;
    279 			else
    280 				dir->printoff = 0;
    281 			return entry;
    282 		case 'b':
    283 			if (nitems > lines)
    284 				dir->printoff = nitems - lines;
    285 			else
    286 				dir->printoff = 0;
    287 			return entry;
    288 		case 't':
    289 			dir->printoff = 0;
    290 			return entry;
    291 		case '!':
    292 			if (entry->raw)
    293 				continue;
    294 			return entry;
    295 		case 'u':
    296 			if (item > 0 && item <= nitems)
    297 				printuri(&dir->items[item-1], item);
    298 			continue;
    299 		case '/':
    300 			if (*sstr)
    301 				searchinline(sstr, entry);
    302 			continue;
    303 		case 'h':
    304 		case '?':
    305 			help();
    306 			continue;
    307 		default:
    308 			cmd = 'h';
    309 			continue;
    310 		}
    311 
    312 		if (item >= 0 && item <= nitems)
    313 			break;
    314 	}
    315 
    316 	if (item > 0)
    317 		return &dir->items[item-1];
    318 
    319 	return entry->entry;
    320 }
    321 
    322 void
    323 uisigwinch(int signal)
    324 {
    325 	uisetup();
    326 	putchar('\n');
    327 	uidisplay(curentry);
    328 	printstatus(curentry, cmd);
    329 	fflush(stdout);
    330 }