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