1 /* $OpenBSD: region.c,v 1.44 2023/03/28 14:47:28 op Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Region based commands. 7 * The routines in this file deal with the region, that magic space between 8 * "." and mark. Some functions are commands. Some functions are just for 9 * internal use. 10 */ 11 12 #include <sys/queue.h> 13 #include <sys/socket.h> 14 #include <sys/types.h> 15 #include <sys/wait.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <paths.h> 19 #include <poll.h> 20 #include <signal.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "def.h" 27 28 #define TIMEOUT 10000 29 30 static char leftover[BUFSIZ]; 31 32 static int getregion(struct region *); 33 static int iomux(int, char * const, int, struct buffer *); 34 static int preadin(int, struct buffer *); 35 static void pwriteout(int, char **, int *); 36 static int setsize(struct region *, RSIZE); 37 static int shellcmdoutput(char * const, char * const, int); 38 39 /* 40 * Kill the region. Ask "getregion" to figure out the bounds of the region. 41 * Move "." to the start, and kill the characters. Mark is cleared afterwards. 42 */ 43 int 44 killregion(int f, int n) 45 { 46 int s; 47 struct region region; 48 49 if ((s = getregion(®ion)) != TRUE) 50 return (s); 51 /* This is a kill-type command, so do magic kill buffer stuff. */ 52 if ((lastflag & CFKILL) == 0) 53 kdelete(); 54 thisflag |= CFKILL; 55 curwp->w_dotp = region.r_linep; 56 curwp->w_doto = region.r_offset; 57 curwp->w_dotline = region.r_lineno; 58 s = ldelete(region.r_size, KFORW | KREG); 59 clearmark(FFARG, 0); 60 61 return (s); 62 } 63 64 /* 65 * Copy all of the characters in the region to the kill buffer, 66 * clearing the mark afterwards. 67 * This is a bit like a kill region followed by a yank. 68 */ 69 int 70 copyregion(int f, int n) 71 { 72 struct line *linep; 73 struct region region; 74 int loffs; 75 int s; 76 77 if ((s = getregion(®ion)) != TRUE) 78 return (s); 79 80 /* kill type command */ 81 if ((lastflag & CFKILL) == 0) 82 kdelete(); 83 thisflag |= CFKILL; 84 85 /* current line */ 86 linep = region.r_linep; 87 88 /* current offset */ 89 loffs = region.r_offset; 90 91 while (region.r_size--) { 92 if (loffs == llength(linep)) { /* End of line. */ 93 if ((s = kinsert(*curbp->b_nlchr, KFORW)) != TRUE) 94 return (s); 95 linep = lforw(linep); 96 loffs = 0; 97 } else { /* Middle of line. */ 98 if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE) 99 return (s); 100 ++loffs; 101 } 102 } 103 clearmark(FFARG, 0); 104 105 return (TRUE); 106 } 107 108 /* 109 * Lower case region. Zap all of the upper case characters in the region to 110 * lower case. Use the region code to set the limits. Scan the buffer, doing 111 * the changes. Call "lchange" to ensure that redisplay is done in all 112 * buffers. 113 */ 114 int 115 lowerregion(int f, int n) 116 { 117 struct line *linep; 118 struct region region; 119 int loffs, c, s; 120 121 if ((s = checkdirty(curbp)) != TRUE) 122 return (s); 123 if (curbp->b_flag & BFREADONLY) { 124 dobeep(); 125 ewprintf("Buffer is read-only"); 126 return (FALSE); 127 } 128 129 if ((s = getregion(®ion)) != TRUE) 130 return (s); 131 132 undo_add_change(region.r_linep, region.r_offset, region.r_size); 133 134 lchange(WFFULL); 135 linep = region.r_linep; 136 loffs = region.r_offset; 137 while (region.r_size--) { 138 if (loffs == llength(linep)) { 139 linep = lforw(linep); 140 loffs = 0; 141 } else { 142 c = lgetc(linep, loffs); 143 if (ISUPPER(c) != FALSE) 144 lputc(linep, loffs, TOLOWER(c)); 145 ++loffs; 146 } 147 } 148 return (TRUE); 149 } 150 151 /* 152 * Upper case region. Zap all of the lower case characters in the region to 153 * upper case. Use the region code to set the limits. Scan the buffer, 154 * doing the changes. Call "lchange" to ensure that redisplay is done in all 155 * buffers. 156 */ 157 int 158 upperregion(int f, int n) 159 { 160 struct line *linep; 161 struct region region; 162 int loffs, c, s; 163 164 if ((s = checkdirty(curbp)) != TRUE) 165 return (s); 166 if (curbp->b_flag & BFREADONLY) { 167 dobeep(); 168 ewprintf("Buffer is read-only"); 169 return (FALSE); 170 } 171 if ((s = getregion(®ion)) != TRUE) 172 return (s); 173 174 undo_add_change(region.r_linep, region.r_offset, region.r_size); 175 176 lchange(WFFULL); 177 linep = region.r_linep; 178 loffs = region.r_offset; 179 while (region.r_size--) { 180 if (loffs == llength(linep)) { 181 linep = lforw(linep); 182 loffs = 0; 183 } else { 184 c = lgetc(linep, loffs); 185 if (ISLOWER(c) != FALSE) 186 lputc(linep, loffs, TOUPPER(c)); 187 ++loffs; 188 } 189 } 190 return (TRUE); 191 } 192 193 /* 194 * This routine figures out the bound of the region in the current window, 195 * and stores the results into the fields of the REGION structure. Dot and 196 * mark are usually close together, but I don't know the order, so I scan 197 * outward from dot, in both directions, looking for mark. The size is kept 198 * in a long. At the end, after the size is figured out, it is assigned to 199 * the size field of the region structure. If this assignment loses any bits, 200 * then we print an error. This is "type independent" overflow checking. All 201 * of the callers of this routine should be ready to get an ABORT status, 202 * because I might add a "if regions is big, ask before clobbering" flag. 203 */ 204 static int 205 getregion(struct region *rp) 206 { 207 struct line *flp, *blp; 208 long fsize, bsize; 209 210 if (curwp->w_markp == NULL) { 211 dobeep(); 212 ewprintf("No mark set in this window"); 213 return (FALSE); 214 } 215 216 /* "r_size" always ok */ 217 if (curwp->w_dotp == curwp->w_markp) { 218 rp->r_linep = curwp->w_dotp; 219 rp->r_lineno = curwp->w_dotline; 220 if (curwp->w_doto < curwp->w_marko) { 221 rp->r_offset = curwp->w_doto; 222 rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto); 223 } else { 224 rp->r_offset = curwp->w_marko; 225 rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko); 226 } 227 return (TRUE); 228 } 229 /* get region size */ 230 flp = blp = curwp->w_dotp; 231 bsize = curwp->w_doto; 232 fsize = llength(flp) - curwp->w_doto + 1; 233 while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) { 234 if (lforw(flp) != curbp->b_headp) { 235 flp = lforw(flp); 236 if (flp == curwp->w_markp) { 237 rp->r_linep = curwp->w_dotp; 238 rp->r_offset = curwp->w_doto; 239 rp->r_lineno = curwp->w_dotline; 240 return (setsize(rp, 241 (RSIZE)(fsize + curwp->w_marko))); 242 } 243 fsize += llength(flp) + 1; 244 } 245 if (lback(blp) != curbp->b_headp) { 246 blp = lback(blp); 247 bsize += llength(blp) + 1; 248 if (blp == curwp->w_markp) { 249 rp->r_linep = blp; 250 rp->r_offset = curwp->w_marko; 251 rp->r_lineno = curwp->w_markline; 252 return (setsize(rp, 253 (RSIZE)(bsize - curwp->w_marko))); 254 } 255 } 256 } 257 dobeep(); 258 ewprintf("Bug: lost mark"); 259 return (FALSE); 260 } 261 262 /* 263 * Set size, and check for overflow. 264 */ 265 static int 266 setsize(struct region *rp, RSIZE size) 267 { 268 rp->r_size = size; 269 if (rp->r_size != size) { 270 dobeep(); 271 ewprintf("Region is too large"); 272 return (FALSE); 273 } 274 return (TRUE); 275 } 276 277 #define PREFIXLENGTH 40 278 static char prefix_string[PREFIXLENGTH] = {'>', '\0'}; 279 280 /* 281 * Prefix the region with whatever is in prefix_string. Leaves dot at the 282 * beginning of the line after the end of the region. If an argument is 283 * given, prompts for the line prefix string. 284 */ 285 int 286 prefixregion(int f, int n) 287 { 288 struct line *first, *last; 289 struct region region; 290 char *prefix = prefix_string; 291 int nline; 292 int s; 293 294 if ((s = checkdirty(curbp)) != TRUE) 295 return (s); 296 if (curbp->b_flag & BFREADONLY) { 297 dobeep(); 298 ewprintf("Buffer is read-only"); 299 return (FALSE); 300 } 301 if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE)) 302 return (s); 303 304 /* get # of lines to affect */ 305 if ((s = getregion(®ion)) != TRUE) 306 return (s); 307 first = region.r_linep; 308 last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp; 309 for (nline = 1; first != last; nline++) 310 first = lforw(first); 311 312 /* move to beginning of region */ 313 curwp->w_dotp = region.r_linep; 314 curwp->w_doto = region.r_offset; 315 curwp->w_dotline = region.r_lineno; 316 317 /* for each line, go to beginning and insert the prefix string */ 318 while (nline--) { 319 (void)gotobol(FFRAND, 1); 320 for (prefix = prefix_string; *prefix; prefix++) 321 (void)linsert(1, *prefix); 322 (void)forwline(FFRAND, 1); 323 } 324 (void)gotobol(FFRAND, 1); 325 return (TRUE); 326 } 327 328 /* 329 * Set line prefix string. Used by prefixregion. 330 */ 331 int 332 setprefix(int f, int n) 333 { 334 char buf[PREFIXLENGTH], *rep; 335 int retval; 336 337 if (prefix_string[0] == '\0') 338 rep = eread("Prefix string: ", buf, sizeof(buf), 339 EFNEW | EFCR); 340 else 341 rep = eread("Prefix string (default %s): ", buf, sizeof(buf), 342 EFNUL | EFNEW | EFCR, prefix_string); 343 if (rep == NULL) 344 return (ABORT); 345 if (rep[0] != '\0') { 346 (void)strlcpy(prefix_string, rep, sizeof(prefix_string)); 347 retval = TRUE; 348 } else if (rep[0] == '\0' && prefix_string[0] != '\0') { 349 /* CR -- use old one */ 350 retval = TRUE; 351 } else 352 retval = FALSE; 353 return (retval); 354 } 355 356 int 357 region_get_data(struct region *reg, char *buf, int len) 358 { 359 int i, off; 360 struct line *lp; 361 362 off = reg->r_offset; 363 lp = reg->r_linep; 364 for (i = 0; i < len; i++) { 365 if (off == llength(lp)) { 366 lp = lforw(lp); 367 if (lp == curbp->b_headp) 368 break; 369 off = 0; 370 buf[i] = *curbp->b_nlchr; 371 } else { 372 buf[i] = lgetc(lp, off); 373 off++; 374 } 375 } 376 buf[i] = '\0'; 377 return (i); 378 } 379 380 void 381 region_put_data(const char *buf, int len) 382 { 383 int i; 384 385 for (i = 0; buf[i] != '\0' && i < len; i++) { 386 if (buf[i] == *curbp->b_nlchr) 387 lnewline(); 388 else 389 linsert(1, buf[i]); 390 } 391 } 392 393 /* 394 * Mark whole buffer by first traversing to end-of-buffer 395 * and then to beginning-of-buffer. Mark, dot are implicitly 396 * set to eob, bob respectively during traversal. 397 */ 398 int 399 markbuffer(int f, int n) 400 { 401 if (gotoeob(f,n) == FALSE) 402 return (FALSE); 403 (void) clearmark(f, n); 404 if (gotobob(f,n) == FALSE) 405 return (FALSE); 406 return (TRUE); 407 } 408 409 /* 410 * Pipe text from current region to external command. 411 */ 412 int 413 piperegion(int f, int n) 414 { 415 struct region region; 416 int len; 417 char *cmd, cmdbuf[NFILEN], *text; 418 419 /* C-u M-| is not supported yet */ 420 if (n > 1) 421 return (ABORT); 422 423 if (curwp->w_markp == NULL) { 424 dobeep(); 425 ewprintf("The mark is not set now, so there is no region"); 426 return (FALSE); 427 } 428 429 if ((cmd = eread("Shell command on region: ", cmdbuf, sizeof(cmdbuf), 430 EFNEW | EFCR)) == NULL || (cmd[0] == '\0')) 431 return (ABORT); 432 433 if (getregion(®ion) != TRUE) 434 return (FALSE); 435 436 len = region.r_size; 437 438 if ((text = malloc(len + 1)) == NULL) { 439 dobeep(); 440 ewprintf("Cannot allocate memory."); 441 return (FALSE); 442 } 443 444 region_get_data(®ion, text, len); 445 446 return shellcmdoutput(cmd, text, len); 447 } 448 449 /* 450 * Get command from mini-buffer and execute externally. 451 */ 452 int 453 shellcommand(int f, int n) 454 { 455 char *cmd, cmdbuf[NFILEN]; 456 457 if (n > 1) 458 return (ABORT); 459 460 if ((cmd = eread("Shell command: ", cmdbuf, sizeof(cmdbuf), 461 EFNEW | EFCR)) == NULL || (cmd[0] == '\0')) 462 return (ABORT); 463 464 return shellcmdoutput(cmd, NULL, 0); 465 } 466 467 int 468 shellcmdoutput(char* const cmd, char* const text, int len) 469 { 470 struct buffer *bp; 471 char *argv[] = {NULL, "-c", cmd, NULL}; 472 char *shellp; 473 int ret; 474 475 bp = bfind("*Shell Command Output*", TRUE); 476 bp->b_flag |= BFREADONLY; 477 if (bclear(bp) != TRUE) { 478 free(text); 479 return (FALSE); 480 } 481 482 if ((shellp = getenv("SHELL")) == NULL) 483 shellp = _PATH_BSHELL; 484 485 if ((argv[0] = strrchr(shellp, '/')) != NULL) 486 argv[0]++; 487 else 488 argv[0] = shellp; 489 490 ret = pipeio(shellp, argv, text, len, bp); 491 492 if (ret == TRUE) { 493 eerase(); 494 if (lforw(bp->b_headp) == bp->b_headp) 495 addline(bp, "(Shell command succeeded with no output)"); 496 } 497 498 free(text); 499 return (ret); 500 } 501 502 /* 503 * Create a socketpair, fork and execv path with argv. 504 * STDIN, STDOUT and STDERR of child process are redirected to socket. 505 * Parent writes len chars from text to socket. 506 */ 507 int 508 pipeio(const char* const path, char* const argv[], char* const text, int len, 509 struct buffer *outbp) 510 { 511 int s[2], ret; 512 char *err; 513 pid_t pid; 514 515 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) { 516 dobeep(); 517 ewprintf("socketpair error"); 518 return (FALSE); 519 } 520 521 switch((pid = fork())) { 522 case -1: 523 dobeep(); 524 ewprintf("Can't fork"); 525 return (FALSE); 526 case 0: 527 /* Child process */ 528 close(s[0]); 529 if (dup2(s[1], STDIN_FILENO) == -1) 530 _exit(1); 531 if (dup2(s[1], STDOUT_FILENO) == -1) 532 _exit(1); 533 if (dup2(s[1], STDERR_FILENO) == -1) 534 _exit(1); 535 536 execv(path, argv); 537 err = strerror(errno); 538 write(s[1], err, strlen(err)); 539 _exit(1); 540 default: 541 /* Parent process */ 542 close(s[1]); 543 ret = iomux(s[0], text, len, outbp); 544 waitpid(pid, NULL, 0); /* Collect child to prevent zombies */ 545 546 return (ret); 547 } 548 return (FALSE); 549 } 550 551 /* 552 * Multiplex read, write on socket fd passed. Put output in outbp 553 * Poll on the fd for both read and write readiness. 554 */ 555 int 556 iomux(int fd, char* const text, int len, struct buffer *outbp) 557 { 558 struct pollfd pfd[1]; 559 int nfds; 560 char *textcopy; 561 562 textcopy = text; 563 fcntl(fd, F_SETFL, O_NONBLOCK); 564 pfd[0].fd = fd; 565 566 /* There is nothing to write if len is zero 567 * but the cmd's output should be read so shutdown 568 * the socket for writing only and don't wait for POLLOUT 569 */ 570 if (len == 0) { 571 shutdown(fd, SHUT_WR); 572 pfd[0].events = POLLIN; 573 } else 574 pfd[0].events = POLLIN | POLLOUT; 575 576 while ((nfds = poll(pfd, 1, TIMEOUT)) != -1 || 577 (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))) { 578 if (pfd[0].revents & POLLOUT && len > 0) 579 pwriteout(fd, &textcopy, &len); 580 else if (pfd[0].revents & POLLIN) 581 if (preadin(fd, outbp) == FALSE) 582 break; 583 if (len == 0 && pfd[0].events & POLLOUT) 584 pfd[0].events = POLLIN; 585 } 586 close(fd); 587 588 /* In case if last line doesn't have a '\n' add the leftover 589 * characters to buffer. 590 */ 591 if (leftover[0] != '\0') { 592 addline(outbp, leftover); 593 leftover[0] = '\0'; 594 } 595 if (nfds == 0) { 596 dobeep(); 597 ewprintf("poll timed out"); 598 return (FALSE); 599 } else if (nfds == -1) { 600 dobeep(); 601 ewprintf("poll error"); 602 return (FALSE); 603 } 604 return (popbuftop(outbp, WNONE)); 605 } 606 607 /* 608 * Write some text from region to fd. Once done shutdown the 609 * write end. 610 */ 611 void 612 pwriteout(int fd, char **text, int *len) 613 { 614 int w; 615 616 if (((w = send(fd, *text, *len, MSG_NOSIGNAL)) == -1)) { 617 switch(errno) { 618 case EPIPE: 619 *len = -1; 620 break; 621 case EAGAIN: 622 return; 623 } 624 } else 625 *len -= w; 626 627 *text += w; 628 if (*len <= 0) 629 shutdown(fd, SHUT_WR); 630 } 631 632 /* 633 * Read some data from socket fd, break on '\n' and add 634 * to buffer. If couldn't break on newline hold leftover 635 * characters and append in next iteration. 636 */ 637 int 638 preadin(int fd, struct buffer *bp) 639 { 640 int len; 641 char buf[BUFSIZ], *p, *q; 642 643 if ((len = read(fd, buf, BUFSIZ - 1)) <= 0) 644 return (FALSE); 645 646 buf[len] = '\0'; 647 p = q = buf; 648 if (leftover[0] != '\0' && ((q = strchr(p, *bp->b_nlchr)) != NULL)) { 649 *q++ = '\0'; 650 if (strlcat(leftover, p, sizeof(leftover)) >= 651 sizeof(leftover)) { 652 dobeep(); 653 ewprintf("line too long"); 654 return (FALSE); 655 } 656 addline(bp, leftover); 657 leftover[0] = '\0'; 658 p = q; 659 } 660 while ((q = strchr(p, *bp->b_nlchr)) != NULL) { 661 *q++ = '\0'; 662 addline(bp, p); 663 p = q; 664 } 665 if (strlcpy(leftover, p, sizeof(leftover)) >= sizeof(leftover)) { 666 dobeep(); 667 ewprintf("line too long"); 668 return (FALSE); 669 } 670 return (TRUE); 671 } 672