You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

339 lines
6.4 KiB

  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. #include "common.h"
  11. static char bufout[256];
  12. static Item *curentry;
  13. static char cmd;
  14. int lines, columns;
  15. static void
  16. viewsize(int *ln, int *col)
  17. {
  18. struct winsize ws;
  19. if (ioctl(1, TIOCGWINSZ, &ws) < 0) {
  20. die("Could not get terminal resolution: %s",
  21. strerror(errno));
  22. }
  23. if (ln)
  24. *ln = ws.ws_row-1; /* one off for status bar */
  25. if (col)
  26. *col = ws.ws_col;
  27. }
  28. void
  29. uisetup(void)
  30. {
  31. viewsize(&lines, &columns);
  32. }
  33. void
  34. uicleanup(void)
  35. {
  36. return;
  37. }
  38. void
  39. help(void)
  40. {
  41. puts("Commands:\n"
  42. "N = [1-9]...: browse item N.\n"
  43. "uN...: print item N uri.\n"
  44. "0: browse previous item.\n"
  45. "n: show next page.\n"
  46. "p: show previous page.\n"
  47. "t: go to the top of the page\n"
  48. "b: go to the bottom of the page\n"
  49. "/foo: search for string \"foo\"\n"
  50. "!: refetch failed item.\n"
  51. "^D, q: quit.\n"
  52. "h, ?: this help.");
  53. }
  54. static int
  55. ndigits(size_t n)
  56. {
  57. return (n < 10) ? 1 : (n < 100) ? 2 : 3;
  58. }
  59. void
  60. uistatus(char *fmt, ...)
  61. {
  62. va_list arg;
  63. int n;
  64. va_start(arg, fmt);
  65. n = vsnprintf(bufout, sizeof(bufout), fmt, arg);
  66. va_end(arg);
  67. if (n < sizeof(bufout)-1) {
  68. n += snprintf(bufout + n, sizeof(bufout) - n,
  69. " [Press Enter to continue \xe2\x98\x83]");
  70. }
  71. if (n >= sizeof(bufout))
  72. bufout[sizeof(bufout)-1] = '\0';
  73. mbsprint(bufout, columns);
  74. fflush(stdout);
  75. getchar();
  76. }
  77. static void
  78. printstatus(Item *item, char c)
  79. {
  80. Dir *dir = item->dat;
  81. char *fmt;
  82. size_t nitems = dir ? dir->nitems : 0;
  83. unsigned long long printoff = dir ? dir->printoff : 0;
  84. fmt = (strcmp(item->port, "70") && strcmp(item->port, "gopher")) ?
  85. "%1$3lld%%%*2$3$c %4$s:%8$s/%5$c%6$s [%7$c]: " :
  86. "%3lld%%%*c %s/%c%s [%c]: ";
  87. if (snprintf(bufout, sizeof(bufout), fmt,
  88. (printoff + lines-1 >= nitems) ? 100 :
  89. (printoff + lines) * 100 / nitems, ndigits(nitems)+2, '|',
  90. item->host, item->type, item->selector, c, item->port)
  91. >= sizeof(bufout))
  92. bufout[sizeof(bufout)-1] = '\0';
  93. mbsprint(bufout, columns);
  94. }
  95. char *
  96. uiprompt(char *fmt, ...)
  97. {
  98. va_list ap;
  99. char *input = NULL;
  100. size_t n = 0;
  101. ssize_t r;
  102. va_start(ap, fmt);
  103. if (vsnprintf(bufout, sizeof(bufout), fmt, ap) >= sizeof(bufout))
  104. bufout[sizeof(bufout)-1] = '\0';
  105. va_end(ap);
  106. mbsprint(bufout, columns);
  107. fflush(stdout);
  108. if ((r = getline(&input, &n, stdin)) < 0) {
  109. clearerr(stdin);
  110. clear(&input);
  111. putchar('\n');
  112. } else if (input[r - 1] == '\n') {
  113. input[--r] = '\0';
  114. }
  115. return input;
  116. }
  117. void
  118. uidisplay(Item *entry)
  119. {
  120. Item *items;
  121. Dir *dir;
  122. size_t i, nlines, nitems;
  123. int nd;
  124. if (!entry ||
  125. !(entry->type == '1' || entry->type == '+' || entry->type == '7') ||
  126. !(dir = entry->dat))
  127. return;
  128. curentry = entry;
  129. items = dir->items;
  130. nitems = dir->nitems;
  131. nlines = dir->printoff + lines;
  132. nd = ndigits(nitems);
  133. for (i = dir->printoff; i < nitems && i < nlines; ++i) {
  134. if (snprintf(bufout, sizeof(bufout), "%*zu %s %s",
  135. nd, i+1, typedisplay(items[i].type),
  136. items[i].username)
  137. >= sizeof(bufout))
  138. bufout[sizeof(bufout)-1] = '\0';
  139. mbsprint(bufout, columns);
  140. putchar('\n');
  141. }
  142. fflush(stdout);
  143. }
  144. void
  145. printuri(Item *item, size_t i)
  146. {
  147. char *fmt;
  148. int n;
  149. if (!item)
  150. return;
  151. switch (item->type) {
  152. case 0:
  153. return;
  154. case '8':
  155. n = snprintf(bufout, sizeof(bufout), "telnet://%s@%s:%s",
  156. item->selector, item->host, item->port);
  157. break;
  158. case 'i':
  159. n = snprintf(bufout, sizeof(bufout), "%zu: %s",
  160. i, item->username);
  161. break;
  162. case 'h':
  163. n = snprintf(bufout, sizeof(bufout), "%zu: %s: %s",
  164. i, item->username, item->selector);
  165. break;
  166. case 'T':
  167. n = snprintf(bufout, sizeof(bufout), "tn3270://%s@%s:%s",
  168. item->selector, item->host, item->port);
  169. break;
  170. default:
  171. fmt = strcmp(item->port, "70") ?
  172. "%1$zu: %2$s: gopher://%3$s:%6$s/%4$c%5$s" :
  173. "%zu: %s: gopher://%s/%c%s";
  174. n = snprintf(bufout, sizeof(bufout), fmt, i, item->username,
  175. item->host, item->type, item->selector, item->port);
  176. break;
  177. }
  178. if (n >= sizeof(bufout))
  179. bufout[sizeof(bufout)-1] = '\0';
  180. mbsprint(bufout, columns);
  181. putchar('\n');
  182. }
  183. void
  184. searchinline(const char *searchstr, Item *entry)
  185. {
  186. Dir *dir;
  187. size_t i;
  188. if (!searchstr || !*searchstr || !(dir = entry->dat))
  189. return;
  190. for (i = 0; i < dir->nitems; ++i)
  191. if (strstr(dir->items[i].username, searchstr))
  192. printuri(&(dir->items[i]), i + 1);
  193. }
  194. Item *
  195. uiselectitem(Item *entry)
  196. {
  197. Dir *dir;
  198. char buf[BUFSIZ], *sstr, nl;
  199. int item, nitems;
  200. if (!entry || !(dir = entry->dat))
  201. return NULL;
  202. nitems = dir ? dir->nitems : 0;
  203. for (;;) {
  204. if (!cmd)
  205. cmd = 'h';
  206. printstatus(entry, cmd);
  207. fflush(stdout);
  208. if (!fgets(buf, sizeof(buf), stdin)) {
  209. putchar('\n');
  210. return NULL;
  211. }
  212. if (isdigit(*buf)) {
  213. cmd = '\0';
  214. nl = '\0';
  215. if (sscanf(buf, "%d%c", &item, &nl) != 2 || nl != '\n')
  216. item = -1;
  217. } else if (!strcmp(buf+1, "\n")) {
  218. item = -1;
  219. cmd = *buf;
  220. } else if (*buf == '/') {
  221. for (sstr = buf+1; *sstr && *sstr != '\n'; ++sstr)
  222. ;
  223. *sstr = '\0';
  224. sstr = buf+1;
  225. cmd = *buf;
  226. } else if (isdigit(*(buf+1))) {
  227. nl = '\0';
  228. if (sscanf(buf+1, "%d%c", &item, &nl) != 2 || nl != '\n')
  229. item = -1;
  230. else
  231. cmd = *buf;
  232. }
  233. switch (cmd) {
  234. case '\0':
  235. break;
  236. case 'q':
  237. return NULL;
  238. case 'n':
  239. if (lines < nitems - dir->printoff &&
  240. lines < (size_t)-1 - dir->printoff)
  241. dir->printoff += lines;
  242. return entry;
  243. case 'p':
  244. if (lines <= dir->printoff)
  245. dir->printoff -= lines;
  246. else
  247. dir->printoff = 0;
  248. return entry;
  249. case 'b':
  250. if (nitems > lines)
  251. dir->printoff = nitems - lines;
  252. else
  253. dir->printoff = 0;
  254. return entry;
  255. case 't':
  256. dir->printoff = 0;
  257. return entry;
  258. case '!':
  259. if (entry->raw)
  260. continue;
  261. return entry;
  262. case 'u':
  263. if (item > 0 && item <= nitems)
  264. printuri(&dir->items[item-1], item);
  265. continue;
  266. case '/':
  267. if (*sstr)
  268. searchinline(sstr, entry);
  269. continue;
  270. case 'h':
  271. case '?':
  272. help();
  273. continue;
  274. default:
  275. cmd = 'h';
  276. continue;
  277. }
  278. if (item >= 0 && item <= nitems)
  279. break;
  280. }
  281. if (item > 0)
  282. return &dir->items[item-1];
  283. return entry->entry;
  284. }
  285. void
  286. uisigwinch(int signal)
  287. {
  288. uisetup();
  289. putchar('\n');
  290. uidisplay(curentry);
  291. printstatus(curentry, cmd);
  292. fflush(stdout);
  293. }