]> pd.if.org Git - pd_readline/blob - mg/region.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / region.c
1 /*      $OpenBSD: region.c,v 1.30 2012/04/11 17:51:10 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/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
20 #include "def.h"
21
22 #define TIMEOUT 10000
23
24 static char leftover[BUFSIZ];
25
26 static  int     getregion(struct region *);
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 *);
31 static  int     setsize(struct region *, RSIZE);
32
33 /*
34  * Kill the region.  Ask "getregion" to figure out the bounds of the region.
35  * Move "." to the start, and kill the characters. Mark is cleared afterwards.
36  */
37 /* ARGSUSED */
38 int
39 killregion(int f, int n)
40 {
41         int     s;
42         struct region   region;
43
44         if ((s = getregion(&region)) != TRUE)
45                 return (s);
46         /* This is a kill-type command, so do magic kill buffer stuff. */
47         if ((lastflag & CFKILL) == 0)
48                 kdelete();
49         thisflag |= CFKILL;
50         curwp->w_dotp = region.r_linep;
51         curwp->w_doto = region.r_offset;
52         curwp->w_dotline = region.r_lineno;
53         s = ldelete(region.r_size, KFORW | KREG);
54         clearmark(FFARG, 0);
55
56         return (s);
57 }
58
59 /*
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.
63  */
64 /* ARGSUSED */
65 int
66 copyregion(int f, int n)
67 {
68         struct line     *linep;
69         struct region    region;
70         int      loffs;
71         int      s;
72
73         if ((s = getregion(&region)) != TRUE)
74                 return (s);
75
76         /* kill type command */
77         if ((lastflag & CFKILL) == 0)
78                 kdelete();
79         thisflag |= CFKILL;
80
81         /* current line */
82         linep = region.r_linep;
83
84         /* current offset */
85         loffs = region.r_offset;
86
87         while (region.r_size--) {
88                 if (loffs == llength(linep)) {  /* End of line.          */
89                         if ((s = kinsert('\n', KFORW)) != TRUE)
90                                 return (s);
91                         linep = lforw(linep);
92                         loffs = 0;
93                 } else {                        /* Middle of line.       */
94                         if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE)
95                                 return (s);
96                         ++loffs;
97                 }
98         }
99         clearmark(FFARG, 0);
100
101         return (TRUE);
102 }
103
104 /*
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
108  * buffers.
109  */
110 /* ARGSUSED */
111 int
112 lowerregion(int f, int n)
113 {
114         struct line     *linep;
115         struct region    region;
116         int      loffs, c, s;
117
118         if ((s = checkdirty(curbp)) != TRUE)
119                 return (s);
120         if (curbp->b_flag & BFREADONLY) {
121                 ewprintf("Buffer is read-only");
122                 return (FALSE);
123         }
124
125         if ((s = getregion(&region)) != TRUE)
126                 return (s);
127
128         undo_add_change(region.r_linep, region.r_offset, region.r_size);
129
130         lchange(WFFULL);
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         }
144         return (TRUE);
145 }
146
147 /*
148  * Upper case region.  Zap all of the lower case characters in the region to
149  * upper case.  Use the region code to set the limits.  Scan the buffer,
150  * doing the changes.  Call "lchange" to ensure that redisplay is done in all
151  * buffers.
152  */
153 /* ARGSUSED */
154 int
155 upperregion(int f, int n)
156 {
157         struct line      *linep;
158         struct region     region;
159         int       loffs, c, s;
160
161         if ((s = checkdirty(curbp)) != TRUE)
162                 return (s);
163         if (curbp->b_flag & BFREADONLY) {
164                 ewprintf("Buffer is read-only");
165                 return (FALSE);
166         }
167         if ((s = getregion(&region)) != TRUE)
168                 return (s);
169
170         undo_add_change(region.r_linep, region.r_offset, region.r_size);
171
172         lchange(WFFULL);
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         }
186         return (TRUE);
187 }
188
189 /*
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,
198  * because I might add a "if regions is big, ask before clobbering" flag.
199  */
200 static int
201 getregion(struct region *rp)
202 {
203         struct line     *flp, *blp;
204         long     fsize, bsize;
205
206         if (curwp->w_markp == NULL) {
207                 ewprintf("No mark set in this window");
208                 return (FALSE);
209         }
210
211         /* "r_size" always ok */
212         if (curwp->w_dotp == curwp->w_markp) {
213                 rp->r_linep = curwp->w_dotp;
214                 rp->r_lineno = curwp->w_dotline;
215                 if (curwp->w_doto < curwp->w_marko) {
216                         rp->r_offset = curwp->w_doto;
217                         rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto);
218                 } else {
219                         rp->r_offset = curwp->w_marko;
220                         rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko);
221                 }
222                 return (TRUE);
223         }
224         /* get region size */
225         flp = blp = curwp->w_dotp;
226         bsize = curwp->w_doto;
227         fsize = llength(flp) - curwp->w_doto + 1;
228         while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) {
229                 if (lforw(flp) != curbp->b_headp) {
230                         flp = lforw(flp);
231                         if (flp == curwp->w_markp) {
232                                 rp->r_linep = curwp->w_dotp;
233                                 rp->r_offset = curwp->w_doto;
234                                 rp->r_lineno = curwp->w_dotline;
235                                 return (setsize(rp,
236                                     (RSIZE)(fsize + curwp->w_marko)));
237                         }
238                         fsize += llength(flp) + 1;
239                 }
240                 if (lback(blp) != curbp->b_headp) {
241                         blp = lback(blp);
242                         bsize += llength(blp) + 1;
243                         if (blp == curwp->w_markp) {
244                                 rp->r_linep = blp;
245                                 rp->r_offset = curwp->w_marko;
246                                 rp->r_lineno = curwp->w_markline;
247                                 return (setsize(rp,
248                                     (RSIZE)(bsize - curwp->w_marko)));
249                         }
250                 }
251         }
252         ewprintf("Bug: lost mark");
253         return (FALSE);
254 }
255
256 /*
257  * Set size, and check for overflow.
258  */
259 static int
260 setsize(struct region *rp, RSIZE size)
261 {
262         rp->r_size = size;
263         if (rp->r_size != size) {
264                 ewprintf("Region is too large");
265                 return (FALSE);
266         }
267         return (TRUE);
268 }
269
270 #define PREFIXLENGTH 40
271 static char     prefix_string[PREFIXLENGTH] = {'>', '\0'};
272
273 /*
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
276  * given, prompts for the line prefix string.
277  */
278 /* ARGSUSED */
279 int
280 prefixregion(int f, int n)
281 {
282         struct line     *first, *last;
283         struct region    region;
284         char    *prefix = prefix_string;
285         int      nline;
286         int      s;
287
288         if ((s = checkdirty(curbp)) != TRUE)
289                 return (s);
290         if (curbp->b_flag & BFREADONLY) {
291                 ewprintf("Buffer is read-only");
292                 return (FALSE);
293         }
294         if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE))
295                 return (s);
296
297         /* get # of lines to affect */
298         if ((s = getregion(&region)) != 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
305         /* move to beginning of region */
306         curwp->w_dotp = region.r_linep;
307         curwp->w_doto = region.r_offset;
308         curwp->w_dotline = region.r_lineno;
309
310         /* for each line, go to beginning and insert the prefix string */
311         while (nline--) {
312                 (void)gotobol(FFRAND, 1);
313                 for (prefix = prefix_string; *prefix; prefix++)
314                         (void)linsert(1, *prefix);
315                 (void)forwline(FFRAND, 1);
316         }
317         (void)gotobol(FFRAND, 1);
318         return (TRUE);
319 }
320
321 /*
322  * Set line prefix string. Used by prefixregion.
323  */
324 /* ARGSUSED */
325 int
326 setprefix(int f, int n)
327 {
328         char    buf[PREFIXLENGTH], *rep;
329         int     retval;
330
331         if (prefix_string[0] == '\0')
332                 rep = eread("Prefix string: ", buf, sizeof(buf),
333                     EFNEW | EFCR);
334         else
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') {
340                 (void)strlcpy(prefix_string, rep, sizeof(prefix_string));
341                 retval = TRUE;
342         } else if (rep[0] == '\0' && prefix_string[0] != '\0') {
343                 /* CR -- use old one */
344                 retval = TRUE;
345         } else
346                 retval = FALSE;
347         return (retval);
348 }
349
350 int
351 region_get_data(struct region *reg, char *buf, int len)
352 {
353         int      i, off;
354         struct line     *lp;
355
356         off = reg->r_offset;
357         lp = reg->r_linep;
358         for (i = 0; i < len; i++) {
359                 if (off == llength(lp)) {
360                         lp = lforw(lp);
361                         if (lp == curbp->b_headp)
362                                 break;
363                         off = 0;
364                         buf[i] = '\n';
365                 } else {
366                         buf[i] = lgetc(lp, off);
367                         off++;
368                 }
369         }
370         buf[i] = '\0';
371         return (i);
372 }
373
374 void
375 region_put_data(const char *buf, int len)
376 {
377         int i;
378
379         for (i = 0; buf[i] != '\0' && i < len; i++) {
380                 if (buf[i] == '\n')
381                         lnewline();
382                 else
383                         linsert(1, buf[i]);
384         }
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(&region) != TRUE)
480                 return (FALSE);
481         
482         if ((text = malloc(region.r_size + 1)) == NULL)
483                 return (ABORT);
484         
485         region_get_data(&region, 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, &region.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);
597 }