URI: 
       tui_txt.c - sacc - sacc(omys), simple console gopher client (mirror)
  HTML git clone https://git.parazyd.org/sacc
   DIR Log
   DIR Files
   DIR Refs
   DIR LICENSE
       ---
       tui_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 }