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