another update from CVS HEAD, for QA
[alioth/jupp.git] / ublock.c
1 /*
2  *      Highlighted block functions
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #include "config.h"
9 #include "types.h"
10
11 __RCSID("$MirOS: contrib/code/jupp/ublock.c,v 1.30 2017/12/08 02:28:07 tg Exp $");
12
13 #include <sys/wait.h>
14 #include <limits.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17
18 #include "b.h"
19 #include "pw.h"
20 #include "queue.h"
21 #include "scrn.h"
22 #include "tty.h"
23 #include "ublock.h"
24 #include "uedit.h"
25 #include "utils.h"
26 #include "vs.h"
27 #include "path.h"
28 #include "poshist.h"
29 #include "ushell.h"
30 #include "charmap.h"
31 #include "w.h"
32
33 /* Global options */
34
35 int square = 0;                 /* Set for rectangle mode */
36 int lightoff = 0;               /* Set if highlighting should turn off
37
38                                    after block operations */
39 extern int marking;
40
41 /* Global variables */
42
43 P *markb = NULL;                /* Beginning and end of block */
44 P *markk = NULL;
45
46 /* Push markb & markk */
47
48 typedef struct marksav MARKSAV;
49 struct marksav {
50         LINK(MARKSAV) link;
51         P *markb, *markk;
52 } markstack = { { &markstack, &markstack}, NULL, NULL };
53 MARKSAV markfree = { {&markfree, &markfree}, NULL, NULL };
54 int nstack = 0;
55
56 int upsh(BW *bw)
57 {
58         MARKSAV *m = alitem(&markfree, sizeof(MARKSAV));
59
60         m->markb = 0;
61         m->markk = 0;
62         if (markk)
63                 pdupown(markk, &m->markk);
64         if (markb)
65                 pdupown(markb, &m->markb);
66         enqueb(MARKSAV, link, &markstack, m);
67         ++nstack;
68         return 0;
69 }
70
71 int upop(BW *bw)
72 {
73         MARKSAV *m = markstack.link.prev;
74
75         if (m != &markstack) {
76                 --nstack;
77                 prm(markk);
78                 prm(markb);
79                 markk = m->markk;
80                 if (markk)
81                         markk->owner = &markk;
82                 markb = m->markb;
83                 if (markb)
84                         markb->owner = &markb;
85                 demote(MARKSAV, link, &markfree, m);
86                 if (lightoff)
87                         unmark(bw);
88                 updall();
89                 return 0;
90         } else
91                 return -1;
92 }
93
94 /* Return true if markb/markk are valid */
95 /* If r is set, swap markb with markk if necessary */
96
97 int autoswap;
98
99 int markv(int r)
100 {
101         if (markb && markk && markb->b == markk->b && markk->byte > markb->byte && (!square || markk->xcol > markb->xcol)) {
102                 return 1;
103         } else if(autoswap && r && markb && markk && markb->b == markk->b && markb->byte > markk->byte && (!square || markk->xcol < markb->xcol)) {
104                 P *p = pdup(markb);
105                 prm(markb); markb=0; pdupown(markk, &markb);
106                 prm(markk); markk=0; pdupown(p, &markk);
107                 prm(p);
108                 return 1;
109         } else
110                 return 0;
111 }
112
113 /* Rectangle-mode subroutines */
114
115 /* B *pextrect(P *org,long height,long left,long right);
116  * Copy a rectangle into a new buffer
117  *
118  * org points to top-left corner of rectangle.
119  * height is number of lines in rectangle.
120  * right is rightmost column of rectangle + 1
121  */
122
123 B *pextrect(P *org, long int height, long int right)
124 {
125         P *p = pdup(org);       /* Left part of text to extract */
126         P *q = pdup(p);         /* After right part of text to extract */
127         B *tmp = bmk(NULL);     /* Buffer to extract to */
128         P *z = pdup(tmp->eof);  /* Buffer pointer */
129
130         while (height--) {
131                 pcol(p, org->xcol);
132                 pset(q, p);
133                 pcolwse(q, right);
134                 p_goto_eof(z);
135                 binsb(z, bcpy(p, q));
136                 p_goto_eof(z);
137                 binsc(z, '\n');
138                 pnextl(p);
139         }
140         prm(p);
141         prm(q);
142         prm(z);
143         return tmp;
144 }
145
146 /* void pdelrect(P *org,long height,long right);
147  * Delete a rectangle.
148  */
149
150 void pdelrect(P *org, long int height, long int right)
151 {
152         P *p = pdup(org);
153         P *q = pdup(p);
154
155         while (height--) {
156                 pcol(p, org->xcol);
157                 pset(q, p);
158                 pcol(q, right);
159                 bdel(p, q);
160                 pnextl(p);
161         }
162         prm(p);
163         prm(q);
164 }
165
166 /* void pclrrect(P *org,long height,long right,int usetabs);
167  * Blank-out a rectangle.
168  */
169
170 void pclrrect(P *org, long int height, long int right, int usetabs)
171 {
172         P *p = pdup(org);
173         P *q = pdup(p);
174
175         while (height--) {
176                 long pos;
177
178                 pcol(p, org->xcol);
179                 pset(q, p);
180                 pcoli(q, right);
181                 pos = q->col;
182                 bdel(p, q);
183                 pfill(p, pos, usetabs);
184                 pnextl(p);
185         }
186         prm(p);
187         prm(q);
188 }
189
190 /* int ptabrect(P *org,long height,long right)
191  * Check if there are any TABs in a rectangle
192  */
193
194 int ptabrect(P *org, long int height, long int right)
195 {
196         P *p = pdup(org);
197
198         while (height--) {
199                 int c;
200
201                 pcol(p, org->xcol);
202                 while ((c = pgetc(p)) != NO_MORE_DATA && c != '\n') {
203                         if (c == '\t') {
204                                 prm(p);
205                                 return '\t';
206                         } else if (piscol(p) > right)
207                                 break;
208                 }
209                 if (c != '\n')
210                         pnextl(p);
211         }
212         prm(p);
213         return ' ';
214 }
215
216 /* Insert rectangle */
217
218 void pinsrect(P *cur, B *tmp, long int width, int usetabs)
219 {
220         P *p = pdup(cur);       /* We insert at & move this pointer */
221         P *q = pdup(tmp->bof);  /* These are for scanning through 'tmp' */
222         P *r = pdup(q);
223
224         if (width)
225                 while (pset(r, q), p_goto_eol(q), (q->line != tmp->eof->line || piscol(q))) {
226                         pcol(p, cur->xcol);
227                         if (piscol(p) < cur->xcol)
228                                 pfill(p, cur->xcol, usetabs);
229                         binsb(p, bcpy(r, q));
230                         pfwrd(p, q->byte - r->byte);
231                         if (piscol(p) < cur->xcol + width)
232                                 pfill(p, cur->xcol + width, usetabs);
233                         if (piseol(p))
234                                 pbackws(p);
235                         if (!pnextl(p)) {
236                                 binsc(p, '\n');
237                                 pgetc(p);
238                         }
239                         if (pgetc(q) == NO_MORE_DATA)
240                                 break;
241                 }
242         prm(p);
243         prm(q);
244         prm(r);
245 }
246
247 /* Block functions */
248
249 /* Set beginning */
250
251 int umarkb(BW *bw)
252 {
253         pdupown(bw->cursor, &markb);
254         markb->xcol = bw->cursor->xcol;
255         updall();
256         return 0;
257 }
258
259 int udrop(BW *bw)
260 {
261         prm(markk);
262         if (marking && markb)
263                 prm(markb);
264         else
265                 umarkb(bw);
266         return 0;
267 }
268
269 int ubegin_marking(BW *bw)
270 {
271         if (marking)
272                 /* We're marking now... don't stop */
273                 return 0;
274         else if (markv(0) && bw->cursor->b==markb->b) {
275                 /* Try to extend current block */
276                 if (bw->cursor->byte==markb->byte) {
277                         pset(markb,markk);
278                         prm(markk); markk=0;
279                         marking = 1;
280                         return 0;
281                 } else if(bw->cursor->byte==markk->byte) {
282                         prm(markk); markk=0;
283                         marking = 1;
284                         return 0;
285                 }
286         }
287         /* Start marking - no message */
288         prm(markb); markb=0;
289         prm(markk); markk=0;
290         updall();
291         marking = 1;
292         return umarkb(bw);
293 }
294
295 int utoggle_marking(BW *bw)
296 {
297         if (markv(0) && bw->cursor->b==markb->b && bw->cursor->byte>=markb->byte && bw->cursor->byte<=markk->byte) {
298                 /* Just clear selection */
299                 prm(markb); markb=0;
300                 prm(markk); markk=0;
301                 updall();
302                 marking = 0;
303                 msgnw(bw->parent, UC "Selection cleared.");
304                 return 0;
305         } else if (markk) {
306                 /* Clear selection and start new one */
307                 prm(markb); markb=0;
308                 prm(markk); markk=0;
309                 updall();
310                 marking = 1;
311                 msgnw(bw->parent, UC "Selection started.");
312                 return umarkb(bw);
313         } else if (markb && markb->b==bw->cursor->b) {
314                 marking = 0;
315                 if (bw->cursor->byte<markb->byte) {
316                         pdupown(markb, &markk);
317                         prm(markb); markb=0;
318                         pdupown(bw->cursor, &markb);
319                         markb->xcol = bw->cursor->xcol;
320                 } else {
321                         pdupown(bw->cursor, &markk);
322                         markk->xcol = bw->cursor->xcol;
323                 }
324                 updall(); /* Because other windows could be changed */
325                 return 0;
326         } else {
327                 marking = 1;
328                 msgnw(bw->parent, UC "Selection started.");
329                 return umarkb(bw);
330         }
331 }
332
333 int uselect(BW *bw)
334 {
335         if (!markb)
336                 umarkb(bw);
337         return 0;
338 }
339
340 /* Set end */
341
342 int umarkk(BW *bw)
343 {
344         pdupown(bw->cursor, &markk);
345         markk->xcol = bw->cursor->xcol;
346         updall();
347         return 0;
348 }
349
350 /* Unset marks */
351
352 int unmark(BW *bw)
353 {
354         prm(markb);
355         prm(markk);
356         updall();
357         return 0;
358 }
359
360 /* Mark line */
361
362 int umarkl(BW *bw)
363 {
364         p_goto_bol(bw->cursor);
365         umarkb(bw);
366         pnextl(bw->cursor);
367         umarkk(bw);
368         utomarkb(bw);
369         pcol(bw->cursor, bw->cursor->xcol);
370         return 0;
371 }
372
373 int utomarkb(BW *bw)
374 {
375         if (markb && markb->b == bw->b) {
376                 pset(bw->cursor, markb);
377                 return 0;
378         } else
379                 return -1;
380 }
381
382 int utomarkk(BW *bw)
383 {
384         if (markk && markk->b == bw->b) {
385                 pset(bw->cursor, markk);
386                 return 0;
387         } else
388                 return -1;
389 }
390
391 int uswap(BW *bw)
392 {
393         if (markb && markb->b == bw->b) {
394                 P *q = pdup(markb);
395
396                 umarkb(bw);
397                 pset(bw->cursor, q);
398                 prm(q);
399                 return 0;
400         } else
401                 return -1;
402 }
403
404 int utomarkbk(BW *bw)
405 {
406         if (markb && markb->b == bw->b && bw->cursor->byte != markb->byte) {
407                 pset(bw->cursor, markb);
408                 return 0;
409         } else if (markk && markk->b == bw->b && bw->cursor->byte != markk->byte) {
410                 pset(bw->cursor, markk);
411                 return 0;
412         } else
413                 return -1;
414 }
415
416 /* Delete block */
417
418 int ublkdel(BW *bw)
419 {
420         if (markv(1)) {
421                 if (square)
422                         if (bw->o.overtype) {
423                                 long ocol = markk->xcol;
424
425                                 pclrrect(markb, markk->line - markb->line + 1, markk->xcol, ptabrect(markb, markk->line - markb->line + 1, markk->xcol));
426                                 pcol(markk, ocol);
427                                 markk->xcol = ocol;
428                         } else
429                                 pdelrect(markb, markk->line - markb->line + 1, markk->xcol);
430                 else
431                         bdel(markb, markk);
432                 if (lightoff)
433                         unmark(bw);
434         } else {
435                 msgnw(bw->parent, UC "No block");
436                 return -1;
437         }
438         return 0;
439 }
440
441 /* Special delete block function for PICO */
442
443 int upicokill(BW *bw)
444 {
445         upsh(bw);
446         umarkk(bw);
447         if (markv(1)) {
448                 if (square)
449                         if (bw->o.overtype) {
450                                 long ocol = markk->xcol;
451
452                                 pclrrect(markb, markk->line - markb->line + 1, markk->xcol, ptabrect(markb, markk->line - markb->line + 1, markk->xcol));
453                                 pcol(markk, ocol);
454                                 markk->xcol = ocol;
455                         } else
456                                 pdelrect(markb, markk->line - markb->line + 1, markk->xcol);
457                 else
458                         bdel(markb, markk);
459                 if (lightoff)
460                         unmark(bw);
461         } else
462                 udelln(bw);
463         return 0;
464 }
465
466 /* Move highlighted block */
467
468 int ublkmove(BW *bw)
469 {
470         if (markv(1)) {
471                 if (markb->b->rdonly) {
472                         msgnw(bw->parent, UC "Read only");
473                         return -1;
474                 }
475                 if (square) {
476                         long height = markk->line - markb->line + 1;
477                         long width = markk->xcol - markb->xcol;
478                         int usetabs = ptabrect(markb, height, markk->xcol);
479                         long ocol = piscol(bw->cursor);
480                         B *tmp = pextrect(markb, height, markk->xcol);
481                         int update_xcol = (bw->cursor->xcol >= markk->xcol && bw->cursor->line >= markb->line && bw->cursor->line <= markk->line);
482
483                         ublkdel(bw);
484                         /* now we can't use markb and markk until we set them again */
485                         /* ublkdel() frees them */
486                         if (bw->o.overtype) {
487                                 /* If cursor was in block, blkdel moves it to left edge of block, so fix it
488                                  * back to its original place here */
489                                 pcol(bw->cursor, ocol);
490                                 pfill(bw->cursor, ocol, ' ');
491                                 pdelrect(bw->cursor, height, piscol(bw->cursor) + width);
492                         } else if (update_xcol)
493                                 /* If cursor was to right of block, xcol was not properly updated */
494                                 bw->cursor->xcol -= width;
495                         pinsrect(bw->cursor, tmp, width, usetabs);
496                         brm(tmp);
497                         if (lightoff)
498                                 unmark(bw);
499                         else {
500                                 umarkb(bw);
501                                 umarkk(bw);
502                                 pline(markk, markk->line + height - 1);
503                                 pcol(markk, markb->xcol + width);
504                                 markk->xcol = markb->xcol + width;
505                         }
506                         return 0;
507                 } else if (bw->cursor->b != markk->b || bw->cursor->byte > markk->byte || bw->cursor->byte < markb->byte) {
508                         long size = markk->byte - markb->byte;
509
510                         binsb(bw->cursor, bcpy(markb, markk));
511                         bdel(markb, markk);
512                         if (lightoff)
513                                 unmark(bw);
514                         else {
515                                 umarkb(bw);
516                                 umarkk(bw);
517                                 pfwrd(markk, size);
518                         }
519                         updall();
520                         return 0;
521                 }
522         }
523         msgnw(bw->parent, UC "No block");
524         return -1;
525 }
526
527 /* Duplicate highlighted block */
528
529 int ublkcpy(BW *bw)
530 {
531         if (markv(1)) {
532                 if (square) {
533                         long height = markk->line - markb->line + 1;
534                         long width = markk->xcol - markb->xcol;
535                         int usetabs = ptabrect(markb, height, markk->xcol);
536                         B *tmp = pextrect(markb, height, markk->xcol);
537
538                         if (bw->o.overtype)
539                                 pdelrect(bw->cursor, height, piscol(bw->cursor) + width);
540                         pinsrect(bw->cursor, tmp, width, usetabs);
541                         brm(tmp);
542                         if (lightoff)
543                                 unmark(bw);
544                         else {
545                                 umarkb(bw);
546                                 umarkk(bw);
547                                 pline(markk, markk->line + height - 1);
548                                 pcol(markk, markb->xcol + width);
549                                 markk->xcol = markb->xcol + width;
550                         }
551                         return 0;
552                 } else {
553                         long size = markk->byte - markb->byte;
554                         B *tmp = bcpy(markb, markk);
555
556                         /* Simple overtype for hex mode */
557                         if (bw->o.hex && bw->o.overtype) {
558                                 P *q = pdup(bw->cursor);
559                                 if (q->byte + size >= q->b->eof->byte)
560                                         pset(q, q->b->eof);
561                                 else
562                                         pfwrd(q, size);
563                                 bdel(bw->cursor, q);
564                                 prm(q);
565                         }
566
567                         binsb(bw->cursor, tmp);
568                         if (lightoff)
569                                 unmark(bw);
570                         else {
571                                 umarkb(bw);
572                                 umarkk(bw);
573                                 pfwrd(markk, size);
574                         }
575                         updall();
576                         return 0;
577                 }
578         } else {
579                 msgnw(bw->parent, UC "No block");
580                 return -1;
581         }
582 }
583
584 /* Write highlighted block to a file */
585 /* This is called by ublksave in ufile.c */
586
587 int dowrite(BW *bw, unsigned char *s, void *object, int *notify)
588 {
589         int fl;
590         int ret = 0;
591
592         if (notify)
593                 *notify = 1;
594         if (!markv(1)) {
595                 vsrm(s);
596                 msgnw(bw->parent, UC "No block");
597                 return (-1);
598         }
599         if (square) {
600                 B *tmp = pextrect(markb,
601                                   markk->line - markb->line + 1,
602                                   markk->xcol);
603
604                 fl = bsave(tmp->bof, s, tmp->eof->byte, 0);
605                 brm(tmp);
606         } else {
607                 fl = bsave(markb, s, markk->byte - markb->byte, 0);
608         }
609         if (fl != 0) {
610                 msgnw(bw->parent, msgs[-fl]);
611                 ret = -1;
612         }
613         if (lightoff)
614                 unmark(bw);
615         vsrm(s);
616         return (ret);
617 }
618
619 /* Set highlighted block on a program block */
620
621 void setindent(BW *bw)
622 {
623         P *p, *q;
624         long indent;
625
626         if (pisblank(bw->cursor))
627                 return;
628
629         p = pdup(bw->cursor);
630         q = pdup(p);
631         indent = pisindent(p);
632
633         do {
634                 if (!pprevl(p))
635                         goto done;
636                 else
637                         p_goto_bol(p);
638         } while (pisindent(p) >= indent || pisblank(p));
639         pnextl(p);
640         /* Maybe skip blank lines at beginning */
641  done:
642         p_goto_bol(p);
643         p->xcol = piscol(p);
644         if (markb)
645                 prm(markb);
646         markb = p;
647         p->owner = &markb;
648
649         do {
650                 if (!pnextl(q))
651                         break;
652         } while (pisindent(q) >= indent || pisblank(q));
653         /* Maybe skip blank lines at end */
654         if (markk)
655                 prm(markk);
656         q->xcol = piscol(q);
657         markk = q;
658         q->owner = &markk;
659
660         updall();
661 }
662
663 /* Purity check */
664 /* Verifies that at least n indentation characters (for non-blank lines) match c */
665 /* If n is 0 (for urindent), this fails if c is space but indentation begins with tab */
666
667 static int
668 purity_check(int c, int n)
669 {
670         P *p = pdup(markb);
671         while (p->byte < markk->byte) {
672                 int x;
673                 p_goto_bol(p);
674                 if (!n && c==' ' && brc(p)=='\t') {
675                         prm(p);
676                         return 0;
677                 } else if (!piseol(p))
678                         for (x=0; x!=n; ++x)
679                                 if (pgetc(p)!=c) {
680                                         prm(p);
681                                         return 0;
682                                 }
683                 pnextl(p);
684         }
685         prm(p);
686         return 1;
687 }
688
689 /* Left indent check */
690 /* Verify that there is enough whitespace to do the left indent */
691
692 static int
693 lindent_check(int c, int n)
694 {
695         P *p = pdup(markb);
696         int indwid;
697         if (c=='\t')
698                 indwid = n * p->b->o.tab;
699         else
700                 indwid = n;
701         while (p->byte < markk->byte) {
702                 p_goto_bol(p);
703                 if (!piseol(p) && pisindent(p)<indwid) {
704                         prm(p);
705                         return 0;
706                 }
707                 pnextl(p);
708         }
709         prm(p);
710         return 1;
711 }
712
713 /* Indent more */
714
715 int urindent(BW *bw)
716 {
717         if (square) {
718                 if (markb && markk && markb->b == markk->b && markb->byte <= markk->byte && markb->xcol <= markk->xcol) {
719                         P *p = pdup(markb);
720
721                         do {
722                                 pcol(p, markb->xcol);
723                                 pfill(p, markb->xcol + bw->o.istep, bw->o.indentc);
724                         } while (pnextl(p) && p->line <= markk->line);
725                         prm(p);
726                 }
727         } else {
728                 if (!markb || !markk || markb->b != markk->b || bw->cursor->byte < markb->byte || bw->cursor->byte > markk->byte || markb->byte == markk->byte) {
729                         setindent(bw);
730                 } else if ( 1 /* bw->o.purify */) {
731                         P *p = pdup(markb);
732                         P *q = pdup(markb);
733                         int indwid;
734
735                         if (bw->o.indentc=='\t')
736                                 indwid = bw->o.tab * bw->o.istep;
737                         else
738                                 indwid = bw->o.istep;
739
740                         while (p->byte < markk->byte) {
741                                 p_goto_bol(p);
742                                 if (!piseol(p)) {
743                                         int col;
744                                         pset(q, p);
745                                         p_goto_indent(q, bw->o.indentc);
746                                         col = piscol(q);
747                                         bdel(p,q);
748                                         pfill(p,col+indwid,bw->o.indentc);
749                                 }
750                                 pnextl(p);
751                         }
752                         prm(p);
753                         prm(q);
754                 } else if (purity_check(bw->o.indentc,0)) {
755                         P *p = pdup(markb);
756
757                         while (p->byte < markk->byte) {
758                                 p_goto_bol(p);
759                                 if (!piseol(p))
760                                         while (piscol(p) < bw->o.istep) {
761                                                 binsc(p, bw->o.indentc);
762                                                 pgetc(p);
763                                         }
764                                 pnextl(p);
765                         }
766                         prm(p);
767                 } else {
768                         /* Purity failure */
769                         msgnw(bw->parent,UC "Selected lines not properly indented");
770                         return 1;
771                 }
772         }
773         return 0;
774 }
775
776 /* Indent less */
777
778 int ulindent(BW *bw)
779 {
780         if (square) {
781                 if (markb && markk && markb->b == markk->b && markb->byte <= markk->byte && markb->xcol <= markk->xcol) {
782                         P *p = pdup(markb);
783                         P *q = pdup(p);
784
785                         do {
786                                 pcol(p, markb->xcol);
787                                 while (piscol(p) < markb->xcol + bw->o.istep) {
788                                         int c = pgetc(p);
789
790                                         if (c != ' ' && c != '\t' && c != bw->o.indentc) {
791                                                 prm(p);
792                                                 prm(q);
793                                                 return -1;
794                                         }
795                                 }
796                         } while (pnextl(p) && p->line <= markk->line);
797                         pset(p, markb);
798                         do {
799                                 pcol(p, markb->xcol);
800                                 pset(q, p);
801                                 pcol(q, markb->xcol + bw->o.istep);
802                                 bdel(p, q);
803                         } while (pnextl(p) && p->line <= markk->line);
804                         prm(p);
805                         prm(q);
806                 }
807         } else {
808                 if (!markb || !markk || markb->b != markk->b || bw->cursor->byte < markb->byte || bw->cursor->byte > markk->byte || markb->byte == markk->byte) {
809                         setindent(bw);
810                 } else if (1 /* bw->o.purify */ && lindent_check(bw->o.indentc,bw->o.istep)) {
811                         P *p = pdup(markb);
812                         P *q = pdup(markb);
813                         int indwid;
814
815                         if (bw->o.indentc=='\t')
816                                 indwid = bw->o.tab * bw->o.istep;
817                         else
818                                 indwid = bw->o.istep;
819
820                         while (p->byte < markk->byte) {
821                                 p_goto_bol(p);
822                                 if (!piseol(p)) {
823                                         int col;
824                                         pset(q, p);
825                                         p_goto_indent(q, bw->o.indentc);
826                                         col = piscol(q);
827                                         bdel(p,q);
828                                         pfill(p,col-indwid,bw->o.indentc);
829                                 }
830                                 pnextl(p);
831                         }
832                         prm(p);
833                         prm(q);
834                 } else if (purity_check(bw->o.indentc,bw->o.istep)) {
835                         P *p = pdup(markb);
836                         P *q = pdup(p);
837
838                         p_goto_bol(p);
839                         while (p->byte < markk->byte) {
840                                 if (!piseol(p)) {
841                                         pset(q, p);
842                                         while (piscol(q) < bw->o.istep)
843                                                 pgetc(q);
844                                         bdel(p, q);
845                                 }
846                                 pnextl(p);
847                         }
848                         prm(p);
849                         prm(q);
850                 } else {
851                         /* Purity failure */
852                         msgnw(bw->parent, UC "Selected lines not properly indented");
853                         return 1;
854                 }
855         }
856         return 0;
857 }
858
859 /* Insert a file */
860
861 int doinsf(BW *bw, unsigned char *s, void *object, int *notify)
862 {
863         if (notify)
864                 *notify = 1;
865         if (square) {
866                 if (markv(1)) {
867                         B *tmp;
868                         long width = markk->xcol - markb->xcol;
869                         long height;
870                         int usetabs = ptabrect(markb,
871                                                markk->line - markb->line + 1,
872                                                markk->xcol);
873
874                         tmp = bload(s);
875                         if (error) {
876                                 msgnw(bw->parent, msgs[-error]);
877                                 brm(tmp);
878                                 vsrm(s);
879                                 return -1;
880                         }
881                         if (piscol(tmp->eof))
882                                 height = tmp->eof->line + 1;
883                         else
884                                 height = tmp->eof->line;
885                         if (bw->o.overtype) {
886                                 pclrrect(markb, long_max(markk->line - markb->line + 1, height), markk->xcol, usetabs);
887                                 pdelrect(markb, height, width + markb->xcol);
888                         }
889                         pinsrect(markb, tmp, width, usetabs);
890                         pdupown(markb, &markk);
891                         markk->xcol = markb->xcol;
892                         if (height) {
893                                 pline(markk, markk->line + height - 1);
894                                 pcol(markk, markb->xcol + width);
895                                 markk->xcol = markb->xcol + width;
896                         }
897                         brm(tmp);
898                         updall();
899                         vsrm(s);
900                         return 0;
901                 } else {
902                         vsrm(s);
903                         msgnw(bw->parent, UC "No block");
904                         return -1;
905                 }
906         } else {
907                 int ret = 0;
908                 B *tmp = bload(s);
909
910                 if (error) {
911                         msgnw(bw->parent, msgs[-error]), brm(tmp);
912                         ret = -1;
913                 } else {
914                         P *pafter;
915
916                         pafter = pdup(bw->cursor);
917                         pgetc(pafter);
918                         binsb(bw->cursor, tmp);
919                         prgetc(pafter);
920                         aftermove(bw->parent, pafter);
921                 }
922                 vsrm(s);
923                 bw->cursor->xcol = piscol(bw->cursor);
924                 return ret;
925         }
926 }
927
928
929 /* Filter highlighted block through a UNIX command */
930
931 static int filtflg = 0;
932
933 #if WANT_FORK
934 #define v_or_fork() fork()
935 #else
936 #define v_or_fork() vfork()
937 #endif
938
939 /*
940  * This isn't optimal, but until the home-brewn VM system is removed
941  * it is the best we can do: we cannot use bsavefd() in a concurrent
942  * child because it uses JOE's VM subsystem which then copies around
943  * content in file-backed memory that's not unshared, leading to da-
944  * ta corruption if the content is big enough.
945  *
946  * TBH, I'd rather love to see that VM system gone and revert to the
947  * JOE original code for dofilt... --mirabilos
948  */
949 static int dofilt(BW *bw, unsigned char *s, void *object, int *notify)
950 {
951         int fr[2];
952         int fw;
953         volatile int flg = 0;
954         unsigned char *tf;
955         const char *sh;
956 #if defined(HAVE_PUTENV) && (WANT_FORK || defined(HAVE_UNSETENV))
957         unsigned char *fname;
958 #endif
959
960         if (notify)
961                 *notify = 1;
962         if (markb && markk && !square && markb->b == bw->b && markk->b == bw->b && markb->byte == markk->byte) {
963                 flg = 1;
964                 goto ok;
965         } if (!markv(1)) {
966                 vsrm(s);
967                 msgnw(bw->parent, UC "No block");
968                 return -1;
969         }
970  ok:
971         if (pipe(fr)) {
972                 vsrm(s);
973                 msgnw(bw->parent, UC "Pipe error");
974                 return (-1);
975         }
976         if ((tf = mktmp(NULL, &fw)) == NULL) {
977                 close(fr[0]);
978                 close(fr[1]);
979                 vsrm(s);
980                 msgnw(bw->parent, UC "Cannot create temporary file");
981                 return (-1);
982         }
983         unlink((char *)tf);
984         vsrm(tf);
985         npartial(bw->parent->t->t);
986         ttclsn();
987         if (square) {
988                 B *tmp = pextrect(markb,
989                                   markk->line - markb->line + 1,
990                                   markk->xcol);
991
992                 bsavefd(tmp->bof, fw, tmp->eof->byte);
993         } else
994                 bsavefd(markb, fw, markk->byte - markb->byte);
995         lseek(fw, (off_t)0, SEEK_SET);
996 #if defined(HAVE_PUTENV) && (WANT_FORK || defined(HAVE_UNSETENV))
997         fname = vsncpy(NULL, 0, sc("JOE_FILENAME="));
998         tf = bw->b->name ? bw->b->name : (unsigned char *)"Unnamed";
999         fname = vsncpy(sv(fname), sz(tf));
1000 #if !WANT_FORK
1001         putenv((char *)fname);
1002 #endif
1003 #endif
1004         sh = getushell();
1005         if (!v_or_fork()) {
1006 #if defined(HAVE_PUTENV) && WANT_FORK
1007                 putenv((char *)fname);
1008 #endif
1009                 signrm(1);
1010                 close(0);
1011                 close(1);
1012                 close(2);
1013                 /* these dups will not fail */
1014                 if (dup(fw)) {}
1015                 if (dup(fr[1])) {}
1016                 if (dup(fr[1])) {}
1017                 close(fw);
1018                 close(fr[1]);
1019                 close(fr[0]);
1020                 execl(sh, sh, "-c", s, NULL);
1021                 _exit(0);
1022         }
1023         close(fr[1]);
1024         close(fw);
1025 #if defined(HAVE_PUTENV) && (WANT_FORK || defined(HAVE_UNSETENV))
1026 #if !WANT_FORK
1027         unsetenv("JOE_FILENAME");
1028 #endif
1029         vsrm(fname);
1030 #endif
1031         if (square) {
1032                 B *tmp;
1033                 long width = markk->xcol - markb->xcol;
1034                 long height;
1035                 int usetabs = ptabrect(markb,
1036                                        markk->line - markb->line + 1,
1037                                        markk->xcol);
1038
1039                 tmp = bread(fr[0], LONG_MAX);
1040                 if (piscol(tmp->eof))
1041                         height = tmp->eof->line + 1;
1042                 else
1043                         height = tmp->eof->line;
1044                 if (bw->o.overtype) {
1045                         pclrrect(markb, markk->line - markb->line + 1, markk->xcol, usetabs);
1046                         pdelrect(markb, long_max(height, markk->line - markb->line + 1), width + markb->xcol);
1047                 } else
1048                         pdelrect(markb, markk->line - markb->line + 1, markk->xcol);
1049                 pinsrect(markb, tmp, width, usetabs);
1050                 pdupown(markb, &markk);
1051                 markk->xcol = markb->xcol;
1052                 if (height) {
1053                         pline(markk, markk->line + height - 1);
1054                         pcol(markk, markb->xcol + width);
1055                         markk->xcol = markb->xcol + width;
1056                 }
1057                 if (lightoff)
1058                         unmark(bw);
1059                 brm(tmp);
1060                 updall();
1061         } else {
1062                 P *p = pdup(markk);
1063                 if (!flg)
1064                         prgetc(p);
1065                 bdel(markb, p);
1066                 binsb(p, bread(fr[0], LONG_MAX));
1067                 if (!flg) {
1068                         pset(p,markk);
1069                         prgetc(p);
1070                         bdel(p,markk);
1071                 }
1072                 prm(p);
1073                 if (lightoff)
1074                         unmark(bw);
1075         }
1076         close(fr[0]);
1077         wait(NULL);
1078         vsrm(s);
1079         ttopnn();
1080         if (filtflg)
1081                 unmark(bw);
1082         bw->cursor->xcol = piscol(bw->cursor);
1083         return 0;
1084 }
1085
1086 static B *filthist = NULL;
1087
1088 static void markall(BW *bw)
1089 {
1090         pdupown(bw->cursor->b->bof, &markb);
1091         markb->xcol = 0;
1092         pdupown(bw->cursor->b->eof, &markk);
1093         markk->xcol = piscol(markk);
1094         updall();
1095 }
1096
1097 static int checkmark(BW *bw)
1098 {
1099         if (!markv(1))
1100                 if (square)
1101                         return 2;
1102                 else {
1103                         markall(bw);
1104                         filtflg = 1;
1105                         return 1;
1106         } else {
1107                 filtflg = 0;
1108                 return 0;
1109         }
1110 }
1111
1112 int ufilt(BW *bw)
1113 {
1114         switch (checkmark(bw)) {
1115         case 0:
1116                 if (wmkpw(bw->parent, UC "Command to filter block through (^C to abort): ", &filthist, dofilt, NULL, NULL, utypebw, NULL, NULL, locale_map))
1117                         return 0;
1118                 else
1119                         return -1;
1120         case 1:
1121                 if (wmkpw(bw->parent, UC "Command to filter file through (^C to abort): ", &filthist, dofilt, NULL, NULL, utypebw, NULL, NULL, locale_map))
1122                         return 0;
1123                 else
1124                         return -1;
1125         case 2:
1126         default:
1127                 msgnw(bw->parent, UC "No block");
1128                 return -1;
1129         }
1130 }
1131
1132 /* Force region to lower case */
1133
1134 int ulower(BW *bw)
1135 {
1136         if (markv(1)) {
1137                 P *q;
1138                 P *p;
1139                 int c;
1140                 B *b = bcpy(markb,markk);
1141                 /* Leave one character in buffer to keep pointers set properly... */
1142                 q = pdup(markk);
1143                 prgetc(q);
1144                 bdel(markb,q);
1145                 b->o.charmap = markb->b->o.charmap;
1146                 p=pdup(b->bof);
1147                 while ((c=pgetc(p))!=NO_MORE_DATA) {
1148                         c = joe_tolower(b->o.charmap,c);
1149                         binsc(q,c);
1150                         pgetc(q);
1151                 }
1152                 prm(p);
1153                 bdel(q,markk);
1154                 prm(q);
1155                 brm(b);
1156                 bw->cursor->xcol = piscol(bw->cursor);
1157                 return 0;
1158         } else
1159                 return -1;
1160 }
1161
1162 /* Force region to upper case */
1163
1164 int uupper(BW *bw)
1165 {
1166         if (markv(1)) {
1167                 P *q;
1168                 P *p;
1169                 int c;
1170                 B *b = bcpy(markb,markk);
1171                 q = pdup(markk);
1172                 prgetc(q);
1173                 bdel(markb,q);
1174                 b->o.charmap = markb->b->o.charmap;
1175                 p=pdup(b->bof);
1176                 while ((c=pgetc(p))!=NO_MORE_DATA) {
1177                         c = joe_toupper(b->o.charmap,c);
1178                         binsc(q,c);
1179                         pgetc(q);
1180                 }
1181                 prm(p);
1182                 bdel(q,markk);
1183                 prm(q);
1184                 brm(b);
1185                 bw->cursor->xcol = piscol(bw->cursor);
1186                 return 0;
1187         } else
1188                 return -1;
1189 }