1 /* $OpenBSD: grep.c,v 1.50 2023/03/08 04:43:11 guenther Exp $ */ 2 3 /* This file is in the public domain */ 4 5 #include <sys/queue.h> 6 #include <sys/types.h> 7 #include <sys/wait.h> 8 9 #include <ctype.h> 10 #include <limits.h> 11 #include <signal.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <time.h> 16 #include <unistd.h> 17 18 #include "def.h" 19 #include "kbd.h" 20 #include "funmap.h" 21 22 int globalwd = FALSE; 23 static int compile_goto_error(int, int); 24 int next_error(int, int); 25 static int grep(int, int); 26 static int gid(int, int); 27 static struct buffer *compile_mode(const char *, const char *); 28 void grep_init(void); 29 30 static char compile_last_command[NFILEN] = "make "; 31 32 /* 33 * Hints for next-error 34 * 35 * XXX - need some kind of callback to find out when those get killed. 36 */ 37 struct mgwin *compile_win; 38 struct buffer *compile_buffer; 39 40 static PF compile_pf[] = { 41 compile_goto_error 42 }; 43 44 static struct KEYMAPE (1) compilemap = { 45 1, 46 1, 47 rescan, 48 { 49 { CCHR('M'), CCHR('M'), compile_pf, NULL } 50 } 51 }; 52 53 void 54 grep_init(void) 55 { 56 funmap_add(compile_goto_error, "compile-goto-error", 0); 57 funmap_add(next_error, "next-error", 0); 58 funmap_add(grep, "grep", 1); 59 funmap_add(compile, "compile", 0); 60 funmap_add(gid, "gid", 1); 61 maps_add((KEYMAP *)&compilemap, "compile"); 62 } 63 64 static int 65 grep(int f, int n) 66 { 67 char cprompt[NFILEN], *bufp; 68 struct buffer *bp; 69 struct mgwin *wp; 70 71 (void)strlcpy(cprompt, "grep -n ", sizeof(cprompt)); 72 if ((bufp = eread("Run grep: ", cprompt, NFILEN, 73 EFDEF | EFNEW | EFCR)) == NULL) 74 return (ABORT); 75 else if (bufp[0] == '\0') 76 return (FALSE); 77 if (strlcat(cprompt, " /dev/null", sizeof(cprompt)) >= sizeof(cprompt)) 78 return (FALSE); 79 80 if ((bp = compile_mode("*grep*", cprompt)) == NULL) 81 return (FALSE); 82 if ((wp = popbuf(bp, WNONE)) == NULL) 83 return (FALSE); 84 curbp = bp; 85 compile_win = curwp = wp; 86 return (TRUE); 87 } 88 89 int 90 compile(int f, int n) 91 { 92 char cprompt[NFILEN], *bufp; 93 struct buffer *bp; 94 struct mgwin *wp; 95 96 (void)strlcpy(cprompt, compile_last_command, sizeof(cprompt)); 97 if ((bufp = eread("Compile command: ", cprompt, NFILEN, 98 EFDEF | EFNEW | EFCR)) == NULL) 99 return (ABORT); 100 else if (bufp[0] == '\0') 101 return (FALSE); 102 if (savebuffers(f, n) == ABORT) 103 return (ABORT); 104 (void)strlcpy(compile_last_command, bufp, sizeof(compile_last_command)); 105 106 if ((bp = compile_mode("*compile*", cprompt)) == NULL) 107 return (FALSE); 108 if ((wp = popbuf(bp, WNONE)) == NULL) 109 return (FALSE); 110 curbp = bp; 111 compile_win = curwp = wp; 112 gotoline(FFARG, 0); 113 return (TRUE); 114 } 115 116 /* id-utils foo. */ 117 static int 118 gid(int f, int n) 119 { 120 char command[NFILEN]; 121 char cprompt[NFILEN], *bufp; 122 int c; 123 struct buffer *bp; 124 struct mgwin *wp; 125 int i, j, len; 126 127 /* catch ([^\s(){}]+)[\s(){}]* */ 128 129 i = curwp->w_doto; 130 /* Skip backwards over delimiters we are currently on */ 131 while (i > 0) { 132 c = lgetc(curwp->w_dotp, i); 133 if (isalnum(c) || c == '_') 134 break; 135 136 i--; 137 } 138 139 /* Skip the symbol itself */ 140 for (; i > 0; i--) { 141 c = lgetc(curwp->w_dotp, i - 1); 142 if (!isalnum(c) && c != '_') 143 break; 144 } 145 /* Fill the symbol in cprompt[] */ 146 for (j = 0; j < sizeof(cprompt) - 1 && i < llength(curwp->w_dotp); 147 j++, i++) { 148 c = lgetc(curwp->w_dotp, i); 149 if (!isalnum(c) && c != '_') 150 break; 151 cprompt[j] = c; 152 } 153 cprompt[j] = '\0'; 154 155 if ((bufp = eread("Run gid (with args): ", cprompt, NFILEN, 156 (j ? EFDEF : 0) | EFNEW | EFCR)) == NULL) 157 return (ABORT); 158 else if (bufp[0] == '\0') 159 return (FALSE); 160 len = snprintf(command, sizeof(command), "gid %s", cprompt); 161 if (len < 0 || len >= sizeof(command)) 162 return (FALSE); 163 164 if ((bp = compile_mode("*gid*", command)) == NULL) 165 return (FALSE); 166 if ((wp = popbuf(bp, WNONE)) == NULL) 167 return (FALSE); 168 curbp = bp; 169 compile_win = curwp = wp; 170 return (TRUE); 171 } 172 173 struct buffer * 174 compile_mode(const char *name, const char *command) 175 { 176 struct buffer *bp; 177 FILE *fpipe; 178 char *buf; 179 size_t sz; 180 ssize_t len; 181 int ret, n, status; 182 char cwd[NFILEN], qcmd[NFILEN]; 183 char timestr[NTIME]; 184 time_t t; 185 186 buf = NULL; 187 sz = 0; 188 189 n = snprintf(qcmd, sizeof(qcmd), "%s 2>&1", command); 190 if (n < 0 || n >= sizeof(qcmd)) 191 return (NULL); 192 193 bp = bfind(name, TRUE); 194 if (bclear(bp) != TRUE) 195 return (NULL); 196 197 if (getbufcwd(bp->b_cwd, sizeof(bp->b_cwd)) != TRUE) 198 return (NULL); 199 addlinef(bp, "cd %s", bp->b_cwd); 200 addline(bp, qcmd); 201 addline(bp, ""); 202 203 if (getcwd(cwd, sizeof(cwd)) == NULL) 204 panic("Can't get current directory!"); 205 if (chdir(bp->b_cwd) == -1) { 206 dobeep(); 207 ewprintf("Can't change dir to %s", bp->b_cwd); 208 return (NULL); 209 } 210 if ((fpipe = popen(qcmd, "r")) == NULL) { 211 dobeep(); 212 ewprintf("Problem opening pipe"); 213 return (NULL); 214 } 215 while ((len = getline(&buf, &sz, fpipe)) != -1) { 216 if (buf[len - 1] == *bp->b_nlchr) 217 buf[len - 1] = '\0'; 218 addline(bp, buf); 219 } 220 free(buf); 221 if (ferror(fpipe)) 222 ewprintf("Problem reading pipe"); 223 ret = pclose(fpipe); 224 t = time(NULL); 225 strftime(timestr, sizeof(timestr), "%a %b %e %T %Y", localtime(&t)); 226 addline(bp, ""); 227 if (WIFEXITED(ret)) { 228 status = WEXITSTATUS(ret); 229 if (status == 0) 230 addlinef(bp, "Command finished at %s", timestr); 231 else 232 addlinef(bp, "Command exited abnormally with code %d " 233 "at %s", status, timestr); 234 } else 235 addlinef(bp, "Subshell killed by signal %d at %s", 236 WTERMSIG(ret), timestr); 237 238 bp->b_dotp = bfirstlp(bp); 239 bp->b_modes[0] = name_mode("fundamental"); 240 bp->b_modes[1] = name_mode("compile"); 241 bp->b_nmodes = 1; 242 243 compile_buffer = bp; 244 245 if (chdir(cwd) == -1) { 246 dobeep(); 247 ewprintf("Can't change dir back to %s", cwd); 248 return (NULL); 249 } 250 return (bp); 251 } 252 253 static int 254 compile_goto_error(int f, int n) 255 { 256 struct buffer *bp; 257 struct mgwin *wp; 258 char *fname, *line, *lp, *ln; 259 int lineno; 260 char *adjf, path[NFILEN]; 261 const char *errstr; 262 struct line *last; 263 264 compile_win = curwp; 265 compile_buffer = curbp; 266 last = blastlp(compile_buffer); 267 268 retry: 269 /* last line is compilation result */ 270 if (curwp->w_dotp == last) 271 return (FALSE); 272 273 if ((line = linetostr(curwp->w_dotp)) == NULL) 274 return (FALSE); 275 lp = line; 276 if ((fname = strsep(&lp, ":")) == NULL || *fname == '\0') 277 goto fail; 278 if ((ln = strsep(&lp, ":")) == NULL || *ln == '\0') 279 goto fail; 280 lineno = (int)strtonum(ln, INT_MIN, INT_MAX, &errstr); 281 if (errstr) 282 goto fail; 283 284 if (fname && fname[0] != '/') { 285 if (getbufcwd(path, sizeof(path)) == FALSE) 286 goto fail; 287 if (strlcat(path, fname, sizeof(path)) >= sizeof(path)) 288 goto fail; 289 adjf = path; 290 } else { 291 adjf = adjustname(fname, TRUE); 292 } 293 free(line); 294 295 if (adjf == NULL) 296 return (FALSE); 297 298 if ((bp = findbuffer(adjf)) == NULL) 299 return (FALSE); 300 if ((wp = popbuf(bp, WNONE)) == NULL) 301 return (FALSE); 302 curbp = bp; 303 curwp = wp; 304 if (bp->b_fname[0] == '\0') 305 readin(adjf); 306 gotoline(FFARG, lineno); 307 return (TRUE); 308 fail: 309 free(line); 310 if (curwp->w_dotp != blastlp(curbp)) { 311 curwp->w_dotp = lforw(curwp->w_dotp); 312 curwp->w_rflag |= WFMOVE; 313 goto retry; 314 } 315 dobeep(); 316 ewprintf("No more hits"); 317 return (FALSE); 318 } 319 320 int 321 next_error(int f, int n) 322 { 323 if (compile_win == NULL || compile_buffer == NULL) { 324 dobeep(); 325 ewprintf("No compilation active"); 326 return (FALSE); 327 } 328 curwp = compile_win; 329 curbp = compile_buffer; 330 if (curwp->w_dotp == blastlp(curbp)) { 331 dobeep(); 332 ewprintf("No more hits"); 333 return (FALSE); 334 } 335 curwp->w_dotp = lforw(curwp->w_dotp); 336 curwp->w_rflag |= WFMOVE; 337 338 return (compile_goto_error(f, n)); 339 } 340 341 /* 342 * Since we don't have variables (we probably should) these are command 343 * processors for changing the values of mode flags. 344 */ 345 int 346 globalwdtoggle(int f, int n) 347 { 348 if (f & FFARG) 349 globalwd = n > 0; 350 else 351 globalwd = !globalwd; 352 353 sgarbf = TRUE; 354 355 return (TRUE); 356 } 357