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