Add support for tab-completion when selecting by rule
[alpine.git] / pico / word.c
blobd4cf886aec1b01b1b743f30d80c7cf1a36755e9d
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * Program: Word at a time routines
18 * The routines in this file implement commands that work word at a time.
19 * There are all sorts of word mode commands. If I do any sentence and/or
20 * paragraph mode commands, they are likely to be put in this file.
23 #include "headers.h"
26 int fpnewline(UCS *quote);
27 int fillregion(UCS *qstr, REGION *addedregion);
28 int setquotelevelinregion(int quotelevel, REGION *addedregion);
29 int is_user_separator(UCS c);
32 /* Word wrap on n-spaces. Back-over whatever precedes the point on the current
33 * line and stop on the first word-break or the beginning of the line. If we
34 * reach the beginning of the line, jump back to the end of the word and start
35 * a new line. Otherwise, break the line at the word-break, eat it, and jump
36 * back to the end of the word.
37 * Returns TRUE on success, FALSE on errors.
39 int
40 wrapword(void)
42 register int cnt; /* size of word wrapped to next line */
43 register int bp; /* index to wrap on */
44 register int first = -1;
45 int wid, ww;
47 if(curwp->w_doto <= 0) /* no line to wrap? */
48 return(FALSE);
50 wid = 0;
51 for(bp = cnt = 0; cnt < llength(curwp->w_dotp) && !bp; cnt++){
52 if(ucs4_isspace(lgetc(curwp->w_dotp, cnt).c)){
53 first = 0;
54 if(lgetc(curwp->w_dotp, cnt).c == TAB){
55 ++wid;
56 while(wid & 0x07)
57 ++wid;
59 else
60 ++wid;
62 else{
63 ww = wcellwidth((UCS) lgetc(curwp->w_dotp, cnt).c);
64 wid += (ww >= 0 ? ww : 1);
65 if(!first)
66 first = cnt;
69 if(first > 0 && wid > fillcol)
70 bp = first;
73 if(!bp)
74 return(FALSE);
76 /* bp now points to the first character of the next line */
77 cnt = curwp->w_doto - bp;
78 curwp->w_doto = bp;
80 if(!lnewline()) /* break the line */
81 return(FALSE);
84 * if there's a line below, it doesn't start with whitespace
85 * and there's room for this line...
87 if(!(curbp->b_flag & BFWRAPOPEN)
88 && lforw(curwp->w_dotp) != curbp->b_linep
89 && llength(lforw(curwp->w_dotp))
90 && !ucs4_isspace(lgetc(lforw(curwp->w_dotp), 0).c)
91 && (llength(curwp->w_dotp) + llength(lforw(curwp->w_dotp)) < fillcol)){
92 gotoeol(0, 1); /* then pull text up from below */
93 if(lgetc(curwp->w_dotp, curwp->w_doto - 1).c != ' ')
94 linsert(1, ' ');
96 forwdel(0, 1);
97 gotobol(0, 1);
100 curbp->b_flag &= ~BFWRAPOPEN; /* don't open new line next wrap */
101 /* restore dot (account for NL) */
102 if(cnt && !forwchar(0, cnt < 0 ? cnt-1 : cnt))
103 return(FALSE);
105 return(TRUE);
110 * Move the cursor backward by "n" words. All of the details of motion are
111 * performed by the "backchar" and "forwchar" routines. Error if you try to
112 * move beyond the buffers.
115 backword(int f, int n)
117 if (n < 0)
118 return (forwword(f, -n));
119 if (backchar_no_header_editor(FALSE, 1) == FALSE)
120 return (FALSE);
121 while (n--) {
122 while (inword() == FALSE) {
123 if (backchar_no_header_editor(FALSE, 1) == FALSE)
124 return (FALSE);
126 while (inword() != FALSE) {
127 if (backchar_no_header_editor(FALSE, 1) == FALSE)
128 return (FALSE);
131 return (forwchar(FALSE, 1));
135 * Move the cursor forward by the specified number of words. All of the motion
136 * is done by "forwchar". Error if you try and move beyond the buffer's end.
139 forwword(int f, int n)
141 if (n < 0)
142 return (backword(f, -n));
143 while (n--) {
144 #if NFWORD
145 while (inword() != FALSE) {
146 if (forwchar(FALSE, 1) == FALSE)
147 return (FALSE);
149 #endif
150 while (inword() == FALSE) {
151 if (forwchar(FALSE, 1) == FALSE)
152 return (FALSE);
154 #if NFWORD == 0
155 while (inword() != FALSE) {
156 if (forwchar(FALSE, 1) == FALSE)
157 return (FALSE);
159 #endif
161 return(TRUE);
165 ucs4_isalnum(UCS c)
167 return((c && c <= 0x7f && isalnum((unsigned char) c))
168 || (c >= 0xA0 && !SPECIAL_SPACE(c)));
172 ucs4_isalpha(UCS c)
174 return((c && c <= 0x7f && isalpha((unsigned char) c))
175 || (c >= 0xA0 && !SPECIAL_SPACE(c)));
179 ucs4_isspace(UCS c)
181 return((c < 0xff && isspace((unsigned char) c)) || SPECIAL_SPACE(c));
185 ucs4_ispunct(UCS c)
187 return !ucs4_isalnum(c) && !ucs4_isspace(c);
190 #ifdef MAYBELATER
192 * Move the cursor forward by the specified number of words. As you move,
193 * convert any characters to upper case. Error if you try and move beyond the
194 * end of the buffer. Bound to "M-U".
197 upperword(int f, int n)
199 register int c;
200 CELL ac;
202 ac.a = 0;
203 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
204 return(rdonly()); /* we are in read only mode */
205 if (n < 0)
206 return (FALSE);
207 while (n--) {
208 while (inword() == FALSE) {
209 if (forwchar(FALSE, 1) == FALSE)
210 return (FALSE);
212 while (inword() != FALSE) {
213 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
214 if (c>='a' && c<='z') {
215 ac.c = (c -= 'a'-'A');
216 lputc(curwp->w_dotp, curwp->w_doto, ac);
217 lchange(WFHARD);
219 if (forwchar(FALSE, 1) == FALSE)
220 return (FALSE);
223 return (TRUE);
227 * Move the cursor forward by the specified number of words. As you move
228 * convert characters to lower case. Error if you try and move over the end of
229 * the buffer. Bound to "M-L".
232 lowerword(int f, int n)
234 register int c;
235 CELL ac;
237 ac.a = 0;
238 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
239 return(rdonly()); /* we are in read only mode */
240 if (n < 0)
241 return (FALSE);
242 while (n--) {
243 while (inword() == FALSE) {
244 if (forwchar(FALSE, 1) == FALSE)
245 return (FALSE);
247 while (inword() != FALSE) {
248 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
249 if (c>='A' && c<='Z') {
250 ac.c (c += 'a'-'A');
251 lputc(curwp->w_dotp, curwp->w_doto, ac);
252 lchange(WFHARD);
254 if (forwchar(FALSE, 1) == FALSE)
255 return (FALSE);
258 return (TRUE);
262 * Move the cursor forward by the specified number of words. As you move
263 * convert the first character of the word to upper case, and subsequent
264 * characters to lower case. Error if you try and move past the end of the
265 * buffer. Bound to "M-C".
268 capword(int f, int n)
270 register int c;
271 CELL ac;
273 ac.a = 0;
274 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
275 return(rdonly()); /* we are in read only mode */
276 if (n < 0)
277 return (FALSE);
278 while (n--) {
279 while (inword() == FALSE) {
280 if (forwchar(FALSE, 1) == FALSE)
281 return (FALSE);
283 if (inword() != FALSE) {
284 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
285 if (c>='a' && c<='z') {
286 ac.c = (c -= 'a'-'A');
287 lputc(curwp->w_dotp, curwp->w_doto, ac);
288 lchange(WFHARD);
290 if (forwchar(FALSE, 1) == FALSE)
291 return (FALSE);
292 while (inword() != FALSE) {
293 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
294 if (c>='A' && c<='Z') {
295 ac.c = (c += 'a'-'A');
296 lputc(curwp->w_dotp, curwp->w_doto, ac);
297 lchange(WFHARD);
299 if (forwchar(FALSE, 1) == FALSE)
300 return (FALSE);
304 return (TRUE);
308 * Kill forward by "n" words. Remember the location of dot. Move forward by
309 * the right number of words. Put dot back where it was and issue the kill
310 * command for the right number of characters. Bound to "M-D".
313 delfword(int f, int n)
315 register long size;
316 register LINE *dotp;
317 register int doto;
319 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
320 return(rdonly()); /* we are in read only mode */
321 if (n < 0)
322 return (FALSE);
323 dotp = curwp->w_dotp;
324 doto = curwp->w_doto;
325 size = 0L;
326 while (n--) {
327 #if NFWORD
328 while (inword() != FALSE) {
329 if (forwchar(FALSE,1) == FALSE)
330 return(FALSE);
331 ++size;
333 #endif
334 while (inword() == FALSE) {
335 if (forwchar(FALSE, 1) == FALSE)
336 return (FALSE);
337 ++size;
339 #if NFWORD == 0
340 while (inword() != FALSE) {
341 if (forwchar(FALSE, 1) == FALSE)
342 return (FALSE);
343 ++size;
345 #endif
347 curwp->w_dotp = dotp;
348 curwp->w_doto = doto;
349 return (ldelete(size, kinsert));
353 * Kill backwards by "n" words. Move backwards by the desired number of words,
354 * counting the characters. When dot is finally moved to its resting place,
355 * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
358 delbword(int f, int n)
360 register long size;
362 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
363 return(rdonly()); /* we are in read only mode */
364 if (n < 0)
365 return (FALSE);
366 if (backchar(FALSE, 1) == FALSE)
367 return (FALSE);
368 size = 0L;
369 while (n--) {
370 while (inword() == FALSE) {
371 if (backchar(FALSE, 1) == FALSE)
372 return (FALSE);
373 ++size;
375 while (inword() != FALSE) {
376 if (backchar(FALSE, 1) == FALSE)
377 return (FALSE);
378 ++size;
381 if (forwchar(FALSE, 1) == FALSE)
382 return (FALSE);
383 return (ldelete(size, kinsert));
385 #endif /* MAYBELATER */
388 * Return TRUE if the character at dot is a character that is considered to be
389 * part of a word.
392 inword(void)
394 if(curwp->w_doto < llength(curwp->w_dotp))
396 if(ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto).c))
398 return(TRUE);
400 else if(ucs4_ispunct(lgetc(curwp->w_dotp, curwp->w_doto).c)
401 && !is_user_separator(lgetc(curwp->w_dotp, curwp->w_doto).c))
403 if((curwp->w_doto > 0) &&
404 ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto - 1).c) &&
405 (curwp->w_doto + 1 < llength(curwp->w_dotp)) &&
406 ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto + 1).c))
408 return(TRUE);
413 return(FALSE);
418 is_user_separator(UCS c)
420 UCS *u;
422 if(glo_wordseps)
423 for(u = glo_wordseps; *u; u++)
424 if(*u == c)
425 return 1;
427 return 0;
430 void
431 do_quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen)
433 register int i, j;
434 int qstart, qend, k;
437 * The method for determining the quote string is:
438 * 1) strip leading and trailing whitespace from q
439 * 2) add all leading whitespace to buf
440 * 3) check for q
441 * 4) if q, append q to buf and any trailing whitespace
442 * 5) repeat steps 3 and 4 as necessary
444 * q in the future could be made to be an array of (UCS *)'s
445 * (">" and whatever the user's quote_string is)
448 *buf = '\0';
450 if(l == NULL)
451 return;
453 /* count leading whitespace as part of the quote */
454 for(j = 0; j <= llength(l) && lgetc(l, j).c == ' ' && j+1 < buflen; j++)
455 buf[j] = lgetc(l, j).c;
456 buf[j] = '\0';
458 if(q == NULL || *q == '\0')
459 return;
461 /* pare down q so it contains no leading or trailing whitespace */
462 for(i = 0; q[i] == ' '; i++);
463 qstart = i;
464 for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--);
465 qend = i;
467 /* for quote strings that are blanks, chop buf to the length of q */
468 if(qend <= qstart){
469 if(ucs4_strlen(q) < buflen)
470 buf[ucs4_strlen(q)] = '\0';
471 return;
474 while(j <= llength(l)){
475 for(i = qstart; j <= llength(l) && i < qend; i++, j++)
476 if(q[i] != lgetc(l, j).c)
477 return;
479 if(i >= qend){
480 if(ucs4_strlen(buf) + qend - qstart < (buflen - 1))
481 ucs4_strncat(buf, q + qstart, qend - qstart);
485 * if we're this far along, we've matched a quote string,
486 * and should now add the following white space.
488 for(k = ucs4_strlen(buf);
489 j <= llength(l) && lgetc(l,j).c == ' ' && (k + 1 < buflen);
490 j++, k++){
491 buf[k] = lgetc(l,j).c;
493 buf[k] = '\0';
495 if(j > llength(l))
496 return;
501 * Return number of quotes if whatever starts the line matches the quote string
504 quote_match(UCS *q, LINE *gl, UCS *bufl, size_t buflen)
506 LINE *nl = gl != curbp->b_linep ? lforw(gl) : NULL;
507 LINE *pl = lback(gl) != curbp->b_linep ? lback(gl) : NULL;
508 UCS bufp[NSTRING], bufn[NSTRING];
509 int i, j, qstart, qend;
510 int quoted_line = 0;
512 do_quote_match(q, pl, bufp, sizeof(bufp)); /* previous line */
513 do_quote_match(q, gl, bufl, buflen); /* given line */
514 do_quote_match(q, nl, bufn, sizeof(bufn)); /* next line */
516 if(!ucs4_strcmp(bufp, bufl) || !ucs4_strcmp(bufl, bufn))
517 return ucs4_strlen(bufl);
519 /* is this line quoted? */
520 if(q && *q){
521 /* pare down q so it contains no leading or trailing whitespace */
522 for(i = 0; q[i] == ' '; i++);
523 qstart = i;
524 for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--);
525 qend = i;
526 for(i = 0; i < llength(gl)
527 && i + qstart < qend
528 && lgetc(gl, i).c == q[i+qstart]; i++);
529 if(i + qstart == qend)
530 quoted_line = 1;
533 /* compare bufl and bufn */
534 for(i = 0; bufl[i] && bufn[i] && bufl[i] == bufn[i]; i++);
536 /* go back to last non-space character */
537 for(; i > 0 && bufl[i-1] == ' '; i--);
539 /* do bufl and bufn differ only in spaces? */
540 for(j = i; bufl[j] && bufl[j] == ' '; j++);
542 /* if they differ only on trailing spaces, chop bufl to agree with bufn */
543 if (!bufl[j] )
544 bufl[Pmaster && quoted_line ? (j > i ? i+1 : i) : i] = '\0';
546 return ucs4_strlen(bufl);
550 /* Justify the entire buffer instead of just a paragraph */
552 fillbuf(int f, int n)
554 LINE *eobline;
555 REGION region;
557 if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
558 return(rdonly()); /* we are in read only mode */
560 else if (fillcol == 0) { /* no fill column set */
561 mlwrite_utf8("No fill column set", NULL);
562 return(FALSE);
565 if((lastflag & CFFILL) && (lastflag & CFFLBF)){
566 /* no use doing a full justify twice */
567 thisflag |= (CFFLBF | CFFILL);
568 return(TRUE);
571 /* record the pointer of the last line */
572 if(gotoeob(FALSE, 1) == FALSE)
573 return(FALSE);
575 eobline = curwp->w_dotp; /* last line of buffer */
576 if(!llength(eobline))
577 eobline = lback(eobline);
579 /* and back to the beginning of the buffer */
580 gotobob(FALSE, 1);
582 thisflag |= CFFLBF; /* CFFILL also gets set in fillpara */
584 if(!Pmaster)
585 sgarbk = TRUE;
587 curwp->w_flag |= WFMODE;
590 * clear the kill buffer, that's where we'll store undo
591 * information, we can't do the fill buffer because
592 * fillpara relies on its contents
594 kdelete();
595 curwp->w_doto = 0;
596 getregion(&region, eobline, llength(eobline));
598 /* Put full message in the kill buffer for undo */
599 if(!ldelete(region.r_size, kinsert))
600 return(FALSE);
602 /* before yank'ing, clear lastflag so we don't just unjustify */
603 lastflag &= ~(CFFLBF | CFFILL);
605 /* Now in kill buffer, bring back text to use in fillpara */
606 yank(FALSE, 1);
608 gotobob(FALSE, 1);
610 /* call fillpara until we're at the end of the buffer */
611 while(curwp->w_dotp != curbp->b_linep)
612 if(!(fillpara(FALSE, 1)))
613 return(FALSE);
615 return(TRUE);
620 * Fill the current paragraph according to the current fill column
623 fillpara(int f, int n)
625 UCS *qstr, qstr2[NSTRING], c;
626 int quotelevel = -1;
627 REGION addedregion;
628 char action = 'P';
630 if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
631 return(rdonly()); /* we are in read only mode */
633 else if (fillcol == 0) { /* no fill column set */
634 mlwrite_utf8("No fill column set", NULL);
635 return(FALSE);
637 else if(curwp->w_dotp == curbp->b_linep && !curwp->w_markp) /* don't wrap! */
638 return(FALSE);
641 * If there is already a region set, then we may use it
642 * instead of the current paragraph.
645 if(curwp->w_markp){
646 int k, rv;
647 KEYMENU menu_justify[12];
648 char prompt[100];
650 for(k = 0; k < 12; k++){
651 menu_justify[k].name = NULL;
652 KS_OSDATASET(&menu_justify[k], KS_NONE);
655 menu_justify[1].name = "R";
656 menu_justify[1].label = "[" N_("Region") "]";
657 menu_justify[6].name = "^C";
658 menu_justify[6].label = N_("Cancel");
659 menu_justify[7].name = "P";
660 menu_justify[7].label = N_("Paragraph");
661 menu_justify[2].name = "Q";
662 menu_justify[2].label = N_("Quotelevel");
664 wkeyhelp(menu_justify); /* paint menu */
665 sgarbk = TRUE;
666 if(Pmaster && curwp)
667 curwp->w_flag |= WFMODE;
669 strncpy(prompt, "justify Region, Paragraph; or fix Quotelevel ? ", sizeof(prompt));
670 prompt[sizeof(prompt)-1] = '\0';
671 mlwrite_utf8(prompt, NULL);
672 (*term.t_rev)(1);
673 rv = -1;
674 while(1){
675 switch(c = GetKey()){
677 case (CTRL|'C') : /* Bail out! */
678 case F2 :
679 pputs_utf8(_("ABORT"), 1);
680 rv = ABORT;
681 emlwrite("", NULL);
682 break;
684 case (CTRL|'M') : /* default */
685 case 'r' :
686 case 'R' :
687 case F3 :
688 pputs_utf8(_("Region"), 1);
689 rv = 'R';
690 break;
692 case 'p' :
693 case 'P' :
694 case F7 :
695 pputs_utf8(_("Paragraph"), 1);
696 rv = 'P';
697 break;
699 case 'q' :
700 case 'Q' :
701 case F8 :
702 case '0' : case '1' : case '2' : case '3' : case '4' :
703 case '5' : case '6' : case '7' : case '8' : case '9' :
704 pputs_utf8(_("Quotelevel"), 1);
705 while(rv == -1){
706 switch(c){
707 case 'q' :
708 case 'Q' :
709 case F8 :
710 {char num[20];
712 num[0] = '\0';
713 switch(mlreplyd_utf8("Quote Level ? ", num, sizeof(num), QNORML, NULL)){
714 case TRUE:
715 if(isdigit(num[0])){
716 quotelevel = atoi(num);
717 if(quotelevel < 0){
718 emlwrite("Quote Level cannot be negative", NULL);
719 sleep(3);
721 else if(quotelevel > 20){
722 emlwrite("Quote Level should be less than 20", NULL);
723 rv = ABORT;
725 else{
726 rv = 'Q';
729 else if(num[0]){
730 emlwrite("Quote Level should be a number", NULL);
731 sleep(3);
734 break;
736 case HELPCH:
737 emlwrite("Enter the number of quotes you want before the text", NULL);
738 sleep(3);
739 break;
741 default:
742 emlwrite("Quote Level is a number", NULL);
743 rv = ABORT;
744 break;
748 break;
750 case '0' : case '1' : case '2' : case '3' : case '4' :
751 case '5' : case '6' : case '7' : case '8' : case '9' :
752 rv = 'Q';
753 quotelevel = (int) (c - '0');
754 break;
758 break;
760 case (CTRL|'G') :
761 if(term.t_mrow == 0 && km_popped == 0){
762 movecursor(term.t_nrow-2, 0);
763 peeol();
764 term.t_mrow = 2;
765 (*term.t_rev)(0);
766 wkeyhelp(menu_justify);
767 mlwrite_utf8(prompt, NULL);
768 (*term.t_rev)(1);
769 sgarbk = TRUE; /* mark menu dirty */
770 km_popped++;
771 break;
773 /* else fall through */
775 default:
776 (*term.t_beep)();
778 case NODATA :
779 break;
782 (*term.t_flush)();
783 if(rv != -1){
784 (*term.t_rev)(0);
785 if(km_popped){
786 term.t_mrow = 0;
787 movecursor(term.t_nrow, 0);
788 peeol();
789 sgarbf = 1;
790 km_popped = 0;
793 action = rv;
794 break;
798 if(action != ABORT)
799 emlwrite("", NULL);
802 if(action == 'R' && curwp->w_markp){
803 /* let yank() know that it may be restoring a paragraph */
804 thisflag |= CFFILL;
806 if(!Pmaster)
807 sgarbk = TRUE;
809 curwp->w_flag |= WFMODE;
811 swap_mark_and_dot_if_mark_comes_first();
813 /* determine if we're justifying quoted text or not */
814 qstr = quote_match(glo_quote_str,
815 curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp,
816 qstr2, NSTRING)
817 && *qstr2 ? qstr2 : NULL;
821 * Fillregion moves dot to the end of the filled region.
823 if(!fillregion(qstr, &addedregion))
824 return(FALSE);
826 set_last_region_added(&addedregion);
828 else if(action == 'P'){
831 * Justfiy the current paragraph.
834 if(curwp->w_markp) /* clear mark if already set */
835 setmark(0,0);
837 if(gotoeop(FALSE, 1) == FALSE)
838 return(FALSE);
840 /* determine if we're justifying quoted text or not */
841 qstr = quote_match(glo_quote_str,
842 curwp->w_dotp, qstr2, NSTRING)
843 && *qstr2 ? qstr2 : NULL;
845 setmark(0,0); /* mark last line of para */
847 /* jump back to the beginning of the paragraph */
848 gotobop(FALSE, 1);
850 /* let yank() know that it may be restoring a paragraph */
851 thisflag |= (CFFILL | CFFLPA);
853 if(!Pmaster)
854 sgarbk = TRUE;
856 curwp->w_flag |= WFMODE;
858 curwp->w_doto = 0; /* start region at beginning of line */
861 * Fillregion moves dot to the end of the filled region.
863 if(!fillregion(qstr, &addedregion))
864 return(FALSE);
866 set_last_region_added(&addedregion);
868 /* Leave cursor on first char of first line after justified region */
869 curwp->w_dotp = lforw(curwp->w_dotp);
870 curwp->w_doto = 0;
872 if(curwp->w_markp)
873 setmark(0,0); /* clear mark */
875 else if(action == 'Q'){
876 /* let yank() know that it may be restoring a paragraph */
877 thisflag |= CFFILL;
879 if(!Pmaster)
880 sgarbk = TRUE;
882 curwp->w_flag |= WFHARD;
884 swap_mark_and_dot_if_mark_comes_first();
886 if(!setquotelevelinregion(quotelevel, &addedregion))
887 return(FALSE);
889 set_last_region_added(&addedregion);
891 else{
892 /* abort */
895 return(TRUE);
900 * The region we're filling is the region from dot to mark.
901 * We cut out that region and then put it back in filled.
902 * The cut out part is saved in the ldelete call and the
903 * reinstalled region is noted in addedregion, so that yank()
904 * can delete it and restore the saved part.
907 fillregion(UCS *qstr, REGION *addedregion)
909 long c, sz, last_char = 0;
910 int i, j, qlen, same_word,
911 spaces, word_len, word_ind, line_len, ww;
912 int starts_midline = 0;
913 int ends_midline = 0;
914 int offset_into_start;
915 LINE *line_before_start, *lp;
916 UCS line_last, word[NSTRING];
917 REGION region;
919 /* if region starts midline insert a newline */
920 if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp))
921 starts_midline++;
923 /* if region ends midline insert a newline at end */
924 if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp))
925 ends_midline++;
927 /* cut the paragraph into our fill buffer */
928 fdelete();
929 if(!getregion(&region, curwp->w_markp, curwp->w_marko))
930 return(FALSE);
932 if(!ldelete(region.r_size, finsert))
933 return(FALSE);
935 line_before_start = lback(curwp->w_dotp);
936 offset_into_start = curwp->w_doto;
938 if(starts_midline)
939 lnewline();
941 /* Now insert it back wrapped */
942 spaces = word_len = word_ind = line_len = same_word = 0;
943 qlen = qstr ? ucs4_strlen(qstr) : 0;
945 /* Beginning with leading quoting... */
946 if(qstr){
947 i = 0;
948 while(qstr[i]){
949 ww = wcellwidth(qstr[i]);
950 line_len += (ww >= 0 ? ww : 1);
951 linsert(1, qstr[i++]);
954 line_last = ' '; /* no word-flush space! */
957 /* remove first leading quotes if any */
958 if(starts_midline)
959 i = 0;
960 else
961 for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){
962 linsert(1, line_last = (UCS) c);
963 line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1);
966 /* then digest the rest... */
967 while((c = fremove(i++)) >= 0){
968 last_char = c;
969 switch(c){
970 case '\n' :
971 /* skip next quote string */
972 j = 0;
973 while(j < qlen && ((c = fremove(i+j)) == qstr[j] || c == ' '))
974 j++;
976 i += j;
979 if(!spaces)
980 spaces++;
981 break;
983 case TAB :
984 case ' ' :
985 spaces++;
986 break;
988 default :
989 if(spaces){ /* flush word? */
990 if((line_len - qlen > 0)
991 && line_len + word_len + 1 > fillcol
992 && ((ucs4_isspace(line_last))
993 || (linsert(1, ' ')))
994 && same_word == 0
995 && (line_len = fpnewline(qstr)))
996 line_last = ' '; /* no word-flush space! */
998 if(word_len){ /* word to write? */
999 if(line_len && !ucs4_isspace(line_last)){
1000 linsert(1, ' '); /* need padding? */
1001 line_len++;
1004 line_len += word_len;
1005 for(j = 0; j < word_ind; j++)
1006 linsert(1, line_last = word[j]);
1008 if(spaces > 1 && strchr(".?!:;\")", line_last)){
1009 linsert(2, line_last = ' ');
1010 line_len += 2;
1013 same_word = word_len = word_ind = 0;
1016 spaces = 0;
1019 if(word_ind + 1 >= NSTRING){
1020 /* Magic! Fake that we output a wrapped word */
1021 if((line_len - qlen > 0) && same_word == 0){
1022 if(!ucs4_isspace(line_last))
1023 linsert(1, ' ');
1024 line_len = fpnewline(qstr);
1026 same_word = 1;
1027 line_len += word_len;
1028 for(j = 0; j < word_ind; j++)
1029 linsert(1, word[j]);
1031 word_len = word_ind = 0;
1032 line_last = ' ';
1035 word[word_ind++] = (UCS) c;
1036 ww = wcellwidth((UCS) c);
1037 word_len += (ww >= 0 ? ww : 1);
1039 break;
1043 if(word_len){
1044 if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){
1045 if(!ucs4_isspace(line_last))
1046 linsert(1, ' ');
1047 (void) fpnewline(qstr);
1049 else if(line_len && !ucs4_isspace(line_last))
1050 linsert(1, ' ');
1052 for(j = 0; j < word_ind; j++)
1053 linsert(1, word[j]);
1056 if(last_char == '\n')
1057 lnewline();
1059 if(ends_midline)
1060 (void) fpnewline(qstr);
1063 * Calculate the size of the region that was added.
1065 swapmark(0,1); /* mark current location after adds */
1066 addedregion->r_linep = lforw(line_before_start);
1067 addedregion->r_offset = offset_into_start;
1068 lp = addedregion->r_linep;
1069 sz = llength(lp) - addedregion->r_offset;
1070 if(lforw(lp) != curwp->w_markp->l_fp){
1071 lp = lforw(lp);
1072 while(lp != curwp->w_markp->l_fp){
1073 sz += llength(lp) + 1;
1074 lp = lforw(lp);
1078 sz -= llength(curwp->w_markp) - curwp->w_marko;
1079 addedregion->r_size = sz;
1081 swapmark(0,1);
1083 if(ends_midline){
1085 * We want to back up to the end of the original
1086 * region instead of being here after the added newline.
1088 curwp->w_doto = 0;
1089 backchar(0, 1);
1090 unmarkbuffer();
1091 markregion(1);
1094 return(TRUE);
1099 * fpnewline - output a fill paragraph newline mindful of quote string
1102 fpnewline(UCS *quote)
1104 int len;
1106 lnewline();
1107 for(len = 0; quote && *quote; quote++){
1108 int ww;
1110 ww = wcellwidth(*quote);
1111 len += (ww >= 0 ? ww : 1);
1112 linsert(1, *quote);
1115 return(len);
1120 setquotelevelinregion(int quotelevel, REGION *addedregion)
1122 int i, standards_based = 0;
1123 int quote_chars = 0, backuptoprevline = 0;
1124 int starts_midline = 0, ends_midline = 0, offset_into_start;
1125 long c, sz;
1126 UCS qstr_def1[] = { '>', ' ', 0}, qstr_def2[] = { '>', 0};
1127 LINE *lp, *line_before_start;
1128 REGION region;
1130 if(curbp->b_mode&MDVIEW) /* don't allow this command if */
1131 return(rdonly()); /* we are in read only mode */
1133 if(!glo_quote_str
1134 || !ucs4_strcmp(glo_quote_str, qstr_def1)
1135 || !ucs4_strcmp(glo_quote_str, qstr_def2))
1136 standards_based++;
1138 if(!standards_based){
1139 emlwrite("Quote level setting only works with standard \"> \" quotes", NULL);
1140 return(FALSE);
1143 /* if region starts midline insert a newline */
1144 if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp))
1145 starts_midline++;
1147 /* if region ends midline insert a newline at end */
1148 if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp)){
1149 ends_midline++;
1150 backuptoprevline++;
1151 /* count quote chars for re-insertion */
1152 for(i = 0; i < llength(curwp->w_markp); ++i)
1153 if(lgetc(curwp->w_markp, i).c != '>')
1154 break;
1156 quote_chars = i;
1158 else if(curwp->w_marko == 0)
1159 backuptoprevline++;
1161 /* find the size of the region */
1162 getregion(&region, curwp->w_markp, curwp->w_marko);
1164 /* cut the paragraph into our fill buffer */
1165 fdelete();
1166 if(!ldelete(region.r_size, finsert))
1167 return(FALSE);
1169 line_before_start = lback(curwp->w_dotp);
1170 offset_into_start = curwp->w_doto;
1172 /* if region starts midline add a newline */
1173 if(starts_midline)
1174 lnewline();
1176 i = 0;
1177 while(fremove(i) >= 0){
1179 /* remove all quote strs from current line */
1180 if(standards_based){
1181 while((c = fremove(i)) == '>')
1182 i++;
1184 if(c == ' ')
1185 i++;
1187 else{
1190 /* insert quotelevel quote strs */
1191 if(standards_based){
1192 linsert(quotelevel, '>');
1193 if(quotelevel > 0)
1194 linsert(1, ' ');
1196 else{
1199 /* put back the actual line */
1200 while((c = fremove(i++)) >= 0 && c != '\n')
1201 linsert(1, (UCS) c);
1203 if(c == '\n')
1204 lnewline();
1207 /* if region ends midline add a newline */
1208 if(ends_midline){
1209 lnewline();
1210 if(quote_chars){
1211 linsert(quote_chars, '>');
1212 if(curwp->w_doto < llength(curwp->w_dotp)
1213 && lgetc(curwp->w_dotp, curwp->w_doto).c != ' ')
1214 linsert(1, ' ');
1219 * Calculate the size of the region that was added.
1221 swapmark(0,1); /* mark current location after adds */
1222 addedregion->r_linep = lforw(line_before_start);
1223 addedregion->r_offset = offset_into_start;
1224 lp = addedregion->r_linep;
1225 sz = llength(lp) - addedregion->r_offset;
1226 if(lforw(lp) != curwp->w_markp->l_fp){
1227 lp = lforw(lp);
1228 while(lp != curwp->w_markp->l_fp){
1229 sz += llength(lp) + 1;
1230 lp = lforw(lp);
1234 sz -= llength(curwp->w_markp) - curwp->w_marko;
1235 addedregion->r_size = sz;
1237 swapmark(0,1);
1240 * This puts us at the end of the quoted region instead
1241 * of on the following line. This makes it convenient
1242 * for the user to follow a quotelevel adjustment with
1243 * a Justify if desired.
1245 if(backuptoprevline){
1246 curwp->w_doto = 0;
1247 backchar(0, 1);
1250 if(ends_midline){ /* doesn't need fixing otherwise */
1251 unmarkbuffer();
1252 markregion(1);
1255 return (TRUE);