Gemini.c (3477B)
1 #include <u.h> 2 #include <libc.h> 3 #include <mp.h> 4 #include <libsec.h> 5 #include <bio.h> 6 7 #include "config.h" 8 9 #define BSIZE 4096 10 11 char *host; 12 char * gethost(char *uri); 13 void printheader(char *line); 14 void printlink(char *); 15 void getbody(char *header, Biobuf *bfd); 16 17 void 18 usage(void) 19 { 20 fprint(2, "usage: %s [-h host] uri\n", argv0); 21 exits("usage"); 22 } 23 24 void 25 main(int argc, char **argv) 26 { 27 char *header, *uri; 28 long n; 29 int fd, tlsfd; 30 Biobuf bfd; 31 TLSconn conn; 32 33 host = nil; 34 35 ARGBEGIN { 36 case 'h': 37 host = EARGF(usage()); 38 break; 39 default: 40 usage(); 41 } ARGEND 42 43 if (argc != 1) usage(); 44 45 uri = argv[0]; 46 if (host == nil) host = gethost(uri); 47 if (host == nil) sysfatal("gethost: %r"); 48 49 fd = dial(netmkaddr(host, "tcp", "1965"), nil, nil, nil); 50 if (fd < 0) sysfatal("dial: %r"); 51 52 memset(&conn, 0, sizeof(conn)); 53 conn.serverName = host; 54 tlsfd = tlsClient(fd, &conn); 55 if (tlsfd < 0) sysfatal("tlsClient: %r"); 56 57 if (Binit(&bfd, tlsfd, OREAD) < 0) 58 sysfatal("Binit: %r"); 59 60 n = fprint(tlsfd, "%s\r\n", uri); 61 if (n < 0) sysfatal("fprint: %r"); 62 63 header = Brdstr(&bfd, '\n', 1); 64 n = Blinelen(&bfd); 65 if (n > 1024 + 2) 66 fprint(2, "warning: header too long\n"); 67 header[n-1] = '\0'; 68 69 enum { 70 SCInput = 10, 71 SCSensitiveInput = 11, 72 SCSuccess = 20, 73 SCRedirect = 30, 74 SCPermRedirect = 31, 75 }; 76 77 switch (atoi(header)) { 78 case SCInput: 79 case SCSensitiveInput: 80 // TODO:ask for input 81 print(".%s\n" "n\n" ".input is not supported\n" "n\n", header); 82 break; 83 case SCSuccess: 84 getbody(header, &bfd); 85 break; 86 case SCRedirect: 87 case SCPermRedirect: 88 print(".Redirect: \n" "l%s\n" ".%s\n" "n\n" "l\n", header + 3, header + 3); 89 break; 90 default: 91 // print header 92 print(".%s\n" "n\n", header); 93 } 94 95 Bterm(&bfd); 96 close(tlsfd); 97 close(fd); 98 } 99 100 101 char * 102 gethost(char *uri) 103 { 104 char *host, *sp, *ep; 105 long length; 106 107 sp = strstr(uri, "://"); 108 if (sp == nil) { 109 werrstr("missing scheme:// in '%s'", uri); 110 return nil; 111 } 112 else sp += 3; 113 114 ep = strchr(sp, '/'); 115 if (ep == nil) ep = sp + strlen(sp); 116 length = ep - sp; 117 host = mallocz(sizeof(char) * (length+1), 1); 118 memcpy(host, sp, length); 119 return host; 120 } 121 122 void 123 getbody(char *header, Biobuf *bfd) 124 { 125 char *line; 126 int preform = 0; 127 128 if (strncmp(header + 3, "text", 4) == 0) { 129 130 while ((line = Brdstr(bfd, '\n', 1)) != nil) { 131 long n = Blinelen(bfd); 132 if (line[n-1] == '\r') line[n-1] = '\0'; 133 if (line[0] == '#') printheader(line); 134 else if (strncmp(line, "```", 3) == 0) { 135 preform = !preform; 136 if (preform == 1) print("%s", fonts[Fcode]); 137 else print("f\n"); 138 } else if (strncmp(line, "=>", 2) == 0) { 139 printlink(line); 140 } else { 141 print(".%s\n" "n\n", line); 142 } 143 free(line); 144 } 145 if (preform == 1) print("f\n"); 146 147 } else { 148 print(".%s\n" "n\n", header); 149 } 150 } 151 152 void 153 printheader(char *line) 154 { 155 int i = 0; 156 while (line[i] == '#') i++; 157 char *font = (i > 3) ? fonts[Fheader3] : fonts[Fheader1 + i - 1]; 158 while ((line[i] == ' ') | (line[i] == '\t')) i++; 159 print("f%s\n" ".%s\n" "n\n" "f\n", font, line + i); 160 } 161 162 void 163 printlink(char *line) 164 { 165 char *p, phost[1024]; 166 167 for (p = line + 2; (*p == ' ') || (*p == '\t'); p++) if (*p == '\0') return; 168 line = strpbrk(p, " \t"); 169 170 if (line != nil) { 171 *line = '\0'; 172 line++; 173 for (; (*line == ' ') || (*line == '\t'); line++); 174 } else line = p; 175 176 phost[0] = '\0'; 177 if (strstr(p, "://") == nil) { 178 strcat(phost, "gemini://"); 179 strcat(phost, host); 180 if (p[0] != '/') strcat(phost, "/"); 181 } 182 183 print("l%s%s\n" ".%s\n" "l\n" "n\n", phost, p, line); 184 }