tui_txt.c - sacc - sacc (saccomys): simple gopher client.
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       tui_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 == '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 }