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