xref: /openbsd/usr.bin/mg/region.c (revision e5dd7070)
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(&region)) != 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(&region)) != 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(&region)) != 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(&region)) != 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(&region)) != 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(&region) != 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(&region, 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