main.c (6078B)
1 /* Copyright 2020, 2021 Jacob R. Edwards 2 * 3 * This file is part of gawk. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19 #include <dirent.h> 20 #include <errno.h> 21 #include <signal.h> 22 #include <stdlib.h> 23 #include <unistd.h> 24 #include <string.h> 25 26 #include "command.h" 27 #include "gawk.h" 28 #include "net.h" 29 #include "util.h" 30 #include "config.h" /* must be included last */ 31 32 static char tmpdir[] = "/tmp/gawk-XXXXXXXXXXX"; 33 static int fatal; 34 35 void 36 putprompt(char const *prompt) 37 { 38 if (isatty(STDIN_FILENO)) 39 fputs(prompt, stderr); 40 else if (errno == ENOTTY) 41 errno = 0; 42 } 43 44 int 45 input(char *buf, int size, char const *delims, char const *prompt, FILE *fp) 46 { 47 int next; 48 static unsigned int len; 49 static char bb[MY_INPUT_MAX]; 50 51 if (len <= 0) { 52 putprompt(prompt); 53 refill_bufbuf: 54 if (fgets(bb + len, sizeof(bb) - len, fp) == NULL) 55 return 1; 56 len = strlen(bb); 57 } 58 59 next = strcspn(bb, delims); 60 if (bb[next] == '\0') { 61 if (len >= sizeof(buf) - 1) { 62 too_big: 63 warn(0, "input too large"); 64 return 1; 65 } 66 goto refill_bufbuf; 67 } 68 if (next >= size) 69 goto too_big; 70 71 bb[next++] = '\0'; 72 strlcpy(buf, bb, size); 73 memmove(bb, bb + next, len - next); 74 len -= next; 75 76 return 0; 77 } 78 79 int 80 runpipe(struct command *cmds, int *indexes, int count, char **item) 81 { 82 int i; 83 int r; 84 85 for (i = 0; i < count; ++i) { 86 r = cmds[i].f(cmds[i].argc, cmds[i].argv, indexes[i]++, item); 87 if (r != PASS && r != NEXT) 88 return r; 89 if (cmds[i].negate) 90 r = !r; 91 if (r) 92 return NEXT; 93 } 94 return PASS; 95 } 96 97 int 98 gphsplit(char **item, int size, char *buf) 99 { 100 int n; 101 102 n = argsplit(item, size, buf, "\t\r", 0) - 1; 103 if (n != 4 && (n < 4 || n > 5 || strcmp(item[GI_PLUS], "+") != 0)) 104 return ERROR; 105 return 0; 106 } 107 108 int 109 runpipes(char const *cache, struct command *cmds, int count) 110 { 111 FILE *fp; 112 char buf[MY_LINE_MAX]; 113 char *item[GI_NULL + 2]; 114 int error; 115 int indexes[count]; 116 117 memset(indexes, 0, sizeof(indexes)); 118 119 fp = wfopen(cache, "r"); 120 if (fp == NULL) 121 return UNWIND; 122 123 error = 0; 124 while (error != FAIL && fgets(buf, sizeof(buf), fp) && *buf != '.') 125 if (gphsplit(item, sizeof(item), buf) == 0) 126 error = runpipe(cmds, indexes, count, item); 127 wfclose(fp); 128 return error; 129 } 130 131 command * 132 getcom(char const *s, struct bind const *const binds, int size) 133 { 134 int i; 135 136 if (strlen(s) != 1) 137 return NULL; 138 for (i = 0; i < size; ++i) 139 if (*s == binds[i].c) 140 return binds[i].f; 141 return NULL; 142 } 143 144 int 145 execute(char const *cache, char *input, int depth, char **addr) 146 { 147 int i; 148 int nbufs; 149 char *bufs[MY_PIPE_MAX]; 150 struct command cmds[MY_PIPE_MAX]; 151 152 nbufs = argsplit(bufs, LEN(bufs), input, separators[1], 0); 153 if (nbufs < 0) { 154 warn(0, "pipeline too long"); 155 return ERROR; 156 } 157 158 for (i = 0; i < nbufs; ++i) { 159 cmds[i].argc = argsplit(cmds[i].argv, LEN(cmds[i].argv), 160 bufs[i], separators[2], 1); 161 if (cmds[i].argc < 1) { 162 if (cmds[i].argc < 0) 163 warn(0, "command %d: Too many arguments", i); 164 else if (nbufs > 1) 165 warn(0, "command %d: Empty command", i); 166 return ERROR; 167 } 168 if (cmds[i].argv[0][0] != negate) { 169 cmds[i].negate = 0; 170 } else { 171 cmds[i].negate = 1; 172 cmds[i].argv[0]++; 173 } 174 } 175 176 for (i = 0; i < nbufs; ++i) { 177 cmds[i].f = getcom(cmds[i].argv[0], itembinds, LEN(itembinds)); 178 if (cmds[i].f == NULL) { 179 if (i > 0) { 180 warn(0, "'%s': Not a pipeline command", 181 cmds[i].argv[0]); 182 return ERROR; 183 } 184 cmds[i].f = getcom(cmds[i].argv[0], binds, LEN(binds)); 185 if (cmds[i].f == NULL) { 186 warn(0, "'%s': Not a command", cmds[i].argv[0]); 187 return ERROR; 188 } 189 return cmds[i].f(cmds[i].argc, cmds[i].argv, depth, addr); 190 } 191 } 192 193 return runpipes(cache, cmds, nbufs); 194 } 195 196 void 197 tmp_mkpath(char *cache, char **addr) 198 { 199 char tmp[MY_PATH_MAX]; 200 201 tr(tmp, addr[AR_PATH], '/', '-'); 202 snprintf(cache, MY_PATH_MAX, "%s/%s_%s_%s", 203 tmpdir, addr[AR_HOST], addr[AR_PORT], tmp); 204 } 205 206 int 207 gawk(char **addr) 208 { 209 static int depth; 210 char cache[MY_PATH_MAX]; 211 char inbuf[MY_INPUT_MAX]; 212 char prompt[MY_PROMPT_MAX]; 213 int done; 214 int mycache; 215 216 tmp_mkpath(cache, addr); 217 if (exists(cache)) { 218 mycache = 0; 219 } else { 220 if (gopher(addr, cache, 0, timeout) == 1) 221 return ERROR; 222 mycache = 1; 223 } 224 225 ++depth; 226 227 snprintf(prompt, sizeof(prompt), "(%d) [%s] %s ", 228 depth, addr[AR_HOST], addr[AR_PATH]); 229 done = 0; 230 while (!fatal && done <= 0 && 231 input(inbuf, sizeof(inbuf), separators[0], prompt, stdin) == 0) { 232 done = execute(cache, inbuf, depth, addr); 233 } 234 235 if (mycache) 236 wremove(cache); 237 if (feof(stdin)) 238 done = depth; 239 else if (ferror(stdin) && !fatal) { 240 warn(errno, "stdin"); 241 return fatal = -1; 242 } 243 244 --depth; 245 return done - 1; 246 } 247 248 void 249 cleanup(void) 250 { 251 DIR *dir; 252 char path[MY_PATH_MAX]; 253 struct dirent *ent; 254 255 dir = opendir(tmpdir); 256 if (dir == NULL) 257 return; /* ENOTDIR */ 258 while ((ent = readdir(dir)) != NULL) { 259 if (ent->d_type == DT_REG) { 260 snprintf(path, sizeof(path), "%s/%s", tmpdir, ent->d_name); 261 wremove(path); 262 } 263 } 264 wremove(tmpdir); 265 } 266 267 void 268 sighandler(int sig) 269 { 270 cleanup(); 271 exit(EXIT_FAILURE); 272 } 273 274 int 275 main(int argc, char *argv[]) 276 { 277 int status; 278 279 if (argc > 1 && *argv[1] == '-') { 280 fprintf(stderr, "usage: %s [path [host [port]]]\n", getprogname()); 281 return 1; 282 } 283 284 #ifdef __OpenBSD__ 285 if (pledge("stdio rpath wpath cpath inet dns proc exec", NULL) == -1) 286 die(errno, "pledge"); 287 #endif /* __OpenBSD__ */ 288 289 signal(SIGCHLD, SIG_IGN); 290 signal(SIGINT, sighandler); 291 292 if (mkdtemp(tmpdir) == NULL) 293 die(errno, "mkdtemp"); 294 status = sgoto(argc, argv, 0, default_address); 295 if (wremove(tmpdir) == -1) 296 return 1; 297 298 return status; 299 }