* S/MIME: Some clients do not transform messages to canonical form when
[alpine.git] / pico / word.c
blob1bcd3e0c87465942a728cb27a633ea16f7a30a00
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: word.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2007 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * Program: Word at a time routines
22 * The routines in this file implement commands that work word at a time.
23 * There are all sorts of word mode commands. If I do any sentence and/or
24 * paragraph mode commands, they are likely to be put in this file.
27 #include "headers.h"
30 int fpnewline(UCS *quote);
31 int fillregion(UCS *qstr, REGION *addedregion);
32 int setquotelevelinregion(int quotelevel, REGION *addedregion);
33 int is_user_separator(UCS c);
36 /* Word wrap on n-spaces. Back-over whatever precedes the point on the current
37 * line and stop on the first word-break or the beginning of the line. If we
38 * reach the beginning of the line, jump back to the end of the word and start
39 * a new line. Otherwise, break the line at the word-break, eat it, and jump
40 * back to the end of the word.
41 * Returns TRUE on success, FALSE on errors.
43 int
44 wrapword(void)
46 register int cnt; /* size of word wrapped to next line */
47 register int bp; /* index to wrap on */
48 register int first = -1;
49 int wid, ww;
51 if(curwp->w_doto <= 0) /* no line to wrap? */
52 return(FALSE);
54 wid = 0;
55 for(bp = cnt = 0; cnt < llength(curwp->w_dotp) && !bp; cnt++){
56 if(ucs4_isspace(lgetc(curwp->w_dotp, cnt).c)){
57 first = 0;
58 if(lgetc(curwp->w_dotp, cnt).c == TAB){
59 ++wid;
60 while(wid & 0x07)
61 ++wid;
63 else
64 ++wid;
66 else{
67 ww = wcellwidth((UCS) lgetc(curwp->w_dotp, cnt).c);
68 wid += (ww >= 0 ? ww : 1);
69 if(!first)
70 first = cnt;
73 if(first > 0 && wid > fillcol)
74 bp = first;
77 if(!bp)
78 return(FALSE);
80 /* bp now points to the first character of the next line */
81 cnt = curwp->w_doto - bp;
82 curwp->w_doto = bp;
84 if(!lnewline()) /* break the line */
85 return(FALSE);
88 * if there's a line below, it doesn't start with whitespace
89 * and there's room for this line...
91 if(!(curbp->b_flag & BFWRAPOPEN)
92 && lforw(curwp->w_dotp) != curbp->b_linep
93 && llength(lforw(curwp->w_dotp))
94 && !ucs4_isspace(lgetc(lforw(curwp->w_dotp), 0).c)
95 && (llength(curwp->w_dotp) + llength(lforw(curwp->w_dotp)) < fillcol)){
96 gotoeol(0, 1); /* then pull text up from below */
97 if(lgetc(curwp->w_dotp, curwp->w_doto - 1).c != ' ')
98 linsert(1, ' ');
100 forwdel(0, 1);
101 gotobol(0, 1);
104 curbp->b_flag &= ~BFWRAPOPEN; /* don't open new line next wrap */
105 /* restore dot (account for NL) */
106 if(cnt && !forwchar(0, cnt < 0 ? cnt-1 : cnt))
107 return(FALSE);
109 return(TRUE);
114 * Move the cursor backward by "n" words. All of the details of motion are
115 * performed by the "backchar" and "forwchar" routines. Error if you try to
116 * move beyond the buffers.
119 backword(int f, int n)
121 if (n < 0)
122 return (forwword(f, -n));
123 if (backchar_no_header_editor(FALSE, 1) == FALSE)
124 return (FALSE);
125 while (n--) {
126 while (inword() == FALSE) {
127 if (backchar_no_header_editor(FALSE, 1) == FALSE)
128 return (FALSE);
130 while (inword() != FALSE) {
131 if (backchar_no_header_editor(FALSE, 1) == FALSE)
132 return (FALSE);
135 return (forwchar(FALSE, 1));
139 * Move the cursor forward by the specified number of words. All of the motion
140 * is done by "forwchar". Error if you try and move beyond the buffer's end.
143 forwword(int f, int n)
145 if (n < 0)
146 return (backword(f, -n));
147 while (n--) {
148 #if NFWORD
149 while (inword() != FALSE) {
150 if (forwchar(FALSE, 1) == FALSE)
151 return (FALSE);
153 #endif
154 while (inword() == FALSE) {
155 if (forwchar(FALSE, 1) == FALSE)
156 return (FALSE);
158 #if NFWORD == 0
159 while (inword() != FALSE) {
160 if (forwchar(FALSE, 1) == FALSE)
161 return (FALSE);
163 #endif
165 return(TRUE);
169 ucs4_isalnum(UCS c)
171 return((c && c <= 0x7f && isalnum((unsigned char) c))
172 || (c >= 0xA0 && !SPECIAL_SPACE(c)));
176 ucs4_isalpha(UCS c)
178 return((c && c <= 0x7f && isalpha((unsigned char) c))
179 || (c >= 0xA0 && !SPECIAL_SPACE(c)));
183 ucs4_isspace(UCS c)
185 return((c < 0xff && isspace((unsigned char) c)) || SPECIAL_SPACE(c));
189 ucs4_ispunct(UCS c)
191 return !ucs4_isalnum(c) && !ucs4_isspace(c);
194 #ifdef MAYBELATER
196 * Move the cursor forward by the specified number of words. As you move,
197 * convert any characters to upper case. Error if you try and move beyond the
198 * end of the buffer. Bound to "M-U".
201 upperword(int f, int n)
203 register int c;
204 CELL ac;
206 ac.a = 0;
207 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
208 return(rdonly()); /* we are in read only mode */
209 if (n < 0)
210 return (FALSE);
211 while (n--) {
212 while (inword() == FALSE) {
213 if (forwchar(FALSE, 1) == FALSE)
214 return (FALSE);
216 while (inword() != FALSE) {
217 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
218 if (c>='a' && c<='z') {
219 ac.c = (c -= 'a'-'A');
220 lputc(curwp->w_dotp, curwp->w_doto, ac);
221 lchange(WFHARD);
223 if (forwchar(FALSE, 1) == FALSE)
224 return (FALSE);
227 return (TRUE);
231 * Move the cursor forward by the specified number of words. As you move
232 * convert characters to lower case. Error if you try and move over the end of
233 * the buffer. Bound to "M-L".
236 lowerword(int f, int n)
238 register int c;
239 CELL ac;
241 ac.a = 0;
242 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
243 return(rdonly()); /* we are in read only mode */
244 if (n < 0)
245 return (FALSE);
246 while (n--) {
247 while (inword() == FALSE) {
248 if (forwchar(FALSE, 1) == FALSE)
249 return (FALSE);
251 while (inword() != FALSE) {
252 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
253 if (c>='A' && c<='Z') {
254 ac.c (c += 'a'-'A');
255 lputc(curwp->w_dotp, curwp->w_doto, ac);
256 lchange(WFHARD);
258 if (forwchar(FALSE, 1) == FALSE)
259 return (FALSE);
262 return (TRUE);
266 * Move the cursor forward by the specified number of words. As you move
267 * convert the first character of the word to upper case, and subsequent
268 * characters to lower case. Error if you try and move past the end of the
269 * buffer. Bound to "M-C".
272 capword(int f, int n)
274 register int c;
275 CELL ac;
277 ac.a = 0;
278 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
279 return(rdonly()); /* we are in read only mode */
280 if (n < 0)
281 return (FALSE);
282 while (n--) {
283 while (inword() == FALSE) {
284 if (forwchar(FALSE, 1) == FALSE)
285 return (FALSE);
287 if (inword() != FALSE) {
288 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
289 if (c>='a' && c<='z') {
290 ac.c = (c -= 'a'-'A');
291 lputc(curwp->w_dotp, curwp->w_doto, ac);
292 lchange(WFHARD);
294 if (forwchar(FALSE, 1) == FALSE)
295 return (FALSE);
296 while (inword() != FALSE) {
297 c = lgetc(curwp->w_dotp, curwp->w_doto).c;
298 if (c>='A' && c<='Z') {
299 ac.c = (c += 'a'-'A');
300 lputc(curwp->w_dotp, curwp->w_doto, ac);
301 lchange(WFHARD);
303 if (forwchar(FALSE, 1) == FALSE)
304 return (FALSE);
308 return (TRUE);
312 * Kill forward by "n" words. Remember the location of dot. Move forward by
313 * the right number of words. Put dot back where it was and issue the kill
314 * command for the right number of characters. Bound to "M-D".
317 delfword(int f, int n)
319 register long size;
320 register LINE *dotp;
321 register int doto;
323 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
324 return(rdonly()); /* we are in read only mode */
325 if (n < 0)
326 return (FALSE);
327 dotp = curwp->w_dotp;
328 doto = curwp->w_doto;
329 size = 0L;
330 while (n--) {
331 #if NFWORD
332 while (inword() != FALSE) {
333 if (forwchar(FALSE,1) == FALSE)
334 return(FALSE);
335 ++size;
337 #endif
338 while (inword() == FALSE) {
339 if (forwchar(FALSE, 1) == FALSE)
340 return (FALSE);
341 ++size;
343 #if NFWORD == 0
344 while (inword() != FALSE) {
345 if (forwchar(FALSE, 1) == FALSE)
346 return (FALSE);
347 ++size;
349 #endif
351 curwp->w_dotp = dotp;
352 curwp->w_doto = doto;
353 return (ldelete(size, kinsert));
357 * Kill backwards by "n" words. Move backwards by the desired number of words,
358 * counting the characters. When dot is finally moved to its resting place,
359 * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
362 delbword(int f, int n)
364 register long size;
366 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
367 return(rdonly()); /* we are in read only mode */
368 if (n < 0)
369 return (FALSE);
370 if (backchar(FALSE, 1) == FALSE)
371 return (FALSE);
372 size = 0L;
373 while (n--) {
374 while (inword() == FALSE) {
375 if (backchar(FALSE, 1) == FALSE)
376 return (FALSE);
377 ++size;
379 while (inword() != FALSE) {
380 if (backchar(FALSE, 1) == FALSE)
381 return (FALSE);
382 ++size;
385 if (forwchar(FALSE, 1) == FALSE)
386 return (FALSE);
387 return (ldelete(size, kinsert));
389 #endif /* MAYBELATER */
392 * Return TRUE if the character at dot is a character that is considered to be
393 * part of a word.
396 inword(void)
398 if(curwp->w_doto < llength(curwp->w_dotp))
400 if(ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto).c))
402 return(TRUE);
404 else if(ucs4_ispunct(lgetc(curwp->w_dotp, curwp->w_doto).c)
405 && !is_user_separator(lgetc(curwp->w_dotp, curwp->w_doto).c))
407 if((curwp->w_doto > 0) &&
408 ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto - 1).c) &&
409 (curwp->w_doto + 1 < llength(curwp->w_dotp)) &&
410 ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto + 1).c))
412 return(TRUE);
417 return(FALSE);
422 is_user_separator(UCS c)
424 UCS *u;
426 if(glo_wordseps)
427 for(u = glo_wordseps; *u; u++)
428 if(*u == c)
429 return 1;
431 return 0;
434 void
435 do_quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen)
437 register int i, j;
438 int qstart, qend, k;
441 * The method for determining the quote string is:
442 * 1) strip leading and trailing whitespace from q
443 * 2) add all leading whitespace to buf
444 * 3) check for q
445 * 4) if q, append q to buf and any trailing whitespace
446 * 5) repeat steps 3 and 4 as necessary
448 * q in the future could be made to be an array of (UCS *)'s
449 * (">" and whatever the user's quote_string is)
452 *buf = '\0';
454 if(l == NULL)
455 return;
457 /* count leading whitespace as part of the quote */
458 for(j = 0; j <= llength(l) && lgetc(l, j).c == ' ' && j+1 < buflen; j++)
459 buf[j] = lgetc(l, j).c;
460 buf[j] = '\0';
462 if(q == NULL || *q == '\0')
463 return;
465 /* pare down q so it contains no leading or trailing whitespace */
466 for(i = 0; q[i] == ' '; i++);
467 qstart = i;
468 for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--);
469 qend = i;
471 /* for quote strings that are blanks, chop buf to the length of q */
472 if(qend <= qstart){
473 if(ucs4_strlen(q) < buflen)
474 buf[ucs4_strlen(q)] = '\0';
475 return;
478 while(j <= llength(l)){
479 for(i = qstart; j <= llength(l) && i < qend; i++, j++)
480 if(q[i] != lgetc(l, j).c)
481 return;
483 if(i >= qend){
484 if(ucs4_strlen(buf) + qend - qstart < (buflen - 1))
485 ucs4_strncat(buf, q + qstart, qend - qstart);
489 * if we're this far along, we've matched a quote string,
490 * and should now add the following white space.
492 for(k = ucs4_strlen(buf);
493 j <= llength(l) && lgetc(l,j).c == ' ' && (k + 1 < buflen);
494 j++, k++){
495 buf[k] = lgetc(l,j).c;
497 buf[k] = '\0';
499 if(j > llength(l))
500 return;
505 * Return number of quotes if whatever starts the line matches the quote string
508 quote_match(UCS *q, LINE *gl, UCS *bufl, size_t buflen)
510 LINE *nl = gl != curbp->b_linep ? lforw(gl) : NULL;
511 LINE *pl = lback(gl) != curbp->b_linep ? lback(gl) : NULL;
512 UCS bufp[NSTRING], bufn[NSTRING];
513 int i, j, qstart, qend;
514 int quoted_line = 0;
516 do_quote_match(q, pl, bufp, sizeof(bufp)); /* previous line */
517 do_quote_match(q, gl, bufl, buflen); /* given line */
518 do_quote_match(q, nl, bufn, sizeof(bufn)); /* next line */
520 if(!ucs4_strcmp(bufp, bufl) || !ucs4_strcmp(bufl, bufn))
521 return ucs4_strlen(bufl);
523 /* is this line quoted? */
524 if(q && *q){
525 /* pare down q so it contains no leading or trailing whitespace */
526 for(i = 0; q[i] == ' '; i++);
527 qstart = i;
528 for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--);
529 qend = i;
530 for(i = 0; i < llength(gl)
531 && i + qstart < qend
532 && lgetc(gl, i).c == q[i+qstart]; i++);
533 if(i + qstart == qend)
534 quoted_line = 1;
537 /* compare bufl and bufn */
538 for(i = 0; bufl[i] && bufn[i] && bufl[i] == bufn[i]; i++);
540 /* go back to last non-space character */
541 for(; i > 0 && bufl[i-1] == ' '; i--);
543 /* do bufl and bufn differ only in spaces? */
544 for(j = i; bufl[j] && bufl[j] == ' '; j++);
546 /* if they differ only on trailing spaces, chop bufl to agree with bufn */
547 if (!bufl[j] )
548 bufl[Pmaster && quoted_line ? (j > i ? i+1 : i) : i] = '\0';
550 return ucs4_strlen(bufl);
554 /* Justify the entire buffer instead of just a paragraph */
556 fillbuf(int f, int n)
558 LINE *eobline;
559 REGION region;
561 if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
562 return(rdonly()); /* we are in read only mode */
564 else if (fillcol == 0) { /* no fill column set */
565 mlwrite_utf8("No fill column set", NULL);
566 return(FALSE);
569 if((lastflag & CFFILL) && (lastflag & CFFLBF)){
570 /* no use doing a full justify twice */
571 thisflag |= (CFFLBF | CFFILL);
572 return(TRUE);
575 /* record the pointer of the last line */
576 if(gotoeob(FALSE, 1) == FALSE)
577 return(FALSE);
579 eobline = curwp->w_dotp; /* last line of buffer */
580 if(!llength(eobline))
581 eobline = lback(eobline);
583 /* and back to the beginning of the buffer */
584 gotobob(FALSE, 1);
586 thisflag |= CFFLBF; /* CFFILL also gets set in fillpara */
588 if(!Pmaster)
589 sgarbk = TRUE;
591 curwp->w_flag |= WFMODE;
594 * clear the kill buffer, that's where we'll store undo
595 * information, we can't do the fill buffer because
596 * fillpara relies on its contents
598 kdelete();
599 curwp->w_doto = 0;
600 getregion(&region, eobline, llength(eobline));
602 /* Put full message in the kill buffer for undo */
603 if(!ldelete(region.r_size, kinsert))
604 return(FALSE);
606 /* before yank'ing, clear lastflag so we don't just unjustify */
607 lastflag &= ~(CFFLBF | CFFILL);
609 /* Now in kill buffer, bring back text to use in fillpara */
610 yank(FALSE, 1);
612 gotobob(FALSE, 1);
614 /* call fillpara until we're at the end of the buffer */
615 while(curwp->w_dotp != curbp->b_linep)
616 if(!(fillpara(FALSE, 1)))
617 return(FALSE);
619 return(TRUE);
624 * Fill the current paragraph according to the current fill column
627 fillpara(int f, int n)
629 UCS *qstr, qstr2[NSTRING], c;
630 int quotelevel = -1;
631 REGION addedregion;
632 char action = 'P';
634 if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
635 return(rdonly()); /* we are in read only mode */
637 else if (fillcol == 0) { /* no fill column set */
638 mlwrite_utf8("No fill column set", NULL);
639 return(FALSE);
641 else if(curwp->w_dotp == curbp->b_linep && !curwp->w_markp) /* don't wrap! */
642 return(FALSE);
645 * If there is already a region set, then we may use it
646 * instead of the current paragraph.
649 if(curwp->w_markp){
650 int k, rv;
651 KEYMENU menu_justify[12];
652 char prompt[100];
654 for(k = 0; k < 12; k++){
655 menu_justify[k].name = NULL;
656 KS_OSDATASET(&menu_justify[k], KS_NONE);
659 menu_justify[1].name = "R";
660 menu_justify[1].label = "[" N_("Region") "]";
661 menu_justify[6].name = "^C";
662 menu_justify[6].label = N_("Cancel");
663 menu_justify[7].name = "P";
664 menu_justify[7].label = N_("Paragraph");
665 menu_justify[2].name = "Q";
666 menu_justify[2].label = N_("Quotelevel");
668 wkeyhelp(menu_justify); /* paint menu */
669 sgarbk = TRUE;
670 if(Pmaster && curwp)
671 curwp->w_flag |= WFMODE;
673 strncpy(prompt, "justify Region, Paragraph; or fix Quotelevel ? ", sizeof(prompt));
674 prompt[sizeof(prompt)-1] = '\0';
675 mlwrite_utf8(prompt, NULL);
676 (*term.t_rev)(1);
677 rv = -1;
678 while(1){
679 switch(c = GetKey()){
681 case (CTRL|'C') : /* Bail out! */
682 case F2 :
683 pputs_utf8(_("ABORT"), 1);
684 rv = ABORT;
685 emlwrite("", NULL);
686 break;
688 case (CTRL|'M') : /* default */
689 case 'r' :
690 case 'R' :
691 case F3 :
692 pputs_utf8(_("Region"), 1);
693 rv = 'R';
694 break;
696 case 'p' :
697 case 'P' :
698 case F7 :
699 pputs_utf8(_("Paragraph"), 1);
700 rv = 'P';
701 break;
703 case 'q' :
704 case 'Q' :
705 case F8 :
706 case '0' : case '1' : case '2' : case '3' : case '4' :
707 case '5' : case '6' : case '7' : case '8' : case '9' :
708 pputs_utf8(_("Quotelevel"), 1);
709 while(rv == -1){
710 switch(c){
711 case 'q' :
712 case 'Q' :
713 case F8 :
714 {char num[20];
716 num[0] = '\0';
717 switch(mlreplyd_utf8("Quote Level ? ", num, sizeof(num), QNORML, NULL)){
718 case TRUE:
719 if(isdigit(num[0])){
720 quotelevel = atoi(num);
721 if(quotelevel < 0){
722 emlwrite("Quote Level cannot be negative", NULL);
723 sleep(3);
725 else if(quotelevel > 20){
726 emlwrite("Quote Level should be less than 20", NULL);
727 rv = ABORT;
729 else{
730 rv = 'Q';
733 else if(num[0]){
734 emlwrite("Quote Level should be a number", NULL);
735 sleep(3);
738 break;
740 case HELPCH:
741 emlwrite("Enter the number of quotes you want before the text", NULL);
742 sleep(3);
743 break;
745 default:
746 emlwrite("Quote Level is a number", NULL);
747 rv = ABORT;
748 break;
752 break;
754 case '0' : case '1' : case '2' : case '3' : case '4' :
755 case '5' : case '6' : case '7' : case '8' : case '9' :
756 rv = 'Q';
757 quotelevel = (int) (c - '0');
758 break;
762 break;
764 case (CTRL|'G') :
765 if(term.t_mrow == 0 && km_popped == 0){
766 movecursor(term.t_nrow-2, 0);
767 peeol();
768 term.t_mrow = 2;
769 (*term.t_rev)(0);
770 wkeyhelp(menu_justify);
771 mlwrite_utf8(prompt, NULL);
772 (*term.t_rev)(1);
773 sgarbk = TRUE; /* mark menu dirty */
774 km_popped++;
775 break;
777 /* else fall through */
779 default:
780 (*term.t_beep)();
782 case NODATA :
783 break;
786 (*term.t_flush)();
787 if(rv != -1){
788 (*term.t_rev)(0);
789 if(km_popped){
790 term.t_mrow = 0;
791 movecursor(term.t_nrow, 0);
792 peeol();
793 sgarbf = 1;
794 km_popped = 0;
797 action = rv;
798 break;
802 if(action != ABORT)
803 emlwrite("", NULL);
806 if(action == 'R' && curwp->w_markp){
807 /* let yank() know that it may be restoring a paragraph */
808 thisflag |= CFFILL;
810 if(!Pmaster)
811 sgarbk = TRUE;
813 curwp->w_flag |= WFMODE;
815 swap_mark_and_dot_if_mark_comes_first();
817 /* determine if we're justifying quoted text or not */
818 qstr = quote_match(glo_quote_str,
819 curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp,
820 qstr2, NSTRING)
821 && *qstr2 ? qstr2 : NULL;
825 * Fillregion moves dot to the end of the filled region.
827 if(!fillregion(qstr, &addedregion))
828 return(FALSE);
830 set_last_region_added(&addedregion);
832 else if(action == 'P'){
835 * Justfiy the current paragraph.
838 if(curwp->w_markp) /* clear mark if already set */
839 setmark(0,0);
841 if(gotoeop(FALSE, 1) == FALSE)
842 return(FALSE);
844 /* determine if we're justifying quoted text or not */
845 qstr = quote_match(glo_quote_str,
846 curwp->w_dotp, qstr2, NSTRING)
847 && *qstr2 ? qstr2 : NULL;
849 setmark(0,0); /* mark last line of para */
851 /* jump back to the beginning of the paragraph */
852 gotobop(FALSE, 1);
854 /* let yank() know that it may be restoring a paragraph */
855 thisflag |= (CFFILL | CFFLPA);
857 if(!Pmaster)
858 sgarbk = TRUE;
860 curwp->w_flag |= WFMODE;
862 curwp->w_doto = 0; /* start region at beginning of line */
865 * Fillregion moves dot to the end of the filled region.
867 if(!fillregion(qstr, &addedregion))
868 return(FALSE);
870 set_last_region_added(&addedregion);
872 /* Leave cursor on first char of first line after justified region */
873 curwp->w_dotp = lforw(curwp->w_dotp);
874 curwp->w_doto = 0;
876 if(curwp->w_markp)
877 setmark(0,0); /* clear mark */
879 else if(action == 'Q'){
880 /* let yank() know that it may be restoring a paragraph */
881 thisflag |= CFFILL;
883 if(!Pmaster)
884 sgarbk = TRUE;
886 curwp->w_flag |= WFHARD;
888 swap_mark_and_dot_if_mark_comes_first();
890 if(!setquotelevelinregion(quotelevel, &addedregion))
891 return(FALSE);
893 set_last_region_added(&addedregion);
895 else{
896 /* abort */
899 return(TRUE);
904 * The region we're filling is the region from dot to mark.
905 * We cut out that region and then put it back in filled.
906 * The cut out part is saved in the ldelete call and the
907 * reinstalled region is noted in addedregion, so that yank()
908 * can delete it and restore the saved part.
911 fillregion(UCS *qstr, REGION *addedregion)
913 long c, sz, last_char = 0;
914 int i, j, qlen, same_word,
915 spaces, word_len, word_ind, line_len, ww;
916 int starts_midline = 0;
917 int ends_midline = 0;
918 int offset_into_start;
919 LINE *line_before_start, *lp;
920 UCS line_last, word[NSTRING];
921 REGION region;
923 /* if region starts midline insert a newline */
924 if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp))
925 starts_midline++;
927 /* if region ends midline insert a newline at end */
928 if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp))
929 ends_midline++;
931 /* cut the paragraph into our fill buffer */
932 fdelete();
933 if(!getregion(&region, curwp->w_markp, curwp->w_marko))
934 return(FALSE);
936 if(!ldelete(region.r_size, finsert))
937 return(FALSE);
939 line_before_start = lback(curwp->w_dotp);
940 offset_into_start = curwp->w_doto;
942 if(starts_midline)
943 lnewline();
945 /* Now insert it back wrapped */
946 spaces = word_len = word_ind = line_len = same_word = 0;
947 qlen = qstr ? ucs4_strlen(qstr) : 0;
949 /* Beginning with leading quoting... */
950 if(qstr){
951 i = 0;
952 while(qstr[i]){
953 ww = wcellwidth(qstr[i]);
954 line_len += (ww >= 0 ? ww : 1);
955 linsert(1, qstr[i++]);
958 line_last = ' '; /* no word-flush space! */
961 /* remove first leading quotes if any */
962 if(starts_midline)
963 i = 0;
964 else
965 for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){
966 linsert(1, line_last = (UCS) c);
967 line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1);
970 /* then digest the rest... */
971 while((c = fremove(i++)) >= 0){
972 last_char = c;
973 switch(c){
974 case '\n' :
975 /* skip next quote string */
976 j = 0;
977 while(j < qlen && ((c = fremove(i+j)) == qstr[j] || c == ' '))
978 j++;
980 i += j;
983 if(!spaces)
984 spaces++;
985 break;
987 case TAB :
988 case ' ' :
989 spaces++;
990 break;
992 default :
993 if(spaces){ /* flush word? */
994 if((line_len - qlen > 0)
995 && line_len + word_len + 1 > fillcol
996 && ((ucs4_isspace(line_last))
997 || (linsert(1, ' ')))
998 && same_word == 0
999 && (line_len = fpnewline(qstr)))
1000 line_last = ' '; /* no word-flush space! */
1002 if(word_len){ /* word to write? */
1003 if(line_len && !ucs4_isspace(line_last)){
1004 linsert(1, ' '); /* need padding? */
1005 line_len++;
1008 line_len += word_len;
1009 for(j = 0; j < word_ind; j++)
1010 linsert(1, line_last = word[j]);
1012 if(spaces > 1 && strchr(".?!:;\")", line_last)){
1013 linsert(2, line_last = ' ');
1014 line_len += 2;
1017 same_word = word_len = word_ind = 0;
1020 spaces = 0;
1023 if(word_ind + 1 >= NSTRING){
1024 /* Magic! Fake that we output a wrapped word */
1025 if((line_len - qlen > 0) && same_word == 0){
1026 if(!ucs4_isspace(line_last))
1027 linsert(1, ' ');
1028 line_len = fpnewline(qstr);
1030 same_word = 1;
1031 line_len += word_len;
1032 for(j = 0; j < word_ind; j++)
1033 linsert(1, word[j]);
1035 word_len = word_ind = 0;
1036 line_last = ' ';
1039 word[word_ind++] = (UCS) c;
1040 ww = wcellwidth((UCS) c);
1041 word_len += (ww >= 0 ? ww : 1);
1043 break;
1047 if(word_len){
1048 if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){
1049 if(!ucs4_isspace(line_last))
1050 linsert(1, ' ');
1051 (void) fpnewline(qstr);
1053 else if(line_len && !ucs4_isspace(line_last))
1054 linsert(1, ' ');
1056 for(j = 0; j < word_ind; j++)
1057 linsert(1, word[j]);
1060 if(last_char == '\n')
1061 lnewline();
1063 if(ends_midline)
1064 (void) fpnewline(qstr);
1067 * Calculate the size of the region that was added.
1069 swapmark(0,1); /* mark current location after adds */
1070 addedregion->r_linep = lforw(line_before_start);
1071 addedregion->r_offset = offset_into_start;
1072 lp = addedregion->r_linep;
1073 sz = llength(lp) - addedregion->r_offset;
1074 if(lforw(lp) != curwp->w_markp->l_fp){
1075 lp = lforw(lp);
1076 while(lp != curwp->w_markp->l_fp){
1077 sz += llength(lp) + 1;
1078 lp = lforw(lp);
1082 sz -= llength(curwp->w_markp) - curwp->w_marko;
1083 addedregion->r_size = sz;
1085 swapmark(0,1);
1087 if(ends_midline){
1089 * We want to back up to the end of the original
1090 * region instead of being here after the added newline.
1092 curwp->w_doto = 0;
1093 backchar(0, 1);
1094 unmarkbuffer();
1095 markregion(1);
1098 return(TRUE);
1103 * fpnewline - output a fill paragraph newline mindful of quote string
1106 fpnewline(UCS *quote)
1108 int len;
1110 lnewline();
1111 for(len = 0; quote && *quote; quote++){
1112 int ww;
1114 ww = wcellwidth(*quote);
1115 len += (ww >= 0 ? ww : 1);
1116 linsert(1, *quote);
1119 return(len);
1124 setquotelevelinregion(int quotelevel, REGION *addedregion)
1126 int i, standards_based = 0;
1127 int quote_chars = 0, backuptoprevline = 0;
1128 int starts_midline = 0, ends_midline = 0, offset_into_start;
1129 long c, sz;
1130 UCS qstr_def1[] = { '>', ' ', 0}, qstr_def2[] = { '>', 0};
1131 LINE *lp, *line_before_start;
1132 REGION region;
1134 if(curbp->b_mode&MDVIEW) /* don't allow this command if */
1135 return(rdonly()); /* we are in read only mode */
1137 if(!glo_quote_str
1138 || !ucs4_strcmp(glo_quote_str, qstr_def1)
1139 || !ucs4_strcmp(glo_quote_str, qstr_def2))
1140 standards_based++;
1142 if(!standards_based){
1143 emlwrite("Quote level setting only works with standard \"> \" quotes", NULL);
1144 return(FALSE);
1147 /* if region starts midline insert a newline */
1148 if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp))
1149 starts_midline++;
1151 /* if region ends midline insert a newline at end */
1152 if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp)){
1153 ends_midline++;
1154 backuptoprevline++;
1155 /* count quote chars for re-insertion */
1156 for(i = 0; i < llength(curwp->w_markp); ++i)
1157 if(lgetc(curwp->w_markp, i).c != '>')
1158 break;
1160 quote_chars = i;
1162 else if(curwp->w_marko == 0)
1163 backuptoprevline++;
1165 /* find the size of the region */
1166 getregion(&region, curwp->w_markp, curwp->w_marko);
1168 /* cut the paragraph into our fill buffer */
1169 fdelete();
1170 if(!ldelete(region.r_size, finsert))
1171 return(FALSE);
1173 line_before_start = lback(curwp->w_dotp);
1174 offset_into_start = curwp->w_doto;
1176 /* if region starts midline add a newline */
1177 if(starts_midline)
1178 lnewline();
1180 i = 0;
1181 while(fremove(i) >= 0){
1183 /* remove all quote strs from current line */
1184 if(standards_based){
1185 while((c = fremove(i)) == '>')
1186 i++;
1188 if(c == ' ')
1189 i++;
1191 else{
1194 /* insert quotelevel quote strs */
1195 if(standards_based){
1196 linsert(quotelevel, '>');
1197 if(quotelevel > 0)
1198 linsert(1, ' ');
1200 else{
1203 /* put back the actual line */
1204 while((c = fremove(i++)) >= 0 && c != '\n')
1205 linsert(1, (UCS) c);
1207 if(c == '\n')
1208 lnewline();
1211 /* if region ends midline add a newline */
1212 if(ends_midline){
1213 lnewline();
1214 if(quote_chars){
1215 linsert(quote_chars, '>');
1216 if(curwp->w_doto < llength(curwp->w_dotp)
1217 && lgetc(curwp->w_dotp, curwp->w_doto).c != ' ')
1218 linsert(1, ' ');
1223 * Calculate the size of the region that was added.
1225 swapmark(0,1); /* mark current location after adds */
1226 addedregion->r_linep = lforw(line_before_start);
1227 addedregion->r_offset = offset_into_start;
1228 lp = addedregion->r_linep;
1229 sz = llength(lp) - addedregion->r_offset;
1230 if(lforw(lp) != curwp->w_markp->l_fp){
1231 lp = lforw(lp);
1232 while(lp != curwp->w_markp->l_fp){
1233 sz += llength(lp) + 1;
1234 lp = lforw(lp);
1238 sz -= llength(curwp->w_markp) - curwp->w_marko;
1239 addedregion->r_size = sz;
1241 swapmark(0,1);
1244 * This puts us at the end of the quoted region instead
1245 * of on the following line. This makes it convenient
1246 * for the user to follow a quotelevel adjustment with
1247 * a Justify if desired.
1249 if(backuptoprevline){
1250 curwp->w_doto = 0;
1251 backchar(0, 1);
1254 if(ends_midline){ /* doesn't need fixing otherwise */
1255 unmarkbuffer();
1256 markregion(1);
1259 return (TRUE);