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