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