Introduction
Introduction Statistics Contact Development Disclaimer Help
sacc.c - sacc - sacc - sacc(omys), simple console gopher client (config)
git clone git://git.codemadness.org/sacc
Log
Files
Refs
LICENSE
---
sacc.c (19147B)
---
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 "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_sock…
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, <empty> 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: …
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)) =…
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->…
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, typedispla…
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]: ", p…
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, typedispla…
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 }
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.