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