diff options
Diffstat (limited to 'pico/word.c')
-rw-r--r-- | pico/word.c | 1179 |
1 files changed, 1179 insertions, 0 deletions
diff --git a/pico/word.c b/pico/word.c new file mode 100644 index 00000000..145fc1d1 --- /dev/null +++ b/pico/word.c @@ -0,0 +1,1179 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: word.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2007 University of Washington + * Copyright 2013 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + +/* + * Program: Word at a time routines + * + * The routines in this file implement commands that work word at a time. + * There are all sorts of word mode commands. If I do any sentence and/or + * paragraph mode commands, they are likely to be put in this file. + */ + +#include "headers.h" + + +int fpnewline(UCS *quote); +int fillregion(UCS *qstr, REGION *addedregion); +int setquotelevelinregion(int quotelevel, REGION *addedregion); +int is_user_separator(UCS c); + + +/* Word wrap on n-spaces. Back-over whatever precedes the point on the current + * line and stop on the first word-break or the beginning of the line. If we + * reach the beginning of the line, jump back to the end of the word and start + * a new line. Otherwise, break the line at the word-break, eat it, and jump + * back to the end of the word. + * Returns TRUE on success, FALSE on errors. + */ +int +wrapword(void) +{ + register int cnt; /* size of word wrapped to next line */ + register int bp; /* index to wrap on */ + register int first = -1; + int wid, ww; + + if(curwp->w_doto <= 0) /* no line to wrap? */ + return(FALSE); + + wid = 0; + for(bp = cnt = 0; cnt < llength(curwp->w_dotp) && !bp; cnt++){ + if(ucs4_isspace(lgetc(curwp->w_dotp, cnt).c)){ + first = 0; + if(lgetc(curwp->w_dotp, cnt).c == TAB){ + ++wid; + while(wid & 0x07) + ++wid; + } + else + ++wid; + } + else{ + ww = wcellwidth((UCS) lgetc(curwp->w_dotp, cnt).c); + wid += (ww >= 0 ? ww : 1); + if(!first) + first = cnt; + } + + if(first > 0 && wid > fillcol) + bp = first; + } + + if(!bp) + return(FALSE); + + /* bp now points to the first character of the next line */ + cnt = curwp->w_doto - bp; + curwp->w_doto = bp; + + if(!lnewline()) /* break the line */ + return(FALSE); + + /* + * if there's a line below, it doesn't start with whitespace + * and there's room for this line... + */ + if(!(curbp->b_flag & BFWRAPOPEN) + && lforw(curwp->w_dotp) != curbp->b_linep + && llength(lforw(curwp->w_dotp)) + && !ucs4_isspace(lgetc(lforw(curwp->w_dotp), 0).c) + && (llength(curwp->w_dotp) + llength(lforw(curwp->w_dotp)) < fillcol)){ + gotoeol(0, 1); /* then pull text up from below */ + if(lgetc(curwp->w_dotp, curwp->w_doto - 1).c != ' ') + linsert(1, ' '); + + forwdel(0, 1); + gotobol(0, 1); + } + + curbp->b_flag &= ~BFWRAPOPEN; /* don't open new line next wrap */ + /* restore dot (account for NL) */ + if(cnt && !forwchar(0, cnt < 0 ? cnt-1 : cnt)) + return(FALSE); + + return(TRUE); +} + + +/* + * Move the cursor backward by "n" words. All of the details of motion are + * performed by the "backchar" and "forwchar" routines. Error if you try to + * move beyond the buffers. + */ +int +backword(int f, int n) +{ + if (n < 0) + return (forwword(f, -n)); + if (backchar_no_header_editor(FALSE, 1) == FALSE) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (backchar_no_header_editor(FALSE, 1) == FALSE) + return (FALSE); + } + while (inword() != FALSE) { + if (backchar_no_header_editor(FALSE, 1) == FALSE) + return (FALSE); + } + } + return (forwchar(FALSE, 1)); +} + +/* + * Move the cursor forward by the specified number of words. All of the motion + * is done by "forwchar". Error if you try and move beyond the buffer's end. + */ +int +forwword(int f, int n) +{ + if (n < 0) + return (backword(f, -n)); + while (n--) { +#if NFWORD + while (inword() != FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } +#endif + while (inword() == FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } +#if NFWORD == 0 + while (inword() != FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } +#endif + } + return(TRUE); +} + +int +ucs4_isalnum(UCS c) +{ + return((c && c <= 0x7f && isalnum((unsigned char) c)) + || (c >= 0xA0 && !SPECIAL_SPACE(c))); +} + +int +ucs4_isalpha(UCS c) +{ + return((c && c <= 0x7f && isalpha((unsigned char) c)) + || (c >= 0xA0 && !SPECIAL_SPACE(c))); +} + +int +ucs4_isspace(UCS c) +{ + return((c < 0xff && isspace((unsigned char) c)) || SPECIAL_SPACE(c)); +} + +int +ucs4_ispunct(UCS c) +{ + return !ucs4_isalnum(c) && !ucs4_isspace(c); +} + +#ifdef MAYBELATER +/* + * Move the cursor forward by the specified number of words. As you move, + * convert any characters to upper case. Error if you try and move beyond the + * end of the buffer. Bound to "M-U". + */ +int +upperword(int f, int n) +{ + register int c; + CELL ac; + + ac.a = 0; + if (curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + if (n < 0) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } + while (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto).c; + if (c>='a' && c<='z') { + ac.c = (c -= 'a'-'A'); + lputc(curwp->w_dotp, curwp->w_doto, ac); + lchange(WFHARD); + } + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } + } + return (TRUE); +} + +/* + * Move the cursor forward by the specified number of words. As you move + * convert characters to lower case. Error if you try and move over the end of + * the buffer. Bound to "M-L". + */ +int +lowerword(int f, int n) +{ + register int c; + CELL ac; + + ac.a = 0; + if (curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + if (n < 0) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } + while (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto).c; + if (c>='A' && c<='Z') { + ac.c (c += 'a'-'A'); + lputc(curwp->w_dotp, curwp->w_doto, ac); + lchange(WFHARD); + } + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } + } + return (TRUE); +} + +/* + * Move the cursor forward by the specified number of words. As you move + * convert the first character of the word to upper case, and subsequent + * characters to lower case. Error if you try and move past the end of the + * buffer. Bound to "M-C". + */ +int +capword(int f, int n) +{ + register int c; + CELL ac; + + ac.a = 0; + if (curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + if (n < 0) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } + if (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto).c; + if (c>='a' && c<='z') { + ac.c = (c -= 'a'-'A'); + lputc(curwp->w_dotp, curwp->w_doto, ac); + lchange(WFHARD); + } + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + while (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto).c; + if (c>='A' && c<='Z') { + ac.c = (c += 'a'-'A'); + lputc(curwp->w_dotp, curwp->w_doto, ac); + lchange(WFHARD); + } + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + } + } + } + return (TRUE); +} + +/* + * Kill forward by "n" words. Remember the location of dot. Move forward by + * the right number of words. Put dot back where it was and issue the kill + * command for the right number of characters. Bound to "M-D". + */ +int +delfword(int f, int n) +{ + register long size; + register LINE *dotp; + register int doto; + + if (curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + if (n < 0) + return (FALSE); + dotp = curwp->w_dotp; + doto = curwp->w_doto; + size = 0L; + while (n--) { +#if NFWORD + while (inword() != FALSE) { + if (forwchar(FALSE,1) == FALSE) + return(FALSE); + ++size; + } +#endif + while (inword() == FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + ++size; + } +#if NFWORD == 0 + while (inword() != FALSE) { + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + ++size; + } +#endif + } + curwp->w_dotp = dotp; + curwp->w_doto = doto; + return (ldelete(size, kinsert)); +} + +/* + * Kill backwards by "n" words. Move backwards by the desired number of words, + * counting the characters. When dot is finally moved to its resting place, + * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace". + */ +int +delbword(int f, int n) +{ + register long size; + + if (curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + if (n < 0) + return (FALSE); + if (backchar(FALSE, 1) == FALSE) + return (FALSE); + size = 0L; + while (n--) { + while (inword() == FALSE) { + if (backchar(FALSE, 1) == FALSE) + return (FALSE); + ++size; + } + while (inword() != FALSE) { + if (backchar(FALSE, 1) == FALSE) + return (FALSE); + ++size; + } + } + if (forwchar(FALSE, 1) == FALSE) + return (FALSE); + return (ldelete(size, kinsert)); +} +#endif /* MAYBELATER */ + +/* + * Return TRUE if the character at dot is a character that is considered to be + * part of a word. + */ +int +inword(void) +{ + if(curwp->w_doto < llength(curwp->w_dotp)) + { + if(ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto).c)) + { + return(TRUE); + } + else if(ucs4_ispunct(lgetc(curwp->w_dotp, curwp->w_doto).c) + && !is_user_separator(lgetc(curwp->w_dotp, curwp->w_doto).c)) + { + if((curwp->w_doto > 0) && + ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto - 1).c) && + (curwp->w_doto + 1 < llength(curwp->w_dotp)) && + ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto + 1).c)) + { + return(TRUE); + } + } + } + + return(FALSE); +} + + +int +is_user_separator(UCS c) +{ + UCS *u; + + if(glo_wordseps) + for(u = glo_wordseps; *u; u++) + if(*u == c) + return 1; + + return 0; +} + + +/* + * Return number of quotes if whatever starts the line matches the quote string + */ +int +quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen) +{ + register int i, n, j, qb; + + *buf = '\0'; + if(*q == '\0') + return(1); + + qb = (ucs4_strlen(q) > 1 && q[ucs4_strlen(q)-1] == ' ') ? 1 : 0; + for(n = 0, j = 0; ;){ + for(i = 0; j <= llength(l) && qb ? q[i+1] : q[i]; i++, j++) + if(q[i] != lgetc(l, j).c) + return(n); + + n++; + if((!qb && q[i] == '\0') || (qb && q[i+1] == '\0')){ + if(ucs4_strlen(buf) + ucs4_strlen(q) + 1 < buflen){ + ucs4_strncat(buf, q, buflen-ucs4_strlen(q)-1); + buf[buflen-1] = '\0'; + if(qb && (j > llength(l) || lgetc(l, j).c != ' ')) + buf[ucs4_strlen(buf)-1] = '\0'; + } + } + if(j > llength(l)) + return(n); + else if(qb && lgetc(l, j).c == ' ') + j++; + } + return(n); /* never reached */ +} + + +/* Justify the entire buffer instead of just a paragraph */ +int +fillbuf(int f, int n) +{ + LINE *eobline; + REGION region; + + if(curbp->b_mode&MDVIEW){ /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + } + else if (fillcol == 0) { /* no fill column set */ + mlwrite_utf8("No fill column set", NULL); + return(FALSE); + } + + if((lastflag & CFFILL) && (lastflag & CFFLBF)){ + /* no use doing a full justify twice */ + thisflag |= (CFFLBF | CFFILL); + return(TRUE); + } + + /* record the pointer of the last line */ + if(gotoeob(FALSE, 1) == FALSE) + return(FALSE); + + eobline = curwp->w_dotp; /* last line of buffer */ + if(!llength(eobline)) + eobline = lback(eobline); + + /* and back to the beginning of the buffer */ + gotobob(FALSE, 1); + + thisflag |= CFFLBF; /* CFFILL also gets set in fillpara */ + + if(!Pmaster) + sgarbk = TRUE; + + curwp->w_flag |= WFMODE; + + /* + * clear the kill buffer, that's where we'll store undo + * information, we can't do the fill buffer because + * fillpara relies on its contents + */ + kdelete(); + curwp->w_doto = 0; + getregion(®ion, eobline, llength(eobline)); + + /* Put full message in the kill buffer for undo */ + if(!ldelete(region.r_size, kinsert)) + return(FALSE); + + /* before yank'ing, clear lastflag so we don't just unjustify */ + lastflag &= ~(CFFLBF | CFFILL); + + /* Now in kill buffer, bring back text to use in fillpara */ + yank(FALSE, 1); + + gotobob(FALSE, 1); + + /* call fillpara until we're at the end of the buffer */ + while(curwp->w_dotp != curbp->b_linep) + if(!(fillpara(FALSE, 1))) + return(FALSE); + + return(TRUE); +} + + +/* + * Fill the current paragraph according to the current fill column + */ +int +fillpara(int f, int n) +{ + UCS *qstr, qstr2[NSTRING], c; + int quotelevel = -1; + REGION addedregion; + char action = 'P'; + + if(curbp->b_mode&MDVIEW){ /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + } + else if (fillcol == 0) { /* no fill column set */ + mlwrite_utf8("No fill column set", NULL); + return(FALSE); + } + else if(curwp->w_dotp == curbp->b_linep && !curwp->w_markp) /* don't wrap! */ + return(FALSE); + + /* + * If there is already a region set, then we may use it + * instead of the current paragraph. + */ + + if(curwp->w_markp){ + int k, rv; + KEYMENU menu_justify[12]; + char prompt[100]; + + for(k = 0; k < 12; k++){ + menu_justify[k].name = NULL; + KS_OSDATASET(&menu_justify[k], KS_NONE); + } + + menu_justify[1].name = "R"; + menu_justify[1].label = "[" N_("Region") "]"; + menu_justify[6].name = "^C"; + menu_justify[6].label = N_("Cancel"); + menu_justify[7].name = "P"; + menu_justify[7].label = N_("Paragraph"); + menu_justify[2].name = "Q"; + menu_justify[2].label = N_("Quotelevel"); + + wkeyhelp(menu_justify); /* paint menu */ + sgarbk = TRUE; + if(Pmaster && curwp) + curwp->w_flag |= WFMODE; + + strncpy(prompt, "justify Region, Paragraph; or fix Quotelevel ? ", sizeof(prompt)); + prompt[sizeof(prompt)-1] = '\0'; + mlwrite_utf8(prompt, NULL); + (*term.t_rev)(1); + rv = -1; + while(1){ + switch(c = GetKey()){ + + case (CTRL|'C') : /* Bail out! */ + case F2 : + pputs_utf8(_("ABORT"), 1); + rv = ABORT; + emlwrite("", NULL); + break; + + case (CTRL|'M') : /* default */ + case 'r' : + case 'R' : + case F3 : + pputs_utf8(_("Region"), 1); + rv = 'R'; + break; + + case 'p' : + case 'P' : + case F7 : + pputs_utf8(_("Paragraph"), 1); + rv = 'P'; + break; + + case 'q' : + case 'Q' : + case F8 : + case '0' : case '1' : case '2' : case '3' : case '4' : + case '5' : case '6' : case '7' : case '8' : case '9' : + pputs_utf8(_("Quotelevel"), 1); + while(rv == -1){ + switch(c){ + case 'q' : + case 'Q' : + case F8 : + {char num[20]; + + num[0] = '\0'; + switch(mlreplyd_utf8("Quote Level ? ", num, sizeof(num), QNORML, NULL)){ + case TRUE: + if(isdigit(num[0])){ + quotelevel = atoi(num); + if(quotelevel < 0){ + emlwrite("Quote Level cannot be negative", NULL); + sleep(3); + } + else if(quotelevel > 20){ + emlwrite("Quote Level should be less than 20", NULL); + rv = ABORT; + } + else{ + rv = 'Q'; + } + } + else if(num[0]){ + emlwrite("Quote Level should be a number", NULL); + sleep(3); + } + + break; + + case HELPCH: + emlwrite("Enter the number of quotes you want before the text", NULL); + sleep(3); + break; + + default: + emlwrite("Quote Level is a number", NULL); + rv = ABORT; + break; + } + } + + break; + + case '0' : case '1' : case '2' : case '3' : case '4' : + case '5' : case '6' : case '7' : case '8' : case '9' : + rv = 'Q'; + quotelevel = (int) (c - '0'); + break; + } + } + + break; + + case (CTRL|'G') : + if(term.t_mrow == 0 && km_popped == 0){ + movecursor(term.t_nrow-2, 0); + peeol(); + term.t_mrow = 2; + (*term.t_rev)(0); + wkeyhelp(menu_justify); + mlwrite_utf8(prompt, NULL); + (*term.t_rev)(1); + sgarbk = TRUE; /* mark menu dirty */ + km_popped++; + break; + } + /* else fall through */ + + default: + (*term.t_beep)(); + + case NODATA : + break; + } + + (*term.t_flush)(); + if(rv != -1){ + (*term.t_rev)(0); + if(km_popped){ + term.t_mrow = 0; + movecursor(term.t_nrow, 0); + peeol(); + sgarbf = 1; + km_popped = 0; + } + + action = rv; + break; + } + } + + if(action != ABORT) + emlwrite("", NULL); + } + + if(action == 'R' && curwp->w_markp){ + /* let yank() know that it may be restoring a paragraph */ + thisflag |= CFFILL; + + if(!Pmaster) + sgarbk = TRUE; + + curwp->w_flag |= WFMODE; + + swap_mark_and_dot_if_mark_comes_first(); + + /* determine if we're justifying quoted text or not */ + qstr = (glo_quote_str + && quote_match(glo_quote_str, + curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp, + qstr2, NSTRING) + && *qstr2) ? qstr2 : NULL; + + + /* + * Fillregion moves dot to the end of the filled region. + */ + if(!fillregion(qstr, &addedregion)) + return(FALSE); + + set_last_region_added(&addedregion); + } + else if(action == 'P'){ + + /* + * Justfiy the current paragraph. + */ + + if(curwp->w_markp) /* clear mark if already set */ + setmark(0,0); + + if(gotoeop(FALSE, 1) == FALSE) + return(FALSE); + + /* determine if we're justifying quoted text or not */ + qstr = (glo_quote_str + && quote_match(glo_quote_str, + curwp->w_dotp, qstr2, NSTRING) + && *qstr2) ? qstr2 : NULL; + + setmark(0,0); /* mark last line of para */ + + /* jump back to the beginning of the paragraph */ + gotobop(FALSE, 1); + + /* let yank() know that it may be restoring a paragraph */ + thisflag |= (CFFILL | CFFLPA); + + if(!Pmaster) + sgarbk = TRUE; + + curwp->w_flag |= WFMODE; + + curwp->w_doto = 0; /* start region at beginning of line */ + + /* + * Fillregion moves dot to the end of the filled region. + */ + if(!fillregion(qstr, &addedregion)) + return(FALSE); + + set_last_region_added(&addedregion); + + /* Leave cursor on first char of first line after justified region */ + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_doto = 0; + + if(curwp->w_markp) + setmark(0,0); /* clear mark */ + } + else if(action == 'Q'){ + /* let yank() know that it may be restoring a paragraph */ + thisflag |= CFFILL; + + if(!Pmaster) + sgarbk = TRUE; + + curwp->w_flag |= WFHARD; + + swap_mark_and_dot_if_mark_comes_first(); + + if(!setquotelevelinregion(quotelevel, &addedregion)) + return(FALSE); + + set_last_region_added(&addedregion); + } + else{ + /* abort */ + } + + return(TRUE); +} + + +/* + * The region we're filling is the region from dot to mark. + * We cut out that region and then put it back in filled. + * The cut out part is saved in the ldelete call and the + * reinstalled region is noted in addedregion, so that yank() + * can delete it and restore the saved part. + */ +int +fillregion(UCS *qstr, REGION *addedregion) +{ + long c, sz, last_char = 0; + int i, j, qlen, same_word, + spaces, word_len, word_ind, line_len, ww; + int starts_midline = 0; + int ends_midline = 0; + int offset_into_start; + LINE *line_before_start, *lp; + UCS line_last, word[NSTRING]; + REGION region; + + /* if region starts midline insert a newline */ + if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp)) + starts_midline++; + + /* if region ends midline insert a newline at end */ + if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp)) + ends_midline++; + + /* cut the paragraph into our fill buffer */ + fdelete(); + if(!getregion(®ion, curwp->w_markp, curwp->w_marko)) + return(FALSE); + + if(!ldelete(region.r_size, finsert)) + return(FALSE); + + line_before_start = lback(curwp->w_dotp); + offset_into_start = curwp->w_doto; + + if(starts_midline) + lnewline(); + + /* Now insert it back wrapped */ + spaces = word_len = word_ind = line_len = same_word = 0; + qlen = qstr ? ucs4_strlen(qstr) : 0; + + /* Beginning with leading quoting... */ + if(qstr){ + i = 0; + while(qstr[i]){ + ww = wcellwidth(qstr[i]); + line_len += (ww >= 0 ? ww : 1); + linsert(1, qstr[i++]); + } + + line_last = ' '; /* no word-flush space! */ + } + + /* remove first leading quotes if any */ + if(starts_midline) + i = 0; + else + for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){ + linsert(1, line_last = (UCS) c); + line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); + } + + /* then digest the rest... */ + while((c = fremove(i++)) >= 0){ + last_char = c; + switch(c){ + case '\n' : + /* skip next quote string */ + j = 0; + while(j < qlen && ((c = fremove(i+j)) == qstr[j] || c == ' ')) + j++; + + i += j; + + + if(!spaces) + spaces++; + break; + + case TAB : + case ' ' : + spaces++; + break; + + default : + if(spaces){ /* flush word? */ + if((line_len - qlen > 0) + && line_len + word_len + 1 > fillcol + && ((ucs4_isspace(line_last)) + || (linsert(1, ' '))) + && same_word == 0 + && (line_len = fpnewline(qstr))) + line_last = ' '; /* no word-flush space! */ + + if(word_len){ /* word to write? */ + if(line_len && !ucs4_isspace(line_last)){ + linsert(1, ' '); /* need padding? */ + line_len++; + } + + line_len += word_len; + for(j = 0; j < word_ind; j++) + linsert(1, line_last = word[j]); + + if(spaces > 1 && strchr(".?!:;\")", line_last)){ + linsert(2, line_last = ' '); + line_len += 2; + } + + same_word = word_len = word_ind = 0; + } + + spaces = 0; + } + + if(word_ind + 1 >= NSTRING){ + /* Magic! Fake that we output a wrapped word */ + if((line_len - qlen > 0) && same_word == 0){ + if(!ucs4_isspace(line_last)) + linsert(1, ' '); + line_len = fpnewline(qstr); + } + same_word = 1; + line_len += word_len; + for(j = 0; j < word_ind; j++) + linsert(1, word[j]); + + word_len = word_ind = 0; + line_last = ' '; + } + + word[word_ind++] = (UCS) c; + ww = wcellwidth((UCS) c); + word_len += (ww >= 0 ? ww : 1); + + break; + } + } + + if(word_len){ + if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){ + if(!ucs4_isspace(line_last)) + linsert(1, ' '); + (void) fpnewline(qstr); + } + else if(line_len && !ucs4_isspace(line_last)) + linsert(1, ' '); + + for(j = 0; j < word_ind; j++) + linsert(1, word[j]); + } + + if(last_char == '\n') + lnewline(); + + if(ends_midline) + (void) fpnewline(qstr); + + /* + * Calculate the size of the region that was added. + */ + swapmark(0,1); /* mark current location after adds */ + addedregion->r_linep = lforw(line_before_start); + addedregion->r_offset = offset_into_start; + lp = addedregion->r_linep; + sz = llength(lp) - addedregion->r_offset; + if(lforw(lp) != curwp->w_markp->l_fp){ + lp = lforw(lp); + while(lp != curwp->w_markp->l_fp){ + sz += llength(lp) + 1; + lp = lforw(lp); + } + } + + sz -= llength(curwp->w_markp) - curwp->w_marko; + addedregion->r_size = sz; + + swapmark(0,1); + + if(ends_midline){ + /* + * We want to back up to the end of the original + * region instead of being here after the added newline. + */ + curwp->w_doto = 0; + backchar(0, 1); + unmarkbuffer(); + markregion(1); + } + + return(TRUE); +} + + +/* + * fpnewline - output a fill paragraph newline mindful of quote string + */ +int +fpnewline(UCS *quote) +{ + int len; + + lnewline(); + for(len = 0; quote && *quote; quote++){ + int ww; + + ww = wcellwidth(*quote); + len += (ww >= 0 ? ww : 1); + linsert(1, *quote); + } + + return(len); +} + + +int +setquotelevelinregion(int quotelevel, REGION *addedregion) +{ + int i, standards_based = 0; + int quote_chars = 0, backuptoprevline = 0; + int starts_midline = 0, ends_midline = 0, offset_into_start; + long c, sz; + UCS qstr_def1[] = { '>', ' ', 0}, qstr_def2[] = { '>', 0}; + LINE *lp, *line_before_start; + REGION region; + + if(curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + + if(!glo_quote_str + || !ucs4_strcmp(glo_quote_str, qstr_def1) + || !ucs4_strcmp(glo_quote_str, qstr_def2)) + standards_based++; + + if(!standards_based){ + emlwrite("Quote level setting only works with standard \"> \" quotes", NULL); + return(FALSE); + } + + /* if region starts midline insert a newline */ + if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp)) + starts_midline++; + + /* if region ends midline insert a newline at end */ + if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp)){ + ends_midline++; + backuptoprevline++; + /* count quote chars for re-insertion */ + for(i = 0; i < llength(curwp->w_markp); ++i) + if(lgetc(curwp->w_markp, i).c != '>') + break; + + quote_chars = i; + } + else if(curwp->w_marko == 0) + backuptoprevline++; + + /* find the size of the region */ + getregion(®ion, curwp->w_markp, curwp->w_marko); + + /* cut the paragraph into our fill buffer */ + fdelete(); + if(!ldelete(region.r_size, finsert)) + return(FALSE); + + line_before_start = lback(curwp->w_dotp); + offset_into_start = curwp->w_doto; + + /* if region starts midline add a newline */ + if(starts_midline) + lnewline(); + + i = 0; + while(fremove(i) >= 0){ + + /* remove all quote strs from current line */ + if(standards_based){ + while((c = fremove(i)) == '>') + i++; + + if(c == ' ') + i++; + } + else{ + } + + /* insert quotelevel quote strs */ + if(standards_based){ + linsert(quotelevel, '>'); + if(quotelevel > 0) + linsert(1, ' '); + } + else{ + } + + /* put back the actual line */ + while((c = fremove(i++)) >= 0 && c != '\n') + linsert(1, (UCS) c); + + if(c == '\n') + lnewline(); + } + + /* if region ends midline add a newline */ + if(ends_midline){ + lnewline(); + if(quote_chars){ + linsert(quote_chars, '>'); + if(curwp->w_doto < llength(curwp->w_dotp) + && lgetc(curwp->w_dotp, curwp->w_doto).c != ' ') + linsert(1, ' '); + } + } + + /* + * Calculate the size of the region that was added. + */ + swapmark(0,1); /* mark current location after adds */ + addedregion->r_linep = lforw(line_before_start); + addedregion->r_offset = offset_into_start; + lp = addedregion->r_linep; + sz = llength(lp) - addedregion->r_offset; + if(lforw(lp) != curwp->w_markp->l_fp){ + lp = lforw(lp); + while(lp != curwp->w_markp->l_fp){ + sz += llength(lp) + 1; + lp = lforw(lp); + } + } + + sz -= llength(curwp->w_markp) - curwp->w_marko; + addedregion->r_size = sz; + + swapmark(0,1); + + /* + * This puts us at the end of the quoted region instead + * of on the following line. This makes it convenient + * for the user to follow a quotelevel adjustment with + * a Justify if desired. + */ + if(backuptoprevline){ + curwp->w_doto = 0; + backchar(0, 1); + } + + if(ends_midline){ /* doesn't need fixing otherwise */ + unmarkbuffer(); + markregion(1); + } + + return (TRUE); +} |