ui_txt.c - sacc - sacc(omys), simple console gopher client | |
git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
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 ==… | |
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 !… | |
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'; ++sst… | |
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… | |
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 } |