| ---
sacc.c (19147B)
---
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': /* fallthrough */
309 if (!strncmp(item->selector, "URL:", 4)) {
310 n = snprintf(buf, bsz, "%s", item->selector+4);
311 break;
312 }
313 default:
314 n = snprintf(buf, bsz, "gopher://%s", item->host);
315
316 if (n < bsz-1 && strcmp(item->port, "70"))
317 n += snprintf(buf+n, bsz-n, ":%s", item->port);
318 if (n < bsz-1) {
319 n += snprintf(buf+n, bsz-n, "/%c%s",
320 item->type, item->selector);
321 }
322 if (n < bsz-1 && item->type == '7' && item->tag) {
323 n += snprintf(buf+n, bsz-n, "%%09%s",
324 item->tag + strlen(item->selector));
325 }
326 break;
327 }
328
329 return n;
330 }
331
332 static void
333 printdir(Item *item)
334 {
335 Dir *dir;
336 Item *items;
337 size_t i, nitems;
338
339 if (!item || !(dir = item->dat))
340 return;
341
342 items = dir->items;
343 nitems = dir->nitems;
344
345 for (i = 0; i < nitems; ++i) {
346 printf("%s%s\n",
347 typedisplay(items[i].type), items[i].username);
348 }
349 }
350
351 static void
352 displaytextitem(Item *item)
353 {
354 struct sigaction sa;
355 FILE *pagerin;
356 int pid, wpid;
357
358 sigemptyset(&sa.sa_mask);
359 sa.sa_flags = SA_RESTART;
360 sa.sa_handler = SIG_DFL;
361 sigaction(SIGWINCH, &sa, NULL);
362
363 uicleanup();
364
365 switch (pid = fork()) {
366 case -1:
367 diag("Couldn't fork.");
368 return;
369 case 0:
370 parent = 0;
371 if (!(pagerin = popen("$PAGER", "w")))
372 _exit(1);
373 fputs(item->raw, pagerin);
374 exit(pclose(pagerin));
375 default:
376 while ((wpid = wait(NULL)) >= 0 && wpid != pid)
377 ;
378 }
379 uisetup();
380
381 sa.sa_handler = uisigwinch;
382 sigaction(SIGWINCH, &sa, NULL);
383 uisigwinch(SIGWINCH); /* force redraw */
384 }
385
386 static char *
387 pickfield(char **raw, const char *sep)
388 {
389 char c, *r, *f = *raw;
390
391 for (r = *raw; (c = *r) && !strchr(sep, c); ++r) {
392 if (c == '\n')
393 goto skipsep;
394 }
395
396 *r++ = '\0';
397 skipsep:
398 *raw = r;
399
400 return f;
401 }
402
403 static char *
404 invaliditem(char *raw)
405 {
406 char c;
407 int tabs;
408
409 for (tabs = 0; (c = *raw) && c != '\n'; ++raw) {
410 if (c == '\t')
411 ++tabs;
412 }
413 if (tabs < 3) {
414 *raw++ = '\0';
415 return raw;
416 }
417
418 return NULL;
419 }
420
421 static void
422 molditem(Item *item, char **raw)
423 {
424 char *next;
425
426 if (!*raw)
427 return;
428
429 if ((next = invaliditem(*raw))) {
430 item->username = *raw;
431 *raw = next;
432 return;
433 }
434
435 item->type = *raw[0]++;
436 item->username = pickfield(raw, "\t");
437 item->selector = pickfield(raw, "\t");
438 item->host = pickfield(raw, "\t");
439 item->port = pickfield(raw, "\t\r");
440 while (*raw[0] != '\n')
441 ++*raw;
442 *raw[0]++ = '\0';
443 }
444
445 static Dir *
446 molddiritem(char *raw)
447 {
448 Item *item, *items = NULL;
449 char *nl, *p;
450 Dir *dir;
451 size_t i, n, nitems;
452
453 for (nl = raw, nitems = 0; p = strchr(nl, '\n'); nl = p+1)
454 ++nitems;
455
456 if (!nitems) {
457 diag("Couldn't parse dir item");
458 return NULL;
459 }
460
461 dir = xmalloc(sizeof(Dir));
462 items = xreallocarray(items, nitems, sizeof(Item));
463 memset(items, 0, nitems * sizeof(Item));
464
465 for (i = 0; i < nitems; ++i) {
466 item = &items[i];
467 molditem(item, &raw);
468 if (item->type == '+') {
469 for (n = i - 1; n < (size_t)-1; --n) {
470 if (items[n].type != '+') {
471 item->redtype = items[n].type;
472 break;
473 }
474 }
475 }
476 }
477
478 dir->items = items;
479 dir->nitems = nitems;
480 dir->printoff = dir->curline = 0;
481
482 return dir;
483 }
484
485 static char *
486 getrawitem(struct cnx *c)
487 {
488 char *raw, *buf;
489 size_t bn, bs;
490 ssize_t n;
491
492 raw = buf = NULL;
493 bn = bs = n = 0;
494
495 do {
496 bs -= n;
497 buf += n;
498
499 if (buf - raw >= 5) {
500 if (strncmp(buf-5, "\r\n.\r\n", 5) == 0) {
501 buf[-3] = '\0';
502 break;
503 }
504 } else if (buf - raw == 3 && strncmp(buf-3, ".\r\n", 3)) {
505 buf[-3] = '\0';
506 break;
507 }
508
509 if (bs < 1) {
510 raw = xreallocarray(raw, ++bn, BUFSIZ);
511 buf = raw + (bn-1) * BUFSIZ;
512 bs = BUFSIZ;
513 }
514
515 } while ((n = ioread(c, buf, bs)) > 0);
516
517 *buf = '\0';
518
519 if (n == -1) {
520 diag("Can't read socket: %s", strerror(errno));
521 clear(&raw);
522 }
523
524 return raw;
525 }
526
527 static int
528 sendselector(struct cnx *c, const char *selector)
529 {
530 char *msg, *p;
531 size_t ln;
532 ssize_t n;
533
534 ln = strlen(selector) + 3;
535 msg = p = xmalloc(ln);
536 snprintf(msg, ln--, "%s\r\n", selector);
537
538 while (ln && (n = iowrite(c, p, ln)) > 0) {
539 ln -= n;
540 p += n;
541 }
542
543 free(msg);
544 if (n == -1)
545 diag("Can't send message: %s", strerror(errno));
546
547 return n;
548 }
549
550 static int
551 connectto(const char *host, const char *port, struct cnx *c)
552 {
553 sigset_t set, oset;
554 static const struct addrinfo hints = {
555 .ai_family = AF_UNSPEC,
556 .ai_socktype = SOCK_STREAM,
557 .ai_protocol = IPPROTO_TCP,
558 };
559 struct addrinfo *addrs, *ai;
560 int r, err;
561
562 sigemptyset(&set);
563 sigaddset(&set, SIGWINCH);
564 sigprocmask(SIG_BLOCK, &set, &oset);
565
566 if (r = getaddrinfo(host, port, &hints, &addrs)) {
567 diag("Can't resolve hostname \"%s\": %s",
568 host, gai_strerror(r));
569 goto err;
570 }
571
572 r = -1;
573 for (ai = addrs; ai && r == -1; ai = ai->ai_next) {
574 do {
575 if ((c->sock = socket(ai->ai_family, ai->ai_socktype,
576 ai->ai_protocol)) == -1) {
577 err = errno;
578 break;
579 }
580
581 if ((r = ioconnect(c, ai, host)) < 0) {
582 err = errno;
583 ioclose(c);
584 }
585 } while (r == CONN_RETRY);
586 }
587
588 freeaddrinfo(addrs);
589
590 if (r == CONN_ERROR)
591 ioconnerr(c, host, port, err);
592 err:
593 sigprocmask(SIG_SETMASK, &oset, NULL);
594
595 return r;
596 }
597
598 static int
599 download(Item *item, int dest)
600 {
601 char buf[BUFSIZ];
602 struct cnx c = { 0 };
603 ssize_t r, w;
604
605 if (item->tag == NULL) {
606 if (connectto(item->host, item->port, &c) < 0 ||
607 sendselector(&c, item->selector) == -1)
608 return 0;
609 } else {
610 if ((c.sock = open(item->tag, O_RDONLY)) == -1) {
611 printf("Can't open source file %s: %s",
612 item->tag, strerror(errno));
613 errno = 0;
614 return 0;
615 }
616 }
617
618 w = 0;
619 while ((r = ioread(&c, buf, BUFSIZ)) > 0) {
620 while ((w = write(dest, buf, r)) > 0)
621 r -= w;
622 }
623
624 if (r == -1 || w == -1) {
625 printf("Error downloading file %s: %s",
626 item->selector, strerror(errno));
627 errno = 0;
628 }
629
630 close(dest);
631 ioclose(&c);
632
633 return (r == 0 && w == 0);
634 }
635
636 static void
637 downloaditem(Item *item)
638 {
639 char *file, *path, *tag;
640 mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
641 int dest;
642
643 if (file = strrchr(item->selector, '/'))
644 ++file;
645 else
646 file = item->selector;
647
648 if (!(path = uiprompt("Download to [%s] (^D cancel): ", file)))
649 return;
650
651 if (!path[0])
652 path = xstrdup(file);
653
654 if (tag = item->tag) {
655 if (access(tag, R_OK) == -1) {
656 clear(&item->tag);
657 } else if (!strcmp(tag, path)) {
658 goto cleanup;
659 }
660 }
661
662 if ((dest = open(path, O_WRONLY|O_CREAT|O_EXCL, mode)) == -1) {
663 diag("Can't open destination file %s: %s",
664 path, strerror(errno));
665 errno = 0;
666 goto cleanup;
667 }
668
669 if (!download(item, dest))
670 goto cleanup;
671
672 if (item->tag)
673 goto cleanup;
674
675 item->tag = path;
676
677 return;
678 cleanup:
679 free(path);
680 return;
681 }
682
683 static int
684 fetchitem(Item *item)
685 {
686 struct cnx c;
687 char *raw;
688
689 if (connectto(item->host, item->port, &c) < 0 ||
690 sendselector(&c, item->selector) == -1)
691 return 0;
692
693 raw = getrawitem(&c);
694 ioclose(&c);
695
696 if (raw == NULL || !*raw) {
697 diag("Empty response from server");
698 clear(&raw);
699 }
700
701 return ((item->raw = raw) != NULL);
702 }
703
704 static void
705 pipeuri(char *cmd, char *msg, char *uri)
706 {
707 FILE *sel;
708
709 if ((sel = popen(cmd, "w")) == NULL) {
710 diag("URI not %s\n", msg);
711 return;
712 }
713
714 fputs(uri, sel);
715 pclose(sel);
716 diag("%s \"%s\"", msg, uri);
717 }
718
719 static void
720 execuri(char *cmd, char *msg, char *uri)
721 {
722 switch (fork()) {
723 case -1:
724 diag("Couldn't fork.");
725 return;
726 case 0:
727 parent = 0;
728 dup2(devnullfd, 1);
729 dup2(devnullfd, 2);
730 if (execlp(cmd, cmd, uri, NULL) == -1)
731 _exit(1);
732 default:
733 if (modalplumber) {
734 while (waitpid(-1, NULL, 0) != -1)
735 ;
736 }
737 }
738
739 diag("%s \"%s\"", msg, uri);
740 }
741
742 static void
743 plumbitem(Item *item)
744 {
745 char *file, *path, *tag;
746 mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP;
747 int dest, plumbitem;
748
749 if (file = strrchr(item->selector, '/'))
750 ++file;
751 else
752 file = item->selector;
753
754 path = uiprompt("Download %s to (^D cancel, plumb): ",
755 file);
756 if (!path)
757 return;
758
759 if ((tag = item->tag) && access(tag, R_OK) == -1) {
760 clear(&item->tag);
761 tag = NULL;
762 }
763
764 plumbitem = path[0] ? 0 : 1;
765
766 if (!path[0]) {
767 clear(&path);
768 if (!tag) {
769 if (asprintf(&path, "%s/%s", tmpdir, file) == -1)
770 die("Can't generate tmpdir path: %s/%s: %s",
771 tmpdir, file, strerror(errno));
772 }
773 }
774
775 if (path && (!tag || strcmp(tag, path))) {
776 if ((dest = open(path, O_WRONLY|O_CREAT|O_EXCL, mode)) == -1) {
777 diag("Can't open destination file %s: %s",
778 path, strerror(errno));
779 errno = 0;
780 goto cleanup;
781 }
782 if (!download(item, dest) || tag)
783 goto cleanup;
784 }
785
786 if (!tag)
787 item->tag = path;
788
789 if (plumbitem)
790 execuri(plumber, "Plumbed", item->tag);
791
792 return;
793 cleanup:
794 free(path);
795 return;
796 }
797
798 void
799 yankitem(Item *item)
800 {
801 if (item->type == 0)
802 return;
803
804 itemuri(item, intbuf, sizeof(intbuf));
805 pipeuri(yanker, "Yanked", intbuf);
806 }
807
808 static int
809 dig(Item *entry, Item *item)
810 {
811 char *plumburi = NULL;
812 int t;
813
814 if (item->raw) /* already in cache */
815 return item->type;
816 if (!item->entry)
817 item->entry = entry ? entry : item;
818
819 t = item->redtype ? item->redtype : item->type;
820 switch (t) {
821 case 'h': /* fallthrough */
822 if (!strncmp(item->selector, "URL:", 4)) {
823 execuri(plumber, "Plumbed", item->selector+4);
824 return 0;
825 }
826 case '0':
827 if (!fetchitem(item))
828 return 0;
829 break;
830 case '1':
831 case '7':
832 if (!fetchitem(item) || !(item->dat = molddiritem(item->raw)))
833 return 0;
834 break;
835 case '4':
836 case '5':
837 case '6':
838 case '9':
839 downloaditem(item);
840 return 0;
841 case '8':
842 if (asprintf(&plumburi, "telnet://%s%s%s:%s",
843 item->selector, item->selector ? "@" : "",
844 item->host, item->port) == -1)
845 return 0;
846 execuri(plumber, "Plumbed", plumburi);
847 free(plumburi);
848 return 0;
849 case 'T':
850 if (asprintf(&plumburi, "tn3270://%s%s%s:%s",
851 item->selector, item->selector ? "@" : "",
852 item->host, item->port) == -1)
853 return 0;
854 execuri(plumber, "Plumbed", plumburi);
855 free(plumburi);
856 return 0;
857 default:
858 if (t >= '0' && t <= 'Z') {
859 diag("Type %c (%s) not supported", t, typedisplay(t));
860 return 0;
861 }
862 case 'g':
863 case 'I':
864 plumbitem(item);
865 case 'i':
866 return 0;
867 }
868
869 return item->type;
870 }
871
872 static char *
873 searchselector(Item *item)
874 {
875 char *pexp, *exp, *tag, *selector = item->selector;
876 size_t n = strlen(selector);
877
878 if ((tag = item->tag) && !strncmp(tag, selector, n))
879 pexp = tag + n+1;
880 else
881 pexp = "";
882
883 if (!(exp = uiprompt("Enter search string (^D cancel) [%s]: ", pexp)))
884 return NULL;
885
886 if (exp[0] && strcmp(exp, pexp)) {
887 n += strlen(exp) + 2;
888 tag = xmalloc(n);
889 snprintf(tag, n, "%s\t%s", selector, exp);
890 }
891
892 free(exp);
893 return tag;
894 }
895
896 static int
897 searchitem(Item *entry, Item *item)
898 {
899 char *sel, *selector;
900
901 if (!(sel = searchselector(item)))
902 return 0;
903
904 if (sel != item->tag)
905 clearitem(item);
906 if (!item->dat) {
907 selector = item->selector;
908 item->selector = item->tag = sel;
909 dig(entry, item);
910 item->selector = selector;
911 }
912 return (item->dat != NULL);
913 }
914
915 static void
916 printout(Item *hole)
917 {
918 char t = 0;
919
920 if (!hole)
921 return;
922
923 switch (hole->redtype ? hole->redtype : (t = hole->type)) {
924 case '0':
925 if (dig(hole, hole))
926 fputs(hole->raw, stdout);
927 return;
928 case '1':
929 case '7':
930 if (dig(hole, hole))
931 printdir(hole);
932 return;
933 default:
934 if (t >= '0' && t <= 'Z') {
935 diag("Type %c (%s) not supported", t, typedisplay(t));
936 return;
937 }
938 case '4':
939 case '5':
940 case '6':
941 case '9':
942 case 'g':
943 case 'I':
944 download(hole, 1);
945 case '2':
946 case '3':
947 case '8':
948 case 'T':
949 return;
950 }
951 }
952
953 static void
954 delve(Item *hole)
955 {
956 Item *entry = NULL;
957
958 while (hole) {
959 switch (hole->redtype ? hole->redtype : hole->type) {
960 case 'h':
961 case '0':
962 if (dig(entry, hole))
963 displaytextitem(hole);
964 break;
965 case '1':
966 case '+':
967 if (dig(entry, hole) && hole->dat)
968 entry = hole;
969 break;
970 case '7':
971 if (searchitem(entry, hole))
972 entry = hole;
973 break;
974 case 0:
975 diag("Couldn't get %s", hole->username);
976 break;
977 case '4':
978 case '5':
979 case '6': /* TODO decode? */
980 case '8':
981 case '9':
982 case 'g':
983 case 'I':
984 case 'T':
985 default:
986 dig(entry, hole);
987 break;
988 }
989
990 if (!entry)
991 return;
992
993 do {
994 uidisplay(entry);
995 hole = uiselectitem(entry);
996 } while (hole == entry);
997 }
998 }
999
1000 static Item *
1001 moldentry(char *url)
1002 {
1003 Item *entry;
1004 char *p, *host = url, *port = "70", *gopherpath = "1";
1005 int parsed, ipv6;
1006
1007 host = ioparseurl(url);
1008
1009 if (*host == '[') {
1010 ipv6 = 1;
1011 ++host;
1012 } else {
1013 ipv6 = 0;
1014 }
1015
1016 for (parsed = 0, p = host; !parsed && *p; ++p) {
1017 switch (*p) {
1018 case ']':
1019 if (ipv6) {
1020 *p = '\0';
1021 ipv6 = 0;
1022 }
1023 continue;
1024 case ':':
1025 if (!ipv6) {
1026 *p = '\0';
1027 port = p+1;
1028 }
1029 continue;
1030 case '/':
1031 *p = '\0';
1032 parsed = 1;
1033 continue;
1034 }
1035 }
1036
1037 if (*host == '\0' || *port == '\0' || ipv6)
1038 die("Can't parse url");
1039
1040 if (*p != '\0')
1041 gopherpath = p;
1042
1043 entry = xcalloc(sizeof(Item));
1044 entry->type = gopherpath[0];
1045 entry->username = entry->selector = ++gopherpath;
1046 if (entry->type == '7') {
1047 if (p = strstr(gopherpath, "%09")) {
1048 memmove(p+1, p+3, strlen(p+3)+1);
1049 *p = '\t';
1050 }
1051 if (p || (p = strchr(gopherpath, '\t'))) {
1052 asprintf(&entry->tag, "%s", gopherpath);
1053 *p = '\0';
1054 }
1055 }
1056 entry->host = host;
1057 entry->port = port;
1058 entry->entry = entry;
1059
1060 return entry;
1061 }
1062
1063 static void
1064 cleanup(void)
1065 {
1066 clearitem(mainentry);
1067 if (parent)
1068 rmdir(tmpdir);
1069 free(mainentry);
1070 free(mainurl);
1071 if (interactive)
1072 uicleanup();
1073 }
1074
1075 static void
1076 sighandler(int signo)
1077 {
1078 exit(128 + signo);
1079 }
1080
1081 static void
1082 setup(void)
1083 {
1084 struct sigaction sa;
1085 int fd;
1086
1087 setlocale(LC_CTYPE, "");
1088 setenv("PAGER", "more", 0);
1089 atexit(cleanup);
1090 /* reopen stdin in case we're reading from a pipe */
1091 if ((fd = open("/dev/tty", O_RDONLY)) == -1)
1092 die("open: /dev/tty: %s", strerror(errno));
1093 if (dup2(fd, 0) == -1)
1094 die("dup2: /dev/tty, stdin: %s", strerror(errno));
1095 close(fd);
1096 if ((devnullfd = open("/dev/null", O_WRONLY)) == -1)
1097 die("open: /dev/null: %s", strerror(errno));
1098
1099 sigemptyset(&sa.sa_mask);
1100 sa.sa_flags = SA_RESTART;
1101 sa.sa_handler = sighandler;
1102 sigaction(SIGINT, &sa, NULL);
1103 sigaction(SIGHUP, &sa, NULL);
1104 sigaction(SIGTERM, &sa, NULL);
1105
1106 sa.sa_handler = SIG_IGN;
1107 sigaction(SIGCHLD, &sa, NULL);
1108
1109 if (!mkdtemp(tmpdir))
1110 die("mkdir: %s: %s", tmpdir, strerror(errno));
1111 if (interactive = isatty(1)) {
1112 uisetup();
1113 diag = uistatus;
1114 sa.sa_handler = uisigwinch;
1115 sigaction(SIGWINCH, &sa, NULL);
1116 } else {
1117 diag = stddiag;
1118 }
1119 iosetup();
1120 }
1121
1122 int
1123 main(int argc, char *argv[])
1124 {
1125 if (argc != 2)
1126 usage();
1127
1128 setup();
1129
1130 mainurl = xstrdup(argv[1]);
1131 mainentry = moldentry(mainurl);
1132
1133 if (interactive)
1134 delve(mainentry);
1135 else
1136 printout(mainentry);
1137
1138 exit(0);
1139 } |