t* sacc + cursorline and uri preview
       
   URI git clone git://git.codevoid.de/sacc-sdk
   DIR Log
   DIR Files
   DIR Refs
   DIR LICENSE
       ---
       tsacc.c (16731B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <ctype.h>
            3 #include <errno.h>
            4 #include <fcntl.h>
            5 #include <limits.h>
            6 #include <locale.h>
            7 #include <netdb.h>
            8 #include <netinet/in.h>
            9 #include <signal.h>
           10 #include <stdarg.h>
           11 #include <stdio.h>
           12 #include <stdlib.h>
           13 #include <string.h>
           14 #include <unistd.h>
           15 #include <wchar.h>
           16 #include <sys/socket.h>
           17 #include <sys/stat.h>
           18 #include <sys/types.h>
           19 #include <sys/wait.h>
           20 
           21 #include "common.h"
           22 
           23 #include "config.h"
           24 
           25 static char *mainurl;
           26 static Item *mainentry;
           27 static int devnullfd;
           28 static int parent = 1;
           29 static int interactive;
           30 
           31 static void (*diag)(char *fmt, ...);
           32 
           33 void
           34 stddiag(char *fmt, ...)
           35 {
           36         va_list arg;
           37 
           38         va_start(arg, fmt);
           39         vfprintf(stderr, fmt, arg);
           40         va_end(arg);
           41         fputc('\n', stderr);
           42 }
           43 
           44 void
           45 die(const char *fmt, ...)
           46 {
           47         va_list arg;
           48 
           49         va_start(arg, fmt);
           50         vfprintf(stderr, fmt, arg);
           51         va_end(arg);
           52         fputc('\n', stderr);
           53 
           54         exit(1);
           55 }
           56 
           57 #ifdef NEED_ASPRINTF
           58 int
           59 asprintf(char **s, const char *fmt, ...)
           60 {
           61         va_list ap;
           62         int n;
           63 
           64         va_start(ap, fmt);
           65         n = vsnprintf(NULL, 0, fmt, ap);
           66         va_end(ap);
           67 
           68         if (n == INT_MAX || !(*s = malloc(++n)))
           69                 return -1;
           70 
           71         va_start(ap, fmt);
           72         vsnprintf(*s, n, fmt, ap);
           73         va_end(ap);
           74 
           75         return n;
           76 }
           77 #endif /* NEED_ASPRINTF */
           78 
           79 #ifdef NEED_STRCASESTR
           80 char *
           81 strcasestr(const char *h, const char *n)
           82 {
           83         size_t i;
           84 
           85         if (!n[0])
           86                 return (char *)h;
           87 
           88         for (; *h; ++h) {
           89                 for (i = 0; n[i] && tolower(n[i]) == tolower(h[i]); ++i)
           90                         ;
           91                 if (n[i] == '\0')
           92                         return (char *)h;
           93         }
           94 
           95         return NULL;
           96 }
           97 #endif /* NEED_STRCASESTR */
           98 
           99 /* print `len' columns of characters. */
          100 size_t
          101 mbsprint(const char *s, size_t len)
          102 {
          103         wchar_t wc;
          104         size_t col = 0, i, slen;
          105         int rl, w;
          106 
          107         if (!len)
          108                 return col;
          109 
          110         slen = strlen(s);
          111         for (i = 0; i < slen; i += rl) {
          112                 if ((rl = mbtowc(&wc, s + i, slen - i < 4 ? slen - i : 4)) <= 0)
          113                         break;
          114                 if ((w = wcwidth(wc)) == -1)
          115                         continue;
          116                 if (col + w > len || (col + w == len && s[i + rl])) {
          117                         fputs("\xe2\x80\xa6", stdout);
          118                         col++;
          119                         break;
          120                 }
          121                 fwrite(s + i, 1, rl, stdout);
          122                 col += w;
          123         }
          124         return col;
          125 }
          126 
          127 static void *
          128 xreallocarray(void *m, const size_t n, const size_t s)
          129 {
          130         void *nm;
          131 
          132         if (n == 0 || s == 0) {
          133                 free(m);
          134                 return NULL;
          135         }
          136         if (s && n > (size_t)-1/s)
          137                 die("realloc: overflow");
          138         if (!(nm = realloc(m, n * s)))
          139                 die("realloc: %s", strerror(errno));
          140 
          141         return nm;
          142 }
          143 
          144 static void *
          145 xmalloc(const size_t n)
          146 {
          147         void *m = malloc(n);
          148 
          149         if (!m)
          150                 die("malloc: %s", strerror(errno));
          151 
          152         return m;
          153 }
          154 
          155 static void *
          156 xcalloc(size_t n)
          157 {
          158         char *m = calloc(1, n);
          159 
          160         if (!m)
          161                 die("calloc: %s", strerror(errno));
          162 
          163         return m;
          164 }
          165 
          166 static char *
          167 xstrdup(const char *str)
          168 {
          169         char *s;
          170 
          171         if (!(s = strdup(str)))
          172                 die("strdup: %s", strerror(errno));
          173 
          174         return s;
          175 }
          176 
          177 static void
          178 usage(void)
          179 {
          180         die("usage: sacc URL");
          181 }
          182 
          183 static void
          184 clearitem(Item *item)
          185 {
          186         Dir *dir;
          187         Item *items;
          188         char *tag;
          189         size_t i;
          190 
          191         if (!item)
          192                 return;
          193 
          194         if ((dir = item->dat)) {
          195                 items = dir->items;
          196                 for (i = 0; i < dir->nitems; ++i)
          197                         clearitem(&items[i]);
          198                 free(items);
          199                 clear(&item->dat);
          200         }
          201 
          202         if (parent && (tag = item->tag) &&
          203             !strncmp(tag, tmpdir, strlen(tmpdir)))
          204                 unlink(tag);
          205 
          206         clear(&item->tag);
          207         clear(&item->raw);
          208 }
          209 
          210 const char *
          211 typedisplay(char t)
          212 {
          213         switch (t) {
          214         case '0':
          215                 return "Text+";
          216         case '1':
          217                 return "Dir +";
          218         case '2':
          219                 return "CSO |";
          220         case '3':
          221                 return "Err |";
          222         case '4':
          223                 return "Macf+";
          224         case '5':
          225                 return "DOSf+";
          226         case '6':
          227                 return "UUEf+";
          228         case '7':
          229                 return "Find+";
          230         case '8':
          231                 return "Tlnt|";
          232         case '9':
          233                 return "Binf+";
          234         case '+':
          235                 return "Mirr+";
          236         case 'T':
          237                 return "IBMt|";
          238         case 'g':
          239                 return "GIF +";
          240         case 'I':
          241                 return "Img +";
          242         case 'h':
          243                 return "HTML+";
          244         case 'i':
          245                 return "    |";
          246         default:
          247                 /* "Characters '0' through 'Z' are reserved." (ASCII) */
          248                 if (t >= '0' && t <= 'Z')
          249                         return "!   |";
          250                 else
          251                         return "UNKN|";
          252         }
          253 }
          254 
          255 static void
          256 printdir(Item *item)
          257 {
          258         Dir *dir;
          259         Item *items;
          260         size_t i, nitems;
          261 
          262         if (!item || !(dir = item->dat))
          263                 return;
          264 
          265         items = dir->items;
          266         nitems = dir->nitems;
          267 
          268         for (i = 0; i < nitems; ++i) {
          269                 printf("%s%s\n",
          270                        typedisplay(items[i].type), items[i].username);
          271         }
          272 }
          273 
          274 static void
          275 displaytextitem(Item *item)
          276 {
          277         FILE *pagerin;
          278         int pid, wpid;
          279 
          280         uicleanup();
          281         switch (pid = fork()) {
          282         case -1:
          283                 diag("Couldn't fork.");
          284                 return;
          285         case 0:
          286                 parent = 0;
          287                 if (!(pagerin = popen("$PAGER", "we")))
          288                         _exit(1);
          289                 fputs(item->raw, pagerin);
          290                 exit(pclose(pagerin));
          291         default:
          292                 while ((wpid = wait(NULL)) >= 0 && wpid != pid)
          293                         ;
          294         }
          295         uisetup();
          296 }
          297 
          298 static char *
          299 pickfield(char **raw, const char *sep)
          300 {
          301         char c, *r, *f = *raw;
          302 
          303         for (r = *raw; (c = *r) && !strchr(sep, c); ++r) {
          304                 if (c == '\n')
          305                         goto skipsep;
          306         }
          307 
          308         *r++ = '\0';
          309 skipsep:
          310         *raw = r;
          311 
          312         return f;
          313 }
          314 
          315 static char *
          316 invaliditem(char *raw)
          317 {
          318         char c;
          319         int tabs;
          320 
          321         for (tabs = 0; (c = *raw) && c != '\n'; ++raw) {
          322                 if (c == '\t')
          323                         ++tabs;
          324         }
          325         if (tabs < 3) {
          326                 *raw++ = '\0';
          327                 return raw;
          328         }
          329 
          330         return NULL;
          331 }
          332 
          333 static void
          334 molditem(Item *item, char **raw)
          335 {
          336         char *next;
          337 
          338         if (!*raw)
          339                 return;
          340 
          341         if ((next = invaliditem(*raw))) {
          342                 item->username = *raw;
          343                 *raw = next;
          344                 return;
          345         }
          346 
          347         item->type = *raw[0]++;
          348         item->username = pickfield(raw, "\t");
          349         item->selector = pickfield(raw, "\t");
          350         item->host = pickfield(raw, "\t");
          351         item->port = pickfield(raw, "\t\r");
          352         while (*raw[0] != '\n')
          353                 ++*raw;
          354         *raw[0]++ = '\0';
          355 }
          356 
          357 static Dir *
          358 molddiritem(char *raw)
          359 {
          360         Item *item, *items = NULL;
          361         char *s, *nl, *p;
          362         Dir *dir;
          363         size_t i, n, nitems;
          364 
          365         for (s = nl = raw, nitems = 0; (p = strchr(nl, '\n')); ++nitems) {
          366                 s = nl;
          367                 nl = p+1;
          368         }
          369         if (!strcmp(s, ".\r\n") || !strcmp(s, ".\n"))
          370                 --nitems;
          371         if (!nitems) {
          372                 diag("Couldn't parse dir item");
          373                 return NULL;
          374         }
          375 
          376         dir = xmalloc(sizeof(Dir));
          377         items = xreallocarray(items, nitems, sizeof(Item));
          378         memset(items, 0, nitems * sizeof(Item));
          379 
          380         for (i = 0; i < nitems; ++i) {
          381                 item = &items[i];
          382                 molditem(item, &raw);
          383                 if (item->type == '+') {
          384                         for (n = i - 1; n < (size_t)-1; --n) {
          385                                 if (items[n].type != '+') {
          386                                         item->redtype = items[n].type;
          387                                         break;
          388                                 }
          389                         }
          390                 }
          391         }
          392 
          393         dir->items = items;
          394         dir->nitems = nitems;
          395         dir->printoff = dir->curline = 0;
          396 
          397         return dir;
          398 }
          399 
          400 static char *
          401 getrawitem(int sock)
          402 {
          403         char *raw, *buf;
          404         size_t bn, bs;
          405         ssize_t n;
          406 
          407         raw = buf = NULL;
          408         bn = bs = n = 0;
          409 
          410         do {
          411                 bs -= n;
          412                 buf += n;
          413                 if (bs < 1) {
          414                         raw = xreallocarray(raw, ++bn, BUFSIZ);
          415                         buf = raw + (bn-1) * BUFSIZ;
          416                         bs = BUFSIZ;
          417                 }
          418         } while ((n = read(sock, buf, bs)) > 0);
          419 
          420         *buf = '\0';
          421 
          422         if (n < 0) {
          423                 diag("Can't read socket: %s", strerror(errno));
          424                 clear(&raw);
          425         }
          426 
          427         return raw;
          428 }
          429 
          430 static int
          431 sendselector(int sock, const char *selector)
          432 {
          433         char *msg, *p;
          434         size_t ln;
          435         ssize_t n;
          436 
          437         ln = strlen(selector) + 3;
          438         msg = p = xmalloc(ln);
          439         snprintf(msg, ln--, "%s\r\n", selector);
          440 
          441         while ((n = write(sock, p, ln)) > 0) {
          442                 ln -= n;
          443                 p += n;
          444         }
          445 
          446         free(msg);
          447         if (n == -1)
          448                 diag("Can't send message: %s", strerror(errno));
          449 
          450         return n;
          451 }
          452 
          453 static int
          454 connectto(const char *host, const char *port)
          455 {
          456         static const struct addrinfo hints = {
          457             .ai_family = AF_UNSPEC,
          458             .ai_socktype = SOCK_STREAM,
          459             .ai_protocol = IPPROTO_TCP,
          460         };
          461         struct addrinfo *addrs, *addr;
          462         int sock, r;
          463 
          464         if ((r = getaddrinfo(host, port, &hints, &addrs))) {
          465                 diag("Can't resolve hostname \"%s\": %s",
          466                      host, gai_strerror(r));
          467                 return -1;
          468         }
          469 
          470         for (addr = addrs; addr; addr = addr->ai_next) {
          471                 if ((sock = socket(addr->ai_family, addr->ai_socktype,
          472                                    addr->ai_protocol)) < 0)
          473                         continue;
          474                 if ((r = connect(sock, addr->ai_addr, addr->ai_addrlen)) < 0) {
          475                         close(sock);
          476                         continue;
          477                 }
          478                 break;
          479         }
          480         if (sock < 0) {
          481                 diag("Can't open socket: %s", strerror(errno));
          482                 return -1;
          483         }
          484         if (r < 0) {
          485                 diag("Can't connect to: %s:%s: %s",
          486                      host, port, strerror(errno));
          487                 return -1;
          488         }
          489 
          490         freeaddrinfo(addrs);
          491 
          492         return sock;
          493 }
          494 
          495 static int
          496 download(Item *item, int dest)
          497 {
          498         char buf[BUFSIZ];
          499         ssize_t r, w;
          500         int src;
          501 
          502         if (!item->tag) {
          503                 if ((src = connectto(item->host, item->port)) < 0 ||
          504                     sendselector(src, item->selector) < 0)
          505                         return 0;
          506         } else if ((src = open(item->tag, O_RDONLY)) < 0) {
          507                 printf("Can't open source file %s: %s",
          508                        item->tag, strerror(errno));
          509                 errno = 0;
          510                 return 0;
          511         }
          512 
          513         w = 0;
          514         while ((r = read(src, buf, BUFSIZ)) > 0) {
          515                 while ((w = write(dest, buf, r)) > 0)
          516                         r -= w;
          517         }
          518 
          519         if (r < 0 || w < 0) {
          520                 printf("Error downloading file %s: %s",
          521                        item->selector, strerror(errno));
          522                 errno = 0;
          523         }
          524 
          525         close(src);
          526 
          527         return (r == 0 && w == 0);
          528 }
          529 
          530 static void
          531 downloaditem(Item *item)
          532 {
          533         char *file, *path, *tag;
          534         mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
          535         int dest;
          536 
          537         if ((file = strrchr(item->selector, '/')))
          538                 ++file;
          539         else
          540                 file = item->selector;
          541 
          542         if (!(path = uiprompt("Download to [%s] (^D cancel): ", file)))
          543                 return;
          544 
          545         if (!path[0])
          546                 path = xstrdup(file);
          547 
          548         if ((tag = item->tag)) {
          549                 if (access(tag, R_OK) < 0) {
          550                         clear(&item->tag);
          551                 } else if (!strcmp(tag, path)) {
          552                         goto cleanup;
          553                 }
          554         }
          555 
          556         if ((dest = open(path, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
          557                 diag("Can't open destination file %s: %s",
          558                      path, strerror(errno));
          559                 errno = 0;
          560                 goto cleanup;
          561         }
          562 
          563         if (!download(item, dest))
          564                 goto cleanup;
          565 
          566         if (!item->tag)
          567                 item->tag = path;
          568         return;
          569 cleanup:
          570         free(path);
          571         return;
          572 }
          573 
          574 static int
          575 fetchitem(Item *item)
          576 {
          577         int sock;
          578 
          579         if ((sock = connectto(item->host, item->port)) < 0 ||
          580             sendselector(sock, item->selector) < 0)
          581                 return 0;
          582         item->raw = getrawitem(sock);
          583         close(sock);
          584 
          585         if (item->raw && !*item->raw) {
          586                 diag("Empty response from server");
          587                 clear(&item->raw);
          588         }
          589 
          590         return (item->raw != NULL);
          591 }
          592 
          593 static void
          594 browse(char *url)
          595 {
          596         switch (fork()) {
          597         case -1:
          598                 diag("Couldn't fork.");
          599                 return;
          600         case 0:
          601                 parent = 0;
          602                 dup2(devnullfd, 1);
          603                 dup2(devnullfd, 2);
          604                 if (execlp(browser,browser, url, NULL) < 0)
          605                         _exit(1);
          606         }
          607 
          608         diag("Browsed \"%s\"", url);
          609 }
          610 
          611 static void
          612 plumb(char *url)
          613 {
          614         switch (fork()) {
          615         case -1:
          616                 diag("Couldn't fork.");
          617                 return;
          618         case 0:
          619                 parent = 0;
          620                 dup2(devnullfd, 1);
          621                 dup2(devnullfd, 2);
          622                 if (execlp(plumber, plumber, url, NULL) < 0)
          623                         _exit(1);
          624         }
          625 
          626         diag("Plumbed \"%s\"", url);
          627 }
          628 
          629 static void
          630 plumbitem(Item *item)
          631 {
          632         char *file, *path, *tag;
          633         mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
          634         int n, dest, plumbitem;
          635 
          636         if ((file = strrchr(item->selector, '/')))
          637                 ++file;
          638         else
          639                 file = item->selector;
          640 
          641         path = uiprompt("Download %s to (^D cancel, <empty> plumb): ",
          642                         file);
          643 
          644         if (!path)
          645                 return;
          646 
          647         if ((tag = item->tag) && access(tag, R_OK) < 0) {
          648                 clear(&item->tag);
          649                 tag = NULL;
          650         }
          651 
          652         plumbitem = path[0] ? 0 : 1;
          653 
          654         if (!path[0]) {
          655                 clear(&path);
          656                 if (!tag) {
          657                         if (asprintf(&path, "%s/%s", tmpdir, file) < 0)
          658                                 die("Can't generate tmpdir path: %s/%s: %s",
          659                                     tmpdir, file, strerror(errno));
          660                 }
          661         }
          662 
          663         if (path && (!tag || strcmp(tag, path))) {
          664                 if ((dest = open(path, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
          665                         diag("Can't open destination file %s: %s",
          666                              path, strerror(errno));
          667                         errno = 0;
          668                         goto cleanup;
          669                 }
          670                 if (!download(item, dest) || tag)
          671                         goto cleanup;
          672         }
          673 
          674         if (!tag)
          675                 item->tag = path;
          676 
          677         if (plumbitem)
          678                 plumb(item->tag);
          679 
          680         return;
          681 cleanup:
          682         free(path);
          683         return;
          684 }
          685 
          686 static int
          687 dig(Item *entry, Item *item)
          688 {
          689         char *plumburi = NULL;
          690         int t;
          691 
          692         if (item->raw) /* already in cache */
          693                 return item->type;
          694         if (!item->entry)
          695                 item->entry = entry ? entry : item;
          696 
          697         t = item->redtype ? item->redtype : item->type;
          698         switch (t) {
          699         case 'h': /* fallthrough */
          700                 if (!strncmp(item->selector, "URL:", 4)) {
          701                         browse(item->selector+4);
          702                         return 0;
          703                 }
          704         case '0':
          705                 if (!fetchitem(item))
          706                         return 0;
          707                 break;
          708         case '1':
          709         case '7':
          710                 if (!fetchitem(item) || !(item->dat = molddiritem(item->raw)))
          711                         return 0;
          712                 break;
          713         case '4':
          714         case '5':
          715         case '6':
          716         case '9':
          717                 downloaditem(item);
          718                 return 0;
          719         case '8':
          720                 if (asprintf(&plumburi, "telnet://%s%s%s:%s",
          721                              item->selector, item->selector ? "@" : "",
          722                              item->host, item->port) < 0)
          723                         return 0;
          724                 plumb(plumburi);
          725                 free(plumburi);
          726                 return 0;
          727         case 'T':
          728                 if (asprintf(&plumburi, "tn3270://%s%s%s:%s",
          729                              item->selector, item->selector ? "@" : "",
          730                              item->host, item->port) < 0)
          731                         return 0;
          732                 plumb(plumburi);
          733                 free(plumburi);
          734                 return 0;
          735         default:
          736                 if (t >= '0' && t <= 'Z') {
          737                         diag("Type %c (%s) not supported", t, typedisplay(t));
          738                         return 0;
          739                 }
          740         case 'g':
          741         case 'I':
          742                 plumbitem(item);
          743         case 'i':
          744                 return 0;
          745         }
          746 
          747         return item->type;
          748 }
          749 
          750 static char *
          751 searchselector(Item *item)
          752 {
          753         char *pexp, *exp, *tag, *selector = item->selector;
          754         size_t n = strlen(selector);
          755 
          756         if ((tag = item->tag) && !strncmp(tag, selector, n))
          757                 pexp = tag + n+1;
          758         else
          759                 pexp = "";
          760 
          761         if (!(exp = uiprompt("Enter search string (^D cancel) [%s]: ", pexp)))
          762                 return NULL;
          763 
          764         if (exp[0] && strcmp(exp, pexp)) {
          765                 n += strlen(exp) + 2;
          766                 tag = xmalloc(n);
          767                 snprintf(tag, n, "%s\t%s", selector, exp);
          768         }
          769 
          770         free(exp);
          771         return tag;
          772 }
          773 
          774 static int
          775 searchitem(Item *entry, Item *item)
          776 {
          777         char *sel, *selector;
          778 
          779         if (!(sel = searchselector(item)))
          780                 return 0;
          781 
          782         if (sel != item->tag)
          783                 clearitem(item);
          784         if (!item->dat) {
          785                 selector = item->selector;
          786                 item->selector = item->tag = sel;
          787                 dig(entry, item);
          788                 item->selector = selector;
          789         }
          790         return (item->dat != NULL);
          791 }
          792 
          793 static void
          794 printout(Item *hole)
          795 {
          796         char t;
          797 
          798         if (!hole)
          799                 return;
          800 
          801         switch (hole->redtype ? hole->redtype : (t = hole->type)) {
          802         case '0':
          803                 if (dig(hole, hole))
          804                         fputs(hole->raw, stdout);
          805                 return;
          806         case '1':
          807         case '7':
          808                 if (dig(hole, hole))
          809                         printdir(hole);
          810                 return;
          811         default:
          812                 if (t >= '0' && t <= 'Z') {
          813                         diag("Type %c (%s) not supported", t, typedisplay(t));
          814                         return;
          815                 }
          816         case '4':
          817         case '5':
          818         case '6':
          819         case '9':
          820         case 'g':
          821         case 'I':
          822                 download(hole, 1);
          823         case '2':
          824         case '3':
          825         case '8':
          826         case 'T':
          827                 return;
          828         }
          829 }
          830 
          831 static void
          832 delve(Item *hole)
          833 {
          834         Item *entry = NULL;
          835 
          836         while (hole) {
          837                 switch (hole->redtype ? hole->redtype : hole->type) {
          838                 case 'h':
          839                 case '0':
          840                         if (dig(entry, hole))
          841                                 displaytextitem(hole);
          842                         break;
          843                 case '1':
          844                 case '+':
          845                         if (dig(entry, hole) && hole->dat)
          846                                 entry = hole;
          847                         break;
          848                 case '7':
          849                         if (searchitem(entry, hole))
          850                                 entry = hole;
          851                         break;
          852                 case 0:
          853                         diag("Couldn't get %s:%s/%c%s", hole->host,
          854                              hole->port, hole->type, hole->selector);
          855                         break;
          856                 case '4':
          857                 case '5':
          858                 case '6': /* TODO decode? */
          859                 case '8':
          860                 case '9':
          861                 case 'g':
          862                 case 'I':
          863                 case 'T':
          864                 default:
          865                         dig(entry, hole);
          866                         break;
          867                 }
          868 
          869                 if (!entry)
          870                         return;
          871 
          872                 do {
          873                         uidisplay(entry);
          874                         hole = uiselectitem(entry);
          875                 } while (hole == entry);
          876         }
          877 }
          878 
          879 static Item *
          880 moldentry(char *url)
          881 {
          882         Item *entry;
          883         char *p, *host = url, *port = "70", *gopherpath = "1";
          884         int parsed, ipv6;
          885 
          886         if ((p = strstr(url, "://"))) {
          887                 if (strncmp(url, "gopher", p - url))
          888                         die("Protocol not supported: %.*s", p - url, url);
          889                 host = p + 3;
          890         }
          891 
          892         if (*host == '[') {
          893                 ipv6 = 1;
          894                 ++host;
          895         } else {
          896                 ipv6 = 0;
          897         }
          898 
          899         for (parsed = 0, p = host; !parsed && *p; ++p) {
          900                 switch (*p) {
          901                 case ']':
          902                         if (ipv6) {
          903                                 *p = '\0';
          904                                 ipv6 = 0;
          905                         }
          906                         continue;
          907                 case ':':
          908                         if (!ipv6) {
          909                                 *p = '\0';
          910                                 port = p+1;
          911                         }
          912                         continue;
          913                 case '/':
          914                         *p = '\0';
          915                         parsed = 1;
          916                         continue;
          917                 }
          918         }
          919 
          920         if (*host == '\0' || *port == '\0' || ipv6)
          921                 die("Can't parse url");
          922 
          923         if (*p != '\0')
          924                 gopherpath = p;
          925 
          926         entry = xcalloc(sizeof(Item));
          927         entry->type = gopherpath[0];
          928         entry->username = entry->selector = ++gopherpath;
          929         if (entry->type == '7') {
          930                 for (; *p; ++p) {
          931                         if (*p == '\t') {
          932                                 asprintf(&entry->tag, "%s", gopherpath);
          933                                 *p = '\0';
          934                                 break;
          935                         }
          936                 }
          937         }
          938         entry->host = host;
          939         entry->port = port;
          940         entry->entry = entry;
          941 
          942         return entry;
          943 }
          944 
          945 static void
          946 cleanup(void)
          947 {
          948         clearitem(mainentry);
          949         if (parent)
          950                 rmdir(tmpdir);
          951         free(mainentry);
          952         free(mainurl);
          953         if (interactive)
          954                 uicleanup();
          955 }
          956 
          957 static void
          958 setup(void)
          959 {
          960         struct sigaction sa;
          961         int fd;
          962 
          963         setlocale(LC_CTYPE, "");
          964         setenv("PAGER", "more", 0);
          965         atexit(cleanup);
          966         /* reopen stdin in case we're reading from a pipe */
          967         if ((fd = open("/dev/tty", O_RDONLY)) < 0)
          968                 die("open: /dev/tty: %s", strerror(errno));
          969         if (dup2(fd, 0) < 0)
          970                 die("dup2: /dev/tty, stdin: %s", strerror(errno));
          971         close(fd);
          972         if ((devnullfd = open("/dev/null", O_WRONLY)) < 0)
          973                 die("open: /dev/null: %s", strerror(errno));
          974 
          975         sigemptyset(&sa.sa_mask);
          976         sa.sa_flags = SA_RESTART;
          977         sa.sa_handler = exit;
          978         sigaction(SIGINT|SIGHUP, &sa, NULL);
          979 
          980         if (mkdir(tmpdir, S_IRWXU) < 0 && errno != EEXIST)
          981                 die("mkdir: %s: %s", tmpdir, strerror(errno));
          982         if((interactive = isatty(1))) {
          983                 uisetup();
          984                 sa.sa_handler = uisigwinch;
          985                 sigaction(SIGWINCH, &sa, NULL);
          986         }
          987 }
          988 
          989 int
          990 main(int argc, char *argv[])
          991 {
          992         if (argc != 2)
          993                 usage();
          994 
          995         setup();
          996 
          997         mainurl = xstrdup(argv[1]);
          998 
          999         mainentry = moldentry(mainurl);
         1000         if (interactive) {
         1001                 diag = uistatus;
         1002                 delve(mainentry);
         1003         } else {
         1004                 diag = stddiag;
         1005                 printout(mainentry);
         1006         }
         1007 
         1008         exit(0);
         1009 }