Recently Written · git

sacc

Mirror of git://bitreich.org/sacc, the sacc gopher client from bitreich.org.

git clone https://github.com/equwal/sacc

Log | Files | Refs


ui_txt.c (5805 bytes)

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 || feof(stdin)) {
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 }