1 /* $MirOS: contrib/code/jupp/ublock.c,v 1.10 2013/08/19 22:04:15 tg Exp $ */
3 * Highlighted block functions
5 * (C) 1992 Joseph H. Allen
7 * This file is part of JOE (Joe's Own Editor)
16 #ifdef HAVE_SYS_TYPES_H
17 #include <sys/types.h>
19 #ifdef HAVE_SYS_WAIT_H
41 int square = 0; /* Set for rectangle mode */
42 int lightoff = 0; /* Set if highlighting should turn off
44 after block operations */
47 /* Global variables */
49 P *markb = NULL; /* Beginning and end of block */
52 /* Push markb & markk */
54 typedef struct marksav MARKSAV;
58 } markstack = { { &markstack, &markstack} };
59 MARKSAV markfree = { {&markfree, &markfree} };
64 MARKSAV *m = alitem(&markfree, sizeof(MARKSAV));
69 pdupown(markk, &m->markk);
71 pdupown(markb, &m->markb);
72 enqueb(MARKSAV, link, &markstack, m);
79 MARKSAV *m = markstack.link.prev;
81 if (m != &markstack) {
87 markk->owner = &markk;
90 markb->owner = &markb;
91 demote(MARKSAV, link, &markfree, m);
100 /* Return true if markb/markk are valid */
101 /* If r is set, swap markb with markk if necessary */
107 if (markb && markk && markb->b == markk->b && markk->byte > markb->byte && (!square || markk->xcol > markb->xcol)) {
109 } else if(autoswap && r && markb && markk && markb->b == markk->b && markb->byte > markk->byte && (!square || markk->xcol < markb->xcol)) {
111 prm(markb); markb=0; pdupown(markk, &markb);
112 prm(markk); markk=0; pdupown(p, &markk);
119 /* Rectangle-mode subroutines */
121 /* B *pextrect(P *org,long height,long left,long right);
122 * Copy a rectangle into a new buffer
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
129 B *pextrect(P *org, long int height, long int right)
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 */
141 binsb(z, bcpy(p, q));
152 /* void pdelrect(P *org,long height,long right);
153 * Delete a rectangle.
156 void pdelrect(P *org, long int height, long int right)
172 /* void pclrrect(P *org,long height,long right,int usetabs);
173 * Blank-out a rectangle.
176 void pclrrect(P *org, long int height, long int right, int usetabs)
189 pfill(p, pos, usetabs);
196 /* int ptabrect(P *org,long height,long right)
197 * Check if there are any TABs in a rectangle
200 int ptabrect(P *org, long int height, long int right)
208 while ((c = pgetc(p)) != NO_MORE_DATA && c != '\n') {
212 } else if (piscol(p) > right)
222 /* Insert rectangle */
224 void pinsrect(P *cur, B *tmp, long int width, int usetabs)
226 P *p = pdup(cur); /* We insert at & move this pointer */
227 P *q = pdup(tmp->bof); /* These are for scanning through 'tmp' */
231 while (pset(r, q), p_goto_eol(q), (q->line != tmp->eof->line || piscol(q))) {
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);
245 if (pgetc(q) == NO_MORE_DATA)
253 /* Block functions */
259 pdupown(bw->cursor, &markb);
260 markb->xcol = bw->cursor->xcol;
268 if (marking && markb)
275 int ubegin_marking(BW *bw)
278 /* We're marking now... don't stop */
280 else if (markv(0) && bw->cursor->b==markb->b) {
281 /* Try to extend current block */
282 if (bw->cursor->byte==markb->byte) {
287 } else if(bw->cursor->byte==markk->byte) {
293 /* Start marking - no message */
301 int utoggle_marking(BW *bw)
303 if (markv(0) && bw->cursor->b==markb->b && bw->cursor->byte>=markb->byte && bw->cursor->byte<=markk->byte) {
304 /* Just clear selection */
309 msgnw(bw->parent, US "Selection cleared.");
312 /* Clear selection and start new one */
317 msgnw(bw->parent, US "Selection started.");
319 } else if (markb && markb->b==bw->cursor->b) {
321 if (bw->cursor->byte<markb->byte) {
322 pdupown(markb, &markk);
324 pdupown(bw->cursor, &markb);
325 markb->xcol = bw->cursor->xcol;
327 pdupown(bw->cursor, &markk);
328 markk->xcol = bw->cursor->xcol;
330 updall(); /* Because other windows could be changed */
334 msgnw(bw->parent, US "Selection started.");
350 pdupown(bw->cursor, &markk);
351 markk->xcol = bw->cursor->xcol;
370 p_goto_bol(bw->cursor);
375 pcol(bw->cursor, bw->cursor->xcol);
381 if (markb && markb->b == bw->b) {
382 pset(bw->cursor, markb);
390 if (markk && markk->b == bw->b) {
391 pset(bw->cursor, markk);
399 if (markb && markb->b == bw->b) {
410 int utomarkbk(BW *bw)
412 if (markb && markb->b == bw->b && bw->cursor->byte != markb->byte) {
413 pset(bw->cursor, markb);
415 } else if (markk && markk->b == bw->b && bw->cursor->byte != markk->byte) {
416 pset(bw->cursor, markk);
428 if (bw->o.overtype) {
429 long ocol = markk->xcol;
431 pclrrect(markb, markk->line - markb->line + 1, markk->xcol, ptabrect(markb, markk->line - markb->line + 1, markk->xcol));
435 pdelrect(markb, markk->line - markb->line + 1, markk->xcol);
441 msgnw(bw->parent, US "No block");
447 /* Special delete block function for PICO */
449 int upicokill(BW *bw)
455 if (bw->o.overtype) {
456 long ocol = markk->xcol;
458 pclrrect(markb, markk->line - markb->line + 1, markk->xcol, ptabrect(markb, markk->line - markb->line + 1, markk->xcol));
462 pdelrect(markb, markk->line - markb->line + 1, markk->xcol);
472 /* Move highlighted block */
477 if (markb->b->rdonly) {
478 msgnw(bw->parent, US "Read only");
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);
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);
508 pline(markk, markk->line + height - 1);
509 pcol(markk, markb->xcol + width);
510 markk->xcol = markb->xcol + width;
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;
516 binsb(bw->cursor, bcpy(markb, markk));
529 msgnw(bw->parent, US "No block");
533 /* Duplicate highlighted block */
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);
545 pdelrect(bw->cursor, height, piscol(bw->cursor) + width);
546 pinsrect(bw->cursor, tmp, width, usetabs);
553 pline(markk, markk->line + height - 1);
554 pcol(markk, markb->xcol + width);
555 markk->xcol = markb->xcol + width;
559 long size = markk->byte - markb->byte;
560 B *tmp = bcpy(markb, markk);
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)
573 binsb(bw->cursor, tmp);
585 msgnw(bw->parent, US "No block");
590 /* Write highlighted block to a file */
591 /* This is called by ublksave in ufile.c */
593 int dowrite(BW *bw, unsigned char *s, void *object, int *notify)
601 B *tmp = pextrect(markb,
602 markk->line - markb->line + 1,
605 if ((fl = bsave(tmp->bof, s, tmp->eof->byte, 0)) != 0) {
606 msgnw(bw->parent, msgs[-fl]);
618 if ((fl = bsave(markb, s, markk->byte - markb->byte, 0)) != 0) {
619 msgnw(bw->parent, msgs[-fl]);
629 msgnw(bw->parent, US "No block");
634 /* Set highlighted block on a program block */
636 void setindent(BW *bw)
641 if (pisblank(bw->cursor))
644 p = pdup(bw->cursor);
646 indent = pisindent(p);
653 } while (pisindent(p) >= indent || pisblank(p));
655 /* Maybe skip blank lines at beginning */
667 } while (pisindent(q) >= indent || pisblank(q));
668 /* Maybe skip blank lines at end */
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 */
682 int purity_check(int c, int n)
685 while (p->byte < markk->byte) {
688 if (!n && c==' ' && brc(p)=='\t') {
691 } else if (!piseol(p))
703 /* Left indent check */
704 /* Verify that there is enough whitespace to do the left indent */
706 int lindent_check(int c, int n)
711 indwid = n * p->b->o.tab;
714 while (p->byte < markk->byte) {
716 if (!piseol(p) && pisindent(p)<indwid) {
731 if (markb && markk && markb->b == markk->b && markb->byte <= markk->byte && markb->xcol <= markk->xcol) {
735 pcol(p, markb->xcol);
736 pfill(p, markb->xcol + bw->o.istep, bw->o.indentc);
737 } while (pnextl(p) && p->line <= markk->line);
741 if (!markb || !markk || markb->b != markk->b || bw->cursor->byte < markb->byte || bw->cursor->byte > markk->byte || markb->byte == markk->byte) {
743 } else if ( 1 /* bw->o.purify */) {
748 if (bw->o.indentc=='\t')
749 indwid = bw->o.tab * bw->o.istep;
751 indwid = bw->o.istep;
753 while (p->byte < markk->byte) {
758 p_goto_indent(q, bw->o.indentc);
761 pfill(p,col+indwid,bw->o.indentc);
767 } else if (purity_check(bw->o.indentc,0)) {
770 while (p->byte < markk->byte) {
773 while (piscol(p) < bw->o.istep) {
774 binsc(p, bw->o.indentc);
782 msgnw(bw->parent,US "Selected lines not properly indented");
794 if (markb && markk && markb->b == markk->b && markb->byte <= markk->byte && markb->xcol <= markk->xcol) {
799 pcol(p, markb->xcol);
800 while (piscol(p) < markb->xcol + bw->o.istep) {
803 if (c != ' ' && c != '\t' && c != bw->o.indentc) {
809 } while (pnextl(p) && p->line <= markk->line);
812 pcol(p, markb->xcol);
814 pcol(q, markb->xcol + bw->o.istep);
816 } while (pnextl(p) && p->line <= markk->line);
821 if (!markb || !markk || markb->b != markk->b || bw->cursor->byte < markb->byte || bw->cursor->byte > markk->byte || markb->byte == markk->byte) {
823 } else if (1 /* bw->o.purify */ && lindent_check(bw->o.indentc,bw->o.istep)) {
828 if (bw->o.indentc=='\t')
829 indwid = bw->o.tab * bw->o.istep;
831 indwid = bw->o.istep;
833 while (p->byte < markk->byte) {
838 p_goto_indent(q, bw->o.indentc);
841 pfill(p,col-indwid,bw->o.indentc);
847 } else if (purity_check(bw->o.indentc,bw->o.istep)) {
852 while (p->byte < markk->byte) {
855 while (piscol(q) < bw->o.istep)
865 msgnw(bw->parent,US "Selected lines not properly indented");
874 int doinsf(BW *bw, unsigned char *s, void *object, int *notify)
881 long width = markk->xcol - markb->xcol;
883 int usetabs = ptabrect(markb,
884 markk->line - markb->line + 1,
889 msgnw(bw->parent, msgs[-error]);
893 if (piscol(tmp->eof))
894 height = tmp->eof->line + 1;
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);
901 pinsrect(markb, tmp, width, usetabs);
902 pdupown(markb, &markk);
903 markk->xcol = markb->xcol;
905 pline(markk, markk->line + height - 1);
906 pcol(markk, markb->xcol + width);
907 markk->xcol = markb->xcol + width;
913 msgnw(bw->parent, US "No block");
920 msgnw(bw->parent, msgs[-error]), brm(tmp);
925 pafter = pdup(bw->cursor);
927 binsb(bw->cursor, tmp);
929 aftermove(bw->parent, pafter);
932 bw->cursor->xcol = piscol(bw->cursor);
938 /* Filter highlighted block through a UNIX command */
940 static int filtflg = 0;
943 * This isn't optimal, but until the home-brewn VM system is removed
944 * it is the best we can do: we cannot use bsavefd() in a concurrent
945 * child because it uses JOE's VM subsystem which then copies around
946 * content in file-backed memory that's not unshared, leading to da-
947 * ta corruption if the content is big enough.
949 * TBH, I'd rather love to see that VM system gone and revert to the
950 * JOE original code for dofilt... --mirabilos
952 static int dofilt(BW *bw, unsigned char *s, void *object, int *notify)
961 if (markb && markk && !square && markb->b == bw->b && markk->b == bw->b && markb->byte == markk->byte) {
965 msgnw(bw->parent, US "No block");
971 msgnw(bw->parent, US "Pipe error");
974 if ((tf = mktmp(NULL, &fw)) == NULL) {
977 msgnw(bw->parent, US "Cannot create temporary file");
982 npartial(bw->parent->t->t);
985 B *tmp = pextrect(markb,
986 markk->line - markb->line + 1,
989 bsavefd(tmp->bof, fw, tmp->eof->byte);
991 bsavefd(markb, fw, markk->byte - markb->byte);
992 lseek(fw, (off_t)0, SEEK_SET);
996 unsigned char *fname, *name;
1003 /* these dups will not fail */
1011 fname = vsncpy(NULL, 0, sc("JOE_FILENAME="));
1012 name = bw->b->name ? bw->b->name : (unsigned char *)"Unnamed";
1013 if((len = slen(name)) >= 512) /* limit filename length */
1015 fname = vsncpy(sv(fname), name, len);
1016 putenv((char *)fname);
1019 execl(sh, sh, "-c", s, NULL);
1026 long width = markk->xcol - markb->xcol;
1028 int usetabs = ptabrect(markb,
1029 markk->line - markb->line + 1,
1032 tmp = bread(fr[0], MAXLONG);
1033 if (piscol(tmp->eof))
1034 height = tmp->eof->line + 1;
1036 height = tmp->eof->line;
1037 if (bw->o.overtype) {
1038 pclrrect(markb, markk->line - markb->line + 1, markk->xcol, usetabs);
1039 pdelrect(markb, long_max(height, markk->line - markb->line + 1), width + markb->xcol);
1041 pdelrect(markb, markk->line - markb->line + 1, markk->xcol);
1042 pinsrect(markb, tmp, width, usetabs);
1043 pdupown(markb, &markk);
1044 markk->xcol = markb->xcol;
1046 pline(markk, markk->line + height - 1);
1047 pcol(markk, markb->xcol + width);
1048 markk->xcol = markb->xcol + width;
1059 binsb(p, bread(fr[0], MAXLONG));
1075 bw->cursor->xcol = piscol(bw->cursor);
1079 static B *filthist = NULL;
1081 static void markall(BW *bw)
1083 pdupown(bw->cursor->b->bof, &markb);
1085 pdupown(bw->cursor->b->eof, &markk);
1086 markk->xcol = piscol(markk);
1090 static int checkmark(BW *bw)
1108 msgnw(bw->parent, "Sorry, no sub-processes in DOS (yet)");
1111 switch (checkmark(bw)) {
1113 if (wmkpw(bw->parent, US "Command to filter block through (^C to abort): ", &filthist, dofilt, NULL, NULL, utypebw, NULL, NULL, locale_map))
1118 if (wmkpw(bw->parent, US "Command to filter file through (^C to abort): ", &filthist, dofilt, NULL, NULL, utypebw, NULL, NULL, locale_map))
1124 msgnw(bw->parent, US "No block");
1130 /* Force region to lower case */
1138 B *b = bcpy(markb,markk);
1139 /* Leave one character in buffer to keep pointers set properly... */
1143 b->o.charmap = markb->b->o.charmap;
1145 while ((c=pgetc(p))!=NO_MORE_DATA) {
1146 c = joe_tolower(b->o.charmap,c);
1154 bw->cursor->xcol = piscol(bw->cursor);
1160 /* Force region to upper case */
1168 B *b = bcpy(markb,markk);
1172 b->o.charmap = markb->b->o.charmap;
1174 while ((c=pgetc(p))!=NO_MORE_DATA) {
1175 c = joe_toupper(b->o.charmap,c);
1183 bw->cursor->xcol = piscol(bw->cursor);