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