sacc

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

ui_txt.c (5790B)


      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 int lines, columns;
     16 static char cmd;
     17 
     18 static void
     19 viewsize(int *ln, int *col)
     20 {
     21 	struct winsize ws;
     22 
     23 	if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
     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 static 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 	     "Y: yank page URI.\n"
     59 	     "y#: yank item number # URI.\n"
     60 	     "/str: search for string \"str\"\n"
     61 	     "!: refetch failed item.\n"
     62 	     "^D, q: quit.\n"
     63 	     "h, ?: this help.");
     64 }
     65 
     66 static int
     67 ndigits(size_t n)
     68 {
     69 	return (n < 10) ? 1 : (n < 100) ? 2 : 3;
     70 }
     71 
     72 void
     73 uistatus(char *fmt, ...)
     74 {
     75 	va_list arg;
     76 	int n;
     77 
     78 	va_start(arg, fmt);
     79 	n = vsnprintf(bufout, sizeof(bufout), fmt, arg);
     80 	va_end(arg);
     81 
     82 	if (n < sizeof(bufout)-1) {
     83 		snprintf(bufout+n, sizeof(bufout)-n,
     84 		         " [Press Enter to continue \xe2\x98\x83]");
     85 	}
     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%% %s/%c%s [%c]: ";
    104 	snprintf(bufout, sizeof(bufout), fmt,
    105 	         (printoff + lines-1 >= nitems) ? 100 :
    106 	         (printoff + lines) * 100 / nitems,
    107 	         item->host, item->type, item->selector, c, item->port);
    108 
    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 	vsnprintf(bufout, sizeof(bufout), fmt, ap);
    122 	va_end(ap);
    123 
    124 	mbsprint(bufout, columns);
    125 	fflush(stdout);
    126 
    127 	if ((r = getline(&input, &n, stdin)) == -1) {
    128 		clearerr(stdin);
    129 		clear(&input);
    130 		putchar('\n');
    131 	} else if (input[r - 1] == '\n') {
    132 		input[--r] = '\0';
    133 	}
    134 
    135 	return input;
    136 }
    137 
    138 void
    139 uidisplay(Item *entry)
    140 {
    141 	Item *items;
    142 	Dir *dir;
    143 	size_t i, nlines, nitems;
    144 	int nd;
    145 
    146 	if (!entry ||
    147 	    !(entry->type == '1' || entry->type == '+' || entry->type == '7') ||
    148 	    !(dir = entry->dat))
    149 		return;
    150 
    151 	curentry = entry;
    152 
    153 	items = dir->items;
    154 	nitems = dir->nitems;
    155 	nlines = dir->printoff + lines;
    156 	nd = ndigits(nitems);
    157 
    158 	for (i = dir->printoff; i < nitems && i < nlines; ++i) {
    159 		snprintf(bufout, sizeof(bufout), "%*zu %s %s",
    160 		         nd, i+1,typedisplay(items[i].type),
    161 		         items[i].username);
    162 
    163 		mbsprint(bufout, columns);
    164 		putchar('\n');
    165 	}
    166 
    167 	fflush(stdout);
    168 }
    169 
    170 static void
    171 printuri(Item *item, size_t i)
    172 {
    173 	if (!item || item->type == 0 || item->type == 'i')
    174 		return;
    175 
    176 	itemuri(item, bufout, sizeof(bufout));
    177 
    178 	mbsprint(bufout, columns);
    179 	putchar('\n');
    180 }
    181 
    182 static void
    183 searchinline(const char *searchstr, Item *entry)
    184 {
    185 	Dir *dir;
    186 	size_t i;
    187 
    188 	if (!searchstr || !*searchstr || !(dir = entry->dat))
    189 		return;
    190 
    191 	for (i = 0; i < dir->nitems; ++i)
    192 		if (strcasestr(dir->items[i].username, searchstr))
    193 			printuri(&(dir->items[i]), i + 1);
    194 }
    195 
    196 Item *
    197 uiselectitem(Item *entry)
    198 {
    199 	Dir *dir;
    200 	char buf[BUFSIZ], *sstr = NULL, nl;
    201 	int item, nitems;
    202 
    203 	if (!entry || !(dir = entry->dat))
    204 		return NULL;
    205 
    206 	nitems = dir ? dir->nitems : 0;
    207 
    208 	for (;;) {
    209 		if (!cmd)
    210 			cmd = 'h';
    211 		printstatus(entry, cmd);
    212 		fflush(stdout);
    213 
    214 		if (!fgets(buf, sizeof(buf), stdin)) {
    215 			putchar('\n');
    216 			return NULL;
    217 		}
    218 		if (isdigit((unsigned char)*buf)) {
    219 			cmd = '\0';
    220 			nl = '\0';
    221 			if (sscanf(buf, "%d%c", &item, &nl) != 2 || nl != '\n')
    222 				item = -1;
    223 		} else if (!strcmp(buf+1, "\n")) {
    224 			item = -1;
    225 			cmd = *buf;
    226 		} else if (*buf == '/') {
    227 			for (sstr = buf+1; *sstr && *sstr != '\n'; ++sstr)
    228 			     ;
    229 			*sstr = '\0';
    230 			sstr = buf+1;
    231 			cmd = *buf;
    232 		} else if (isdigit((unsigned char)*(buf+1))) {
    233 			nl = '\0';
    234 			if (sscanf(buf+1, "%d%c", &item, &nl) != 2 || nl != '\n')
    235 				item = -1;
    236 			else
    237 				cmd = *buf;
    238 		}
    239 
    240 		switch (cmd) {
    241 		case '\0':
    242 			break;
    243 		case 'q':
    244 			return NULL;
    245 		case 'n':
    246 			if (lines < nitems - dir->printoff &&
    247 			    lines < (size_t)-1 - dir->printoff)
    248 				dir->printoff += lines;
    249 			return entry;
    250 		case 'p':
    251 			if (lines <= dir->printoff)
    252 				dir->printoff -= lines;
    253 			else
    254 				dir->printoff = 0;
    255 			return entry;
    256 		case 'b':
    257 			if (nitems > lines)
    258 				dir->printoff = nitems - lines;
    259 			else
    260 				dir->printoff = 0;
    261 			return entry;
    262 		case 't':
    263 			dir->printoff = 0;
    264 			return entry;
    265 		case '!':
    266 			if (entry->raw)
    267 				continue;
    268 			return entry;
    269 		case 'U':
    270 			printuri(entry, 0);
    271 			continue;
    272 		case 'u':
    273 			if (item > 0 && item <= nitems)
    274 				printuri(&dir->items[item-1], item);
    275 			continue;
    276 		case 'Y':
    277 			yankitem(entry);
    278 			continue;
    279 		case 'y':
    280 			if (item > 0 && item <= nitems)
    281 				yankitem(&dir->items[item-1]);
    282 			continue;
    283 		case '/':
    284 			if (sstr && *sstr)
    285 				searchinline(sstr, entry);
    286 			continue;
    287 		case 'h':
    288 		case '?':
    289 			help();
    290 			continue;
    291 		default:
    292 			cmd = 'h';
    293 			continue;
    294 		}
    295 
    296 		if (item >= 0 && item <= nitems)
    297 			break;
    298 	}
    299 
    300 	if (item > 0)
    301 		return &dir->items[item-1];
    302 
    303 	return entry->entry;
    304 }
    305 
    306 void
    307 uisigwinch(int signal)
    308 {
    309 	uisetup();
    310 
    311 	if (!curentry)
    312 		return;
    313 
    314 	putchar('\n');
    315 	uidisplay(curentry);
    316 	printstatus(curentry, cmd);
    317 	fflush(stdout);
    318 }