#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: word.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2013-2021 Eduardo Chappa * Copyright 2006-2007 University of Washington * * 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; } void do_quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen) { register int i, j; int qstart, qend, k; /* * The method for determining the quote string is: * 1) strip leading and trailing whitespace from q * 2) add all leading whitespace to buf * 3) check for q * 4) if q, append q to buf and any trailing whitespace * 5) repeat steps 3 and 4 as necessary * * q in the future could be made to be an array of (UCS *)'s * (">" and whatever the user's quote_string is) */ *buf = '\0'; if(l == NULL) return; /* count leading whitespace as part of the quote */ for(j = 0; j <= llength(l) && lgetc(l, j).c == ' ' && j+1 < buflen; j++) buf[j] = lgetc(l, j).c; buf[j] = '\0'; if(q == NULL || *q == '\0') return; /* pare down q so it contains no leading or trailing whitespace */ for(i = 0; q[i] == ' '; i++); qstart = i; for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--); qend = i; /* for quote strings that are blanks, chop buf to the length of q */ if(qend <= qstart){ if(ucs4_strlen(q) < buflen) buf[ucs4_strlen(q)] = '\0'; return; } while(j <= llength(l)){ for(i = qstart; j <= llength(l) && i < qend; i++, j++) if(q[i] != lgetc(l, j).c) return; if(i >= qend){ if(ucs4_strlen(buf) + qend - qstart < (buflen - 1)) ucs4_strncat(buf, q + qstart, qend - qstart); } /* * if we're this far along, we've matched a quote string, * and should now add the following white space. */ for(k = ucs4_strlen(buf); j <= llength(l) && lgetc(l,j).c == ' ' && (k + 1 < buflen); j++, k++){ buf[k] = lgetc(l,j).c; } buf[k] = '\0'; if(j > llength(l)) return; } } /* * Return number of quotes if whatever starts the line matches the quote string */ int quote_match(UCS *q, LINE *gl, UCS *bufl, size_t buflen) { LINE *nl = gl != curbp->b_linep ? lforw(gl) : NULL; LINE *pl = lback(gl) != curbp->b_linep ? lback(gl) : NULL; UCS bufp[NSTRING], bufn[NSTRING]; int i, j, qstart, qend; int quoted_line = 0; do_quote_match(q, pl, bufp, sizeof(bufp)); /* previous line */ do_quote_match(q, gl, bufl, buflen); /* given line */ do_quote_match(q, nl, bufn, sizeof(bufn)); /* next line */ if(!ucs4_strcmp(bufp, bufl) || !ucs4_strcmp(bufl, bufn)) return ucs4_strlen(bufl); /* is this line quoted? */ if(q && *q){ /* pare down q so it contains no leading or trailing whitespace */ for(i = 0; q[i] == ' '; i++); qstart = i; for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--); qend = i; for(i = 0; i < llength(gl) && i + qstart < qend && lgetc(gl, i).c == q[i+qstart]; i++); if(i + qstart == qend) quoted_line = 1; } /* compare bufl and bufn */ for(i = 0; bufl[i] && bufn[i] && bufl[i] == bufn[i]; i++); /* go back to last non-space character */ for(; i > 0 && bufl[i-1] == ' '; i--); /* do bufl and bufn differ only in spaces? */ for(j = i; bufl[j] && bufl[j] == ' '; j++); /* if they differ only on trailing spaces, chop bufl to agree with bufn */ if (!bufl[j] ) bufl[Pmaster && quoted_line ? (j > i ? i+1 : i) : i] = '\0'; return ucs4_strlen(bufl); } /* 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 = 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 = 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); }