t* sacc + cursorline and uri preview
       
   URI git clone git://git.codevoid.de/sacc-sdk
   DIR Log
   DIR Files
   DIR Refs
   DIR LICENSE
       ---
       tui_ti.c (12954B)
       ---
            1 #include <stdarg.h>
            2 #include <stdio.h>
            3 #include <stdlib.h>
            4 #include <string.h>
            5 #include <term.h>
            6 #include <termios.h>
            7 #include <unistd.h>
            8 #include <sys/types.h>
            9 
           10 #include "config.h"
           11 #include "common.h"
           12 
           13 #define C(c) #c
           14 #define S(c) C(c)
           15 
           16 #define SELECTION   "\x1B[31m"
           17 #define RESET       "\x1B[0m"
           18 
           19 static char bufout[256];
           20 static struct termios tsave;
           21 static struct termios tsacc;
           22 static Item *curentry;
           23 
           24 void
           25 uisetup(void)
           26 {
           27         tcgetattr(0, &tsave);
           28         tsacc = tsave;
           29         tsacc.c_lflag &= ~(ECHO|ICANON);
           30         tcsetattr(0, TCSANOW, &tsacc);
           31 
           32         setupterm(NULL, 1, NULL);
           33         putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           34         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           35         putp(tparm(change_scroll_region, 0, lines-2, 0, 0, 0, 0, 0, 0, 0));
           36         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           37         fflush(stdout);
           38 }
           39 
           40 void
           41 uicleanup(void)
           42 {
           43         putp(tparm(change_scroll_region, 0, lines-1, 0, 0, 0, 0, 0, 0, 0));
           44         putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           45         tcsetattr(0, TCSANOW, &tsave);
           46         fflush(stdout);
           47 }
           48 
           49 char *
           50 uiprompt(char *fmt, ...)
           51 {
           52         va_list ap;
           53         char *input = NULL;
           54         size_t n;
           55         ssize_t r;
           56 
           57         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           58 
           59         putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
           60         putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           61         putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           62 
           63         va_start(ap, fmt);
           64         if (vsnprintf(bufout, sizeof(bufout), fmt, ap) >= sizeof(bufout))
           65                 bufout[sizeof(bufout)-1] = '\0';
           66         va_end(ap);
           67         n = mbsprint(bufout, columns);
           68 
           69         putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           70         if (n < columns)
           71                 printf("%*s", (int)(columns - n), " ");
           72 
           73         putp(tparm(cursor_address, lines-1, n, 0, 0, 0, 0, 0, 0, 0));
           74 
           75         tsacc.c_lflag |= (ECHO|ICANON);
           76         tcsetattr(0, TCSANOW, &tsacc);
           77         fflush(stdout);
           78 
           79         n = 0;
           80         r = getline(&input, &n, stdin);
           81 
           82         tsacc.c_lflag &= ~(ECHO|ICANON);
           83         tcsetattr(0, TCSANOW, &tsacc);
           84         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
           85         fflush(stdout);
           86 
           87         if (r < 0) {
           88                 clearerr(stdin);
           89                 clear(&input);
           90         } else if (input[r - 1] == '\n') {
           91                 input[--r] = '\0';
           92         }
           93 
           94         return input;
           95 }
           96 
           97 static void
           98 printitem(Item *item)
           99 {
          100         if (snprintf(bufout, sizeof(bufout), "%s %s", typedisplay(item->type),
          101             item->username) >= sizeof(bufout))
          102                 bufout[sizeof(bufout)-1] = '\0';
          103         mbsprint(bufout, columns);
          104         putchar('\r');
          105 }
          106 
          107 static Item *
          108 help(Item *entry)
          109 {
          110         static Item item = {
          111                 .type = '0',
          112                 .raw = "Commands:\n"
          113                        "Down, " S(_key_lndown) ": move one line down.\n"
          114                        "Up, " S(_key_lnup) ": move one line up.\n"
          115                        "PgDown, " S(_key_pgdown) ": move one page down.\n"
          116                        "PgUp, " S(_key_pgup) ": move one page up.\n"
          117                        "Home, " S(_key_home) ": move to top of the page.\n"
          118                        "End, " S(_key_end) ": move to end of the page.\n"
          119                        "Right, " S(_key_pgnext) ": view highlighted item.\n"
          120                        "Left, " S(_key_pgprev) ": view previous item.\n"
          121                        S(_key_search) ": search current page.\n"
          122                        S(_key_searchnext) ": search string forward.\n"
          123                        S(_key_searchprev) ": search string backward.\n"
          124                        S(_key_uri) ": print item uri.\n"
          125                        S(_key_help) ": show this help.\n"
          126                        "^D, " S(_key_quit) ": exit sacc.\n"
          127         };
          128 
          129         item.entry = entry;
          130 
          131         return &item;
          132 }
          133 
          134 void
          135 uistatus(char *fmt, ...)
          136 {
          137         va_list ap;
          138         size_t n;
          139 
          140         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          141 
          142         putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
          143         putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          144 
          145         va_start(ap, fmt);
          146         n = vsnprintf(bufout, sizeof(bufout), fmt, ap);
          147         va_end(ap);
          148 
          149         if (n < sizeof(bufout)-1) {
          150                 n += snprintf(bufout + n, sizeof(bufout) - n,
          151                               " [Press a key to continue \xe2\x98\x83]");
          152         }
          153         if (n >= sizeof(bufout))
          154                 bufout[sizeof(bufout)-1] = '\0';
          155 
          156         n = mbsprint(bufout, columns);
          157         putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          158         if (n < columns)
          159                 printf("%*s", (int)(columns - n), " ");
          160 
          161         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          162         fflush(stdout);
          163 
          164         getchar();
          165 }
          166 
          167 static void
          168 displaystatus(Item *item)
          169 {
          170         Dir *dir = item->dat;
          171         char *fmt;
          172         size_t n, nitems = dir ? dir->nitems : 0;
          173         unsigned long long printoff = dir ? dir->printoff : 0;
          174 
          175         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          176 
          177         putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
          178         putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          179         fmt = (strcmp(item->port, "70") && strcmp(item->port, "gopher")) ?
          180               "%1$3lld%%| %2$s:%5$s/%3$c%4$s" : "%3lld%%| %s/%c%s";
          181         if (snprintf(bufout, sizeof(bufout), fmt,
          182                      (printoff + lines-1 >= nitems) ? 100 :
          183                      (printoff + lines-1) * 100 / nitems,
          184                      item->host, item->type, item->selector, item->port)
          185             >= sizeof(bufout))
          186                 bufout[sizeof(bufout)-1] = '\0';
          187         n = mbsprint(bufout, columns);
          188         putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          189         if (n < columns)
          190                 printf("%*s", (int)(columns - n), " ");
          191 
          192         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          193         fflush(stdout);
          194 }
          195 
          196 static void
          197 displayuri(Item *item)
          198 {
          199         char *fmt;
          200         size_t n;
          201 
          202         if (item->type == 0 || item->type == 'i')
          203                 return;
          204 
          205         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          206 
          207         putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0));
          208         putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          209         switch (item->type) {
          210         case '8':
          211                 n = snprintf(bufout, sizeof(bufout), "telnet://%s@%s:%s",
          212                              item->selector, item->host, item->port);
          213                 break;
          214         case 'h':
          215                 n = snprintf(bufout, sizeof(bufout), "%s",
          216                              (item->selector + 4));
          217                 break;
          218         case 'T':
          219                 n = snprintf(bufout, sizeof(bufout), "tn3270://%s@%s:%s",
          220                              item->selector, item->host, item->port);
          221                 break;
          222         default:
          223                 fmt = strcmp(item->port, "70") ?
          224                       "gopher://%1$s:%4$s/%2$c%3$s" :
          225                       "gopher://%s/%c%s";
          226                 n = snprintf(bufout, sizeof(bufout), fmt,
          227                              item->host, item->type,
          228                              item->selector, item->port);
          229                 break;
          230         }
          231 
          232         if (n >= sizeof(bufout))
          233                 bufout[sizeof(bufout)-1] = '\0';
          234 
          235         n = mbsprint(bufout, columns);
          236         putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          237         if (n < columns)
          238                 printf("%*s", (int)(columns - n), " ");
          239 
          240         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          241         fflush(stdout);
          242 }
          243 
          244 void
          245 uidisplay(Item *entry)
          246 {
          247         Item *items;
          248         Dir *dir;
          249         size_t i, curln, lastln, nitems, printoff;
          250 
          251         if (!entry ||
          252             !(entry->type == '1' || entry->type == '+' || entry->type == '7'))
          253                 return;
          254 
          255         curentry = entry;
          256 
          257         putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          258         displaystatus(entry);
          259 
          260         if (!(dir = entry->dat))
          261                 return;
          262 
          263         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          264 
          265         items = dir->items;
          266         nitems = dir->nitems;
          267         printoff = dir->printoff;
          268         curln = dir->curline;
          269         lastln = printoff + lines-1; /* one off for status bar */
          270 
          271         for (i = printoff; i < nitems && i < lastln; ++i) {
          272                 if (i != printoff)
          273                         putp(tparm(cursor_down, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          274                 if (i == curln) {
          275                         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          276                         putp(tparm(SELECTION));
          277                 }
          278                 printitem(&items[i]);
          279                 putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          280                 if (i == curln)
          281                 putp(tparm(RESET));
          282         }
          283 
          284         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          285         fflush(stdout);
          286 }
          287 
          288 static void
          289 movecurline(Item *item, int l)
          290 {
          291         Dir *dir = item->dat;
          292         size_t nitems;
          293         ssize_t curline, offline;
          294         int plines = lines-2;
          295 
          296         if (dir == NULL)
          297                 return;
          298 
          299         curline = dir->curline + l;
          300         nitems = dir->nitems;
          301         if (curline < 0 || curline >= nitems)
          302                 return;
          303 
          304         printitem(&dir->items[dir->curline]);
          305         dir->curline = curline;
          306 
          307         if (l > 0) {
          308                 offline = dir->printoff + lines-1;
          309                 if (curline - dir->printoff >= plines / 2 && offline < nitems) {
          310                         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          311 
          312                         putp(tparm(cursor_address, plines,
          313                                    0, 0, 0, 0, 0, 0, 0, 0));
          314                         putp(tparm(scroll_forward, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          315                         printitem(&dir->items[offline]);
          316 
          317                         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          318                         dir->printoff += l;
          319                 }
          320         } else {
          321                 offline = dir->printoff + l;
          322                 if (curline - offline <= plines / 2 && offline >= 0) {
          323                         putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          324 
          325                         putp(tparm(cursor_address, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          326                         putp(tparm(scroll_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          327                         printitem(&dir->items[offline]);
          328                         putchar('\n');
          329 
          330                         putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0));
          331                         dir->printoff += l;
          332                 }
          333         }
          334 
          335         putp(tparm(cursor_address, curline - dir->printoff,
          336                    0, 0, 0, 0, 0, 0, 0, 0));
          337         putp(tparm(SELECTION));
          338         printitem(&dir->items[curline]);
          339         putp(tparm(RESET));
          340         if (dir->items[dir->curline].type == 'i') {
          341                 displaystatus(item);
          342         } else {
          343                 displayuri(&dir->items[dir->curline]);
          344         }
          345         fflush(stdout);
          346 }
          347 
          348 static void
          349 jumptoline(Item *entry, ssize_t line, int absolute)
          350 {
          351         Dir *dir = entry->dat;
          352         size_t lastitem;
          353         int lastpagetop, plines = lines-2;
          354 
          355         if (!dir)
          356                 return;
          357         lastitem = dir->nitems-1;
          358 
          359         if (line < 0)
          360                 line = 0;
          361         if (line > lastitem)
          362                 line = lastitem;
          363 
          364         if (dir->curline == line)
          365                 return;
          366 
          367         if (lastitem <= plines) {              /* all items fit on one page */
          368                 dir->curline = line;
          369         } else if (line == 0) {                /* jump to top */
          370                 if (absolute || dir->curline > plines || dir->printoff == 0)
          371                         dir->curline = 0;
          372                 dir->printoff = 0;
          373         } else if (line + plines < lastitem) { /* jump before last page */
          374                 dir->curline = line;
          375                 dir->printoff = line;
          376         } else {                               /* jump within the last page */
          377                 lastpagetop = lastitem - plines;
          378                 if (dir->printoff == lastpagetop || absolute)
          379                         dir->curline = line;
          380                 else if (dir->curline < lastpagetop)
          381                         dir->curline = lastpagetop;
          382                 dir->printoff = lastpagetop;
          383         }
          384 
          385         uidisplay(entry);
          386         return;
          387 }
          388 
          389 void
          390 searchinline(const char *searchstr, Item *entry, int pos)
          391 {
          392         Dir *dir;
          393         int i;
          394 
          395         if (!searchstr || !(dir = entry->dat))
          396                 return;
          397 
          398         if (pos > 0) {
          399                 for (i = dir->curline + 1; i < dir->nitems; ++i) {
          400                         if (strcasestr(dir->items[i].username, searchstr)) {
          401                                 jumptoline(entry, i, 1);
          402                                 break;
          403                         }
          404                 }
          405         } else {
          406                 for (i = dir->curline - 1; i > -1; --i) {
          407                         if (strcasestr(dir->items[i].username, searchstr)) {
          408                                 jumptoline(entry, i, 1);
          409                                 break;
          410                         }
          411                 }
          412         }
          413 }
          414 
          415 static ssize_t
          416 nearentry(Item *entry, int direction)
          417 {
          418         Dir *dir = entry->dat;
          419         size_t item, lastitem;
          420 
          421         if (!dir)
          422                 return -1;
          423         lastitem = dir->nitems;
          424         item = dir->curline + direction;
          425 
          426         for (; item < lastitem; item += direction) {
          427                 if (dir->items[item].type != 'i')
          428                         return item;
          429         }
          430 
          431         return dir->curline;
          432 }
          433 
          434 Item *
          435 uiselectitem(Item *entry)
          436 {
          437         Dir *dir;
          438         char *searchstr = NULL;
          439         int plines = lines-2;
          440 
          441         if (!entry || !(dir = entry->dat))
          442                 return NULL;
          443 
          444         for (;;) {
          445                 switch (getchar()) {
          446                 case 0x1b: /* ESC */
          447                         switch (getchar()) {
          448                         case 0x1b:
          449                                 goto quit;
          450                         case '[':
          451                                 break;
          452                         default:
          453                                 continue;
          454                         }
          455                         switch (getchar()) {
          456                         case '4':
          457                                 if (getchar() != '~')
          458                                         continue;
          459                                 goto end;
          460                         case '5':
          461                                 if (getchar() != '~')
          462                                         continue;
          463                                 goto pgup;
          464                         case '6':
          465                                 if (getchar() != '~')
          466                                         continue;
          467                                 goto pgdown;
          468                         case 'A':
          469                                 goto lnup;
          470                         case 'B':
          471                                 goto lndown;
          472                         case 'C':
          473                                 goto pgnext;
          474                         case 'D':
          475                                 goto pgprev;
          476                         case 'H':
          477                                 goto home;
          478                         case 0x1b:
          479                                 goto quit;
          480                         }
          481                         continue;
          482                 case _key_pgprev:
          483                 pgprev:
          484                         return entry->entry;
          485                 case _key_pgnext:
          486                 case '\n':
          487                 pgnext:
          488                         if (dir)
          489                                 return &dir->items[dir->curline];
          490                         continue;
          491                 case _key_lndown:
          492                 lndown:
          493                         movecurline(entry, 1);
          494                         continue;
          495                 case _key_entrydown:
          496                         jumptoline(entry, nearentry(entry, 1), 1);
          497                         continue;
          498                 case _key_pgdown:
          499                 pgdown:
          500                         jumptoline(entry, dir->printoff + plines, 0);
          501                         continue;
          502                 case _key_end:
          503                 end:
          504                         jumptoline(entry, dir->nitems, 0);
          505                         continue;
          506                 case _key_lnup:
          507                 lnup:
          508                         movecurline(entry, -1);
          509                         continue;
          510                 case _key_entryup:
          511                         jumptoline(entry, nearentry(entry, -1), 1);
          512                         continue;
          513                 case _key_pgup:
          514                 pgup:
          515                         jumptoline(entry, dir->printoff - plines, 0);
          516                         continue;
          517                 case _key_home:
          518                 home:
          519                         jumptoline(entry, 0, 0);
          520                         continue;
          521                 case _key_search:
          522                 search:
          523                         free(searchstr);
          524                         if ((searchstr = uiprompt("Search for: ")) &&
          525                             searchstr[0])
          526                                 goto searchnext;
          527                         clear(&searchstr);
          528                         continue;
          529                 case _key_searchnext:
          530                 searchnext:
          531                         searchinline(searchstr, entry, +1);
          532                         continue;
          533                 case _key_searchprev:
          534                         searchinline(searchstr, entry, -1);
          535                         continue;
          536                 case 0x04:
          537                 case _key_quit:
          538                 quit:
          539                         return NULL;
          540                 case _key_fetch:
          541                 fetch:
          542                         if (entry->raw)
          543                                 continue;
          544                         return entry;
          545                 case _key_uri:
          546                         if (dir)
          547                                 displayuri(&dir->items[dir->curline]);
          548                         continue;
          549                 case _key_help: /* FALLTHROUGH */
          550                         return help(entry);
          551                 default:
          552                         continue;
          553                 }
          554         }
          555 }
          556 
          557 void
          558 uisigwinch(int signal)
          559 {
          560         Dir *dir;
          561 
          562         setupterm(NULL, 1, NULL);
          563         putp(tparm(change_scroll_region, 0, lines-2, 0, 0, 0, 0, 0, 0, 0));
          564 
          565         if (!curentry || !(dir = curentry->dat))
          566                 return;
          567 
          568         if (dir->curline - dir->printoff > lines-2)
          569                 dir->curline = dir->printoff + lines-2;
          570 
          571         uidisplay(curentry);
          572 }