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