Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/paragraph.c
- This file was added.
| /* $OpenBSD: paragraph.c,v 1.46 2018/11/17 09:52:34 lum Exp $ */ | |||||
| /* This file is in the public domain. */ | |||||
| /* | |||||
| * Code for dealing with paragraphs and filling. Adapted from MicroEMACS 3.6 | |||||
| * and GNU-ified by mwm@ucbvax. Several bug fixes by blarson@usc-oberon. | |||||
| */ | |||||
| #include <sys/queue.h> | |||||
| #include <ctype.h> | |||||
| #include <limits.h> | |||||
| #include <signal.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include "def.h" | |||||
| static int fillcol = 70; | |||||
| #define MAXWORD 256 | |||||
| static int findpara(void); | |||||
| static int do_gotoeop(int, int, int *); | |||||
| /* | |||||
| * Move to start of paragraph. | |||||
| * Move backwards by line, checking from the 1st character forwards for the | |||||
| * existence a non-space. If a non-space character is found, move to the | |||||
| * preceding line. Keep doing this until a line with only spaces is found or | |||||
| * the start of buffer. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| gotobop(int f, int n) | |||||
| { | |||||
| int col, nospace; | |||||
| /* the other way... */ | |||||
| if (n < 0) | |||||
| return (gotoeop(f, -n)); | |||||
| while (n-- > 0) { | |||||
| nospace = 0; | |||||
| while (lback(curwp->w_dotp) != curbp->b_headp) { | |||||
| curwp->w_doto = 0; | |||||
| col = 0; | |||||
| while (col < llength(curwp->w_dotp) && | |||||
| (isspace(lgetc(curwp->w_dotp, col)))) | |||||
| col++; | |||||
| if (col >= llength(curwp->w_dotp)) { | |||||
| if (nospace) | |||||
| break; | |||||
| } else | |||||
| nospace = 1; | |||||
| curwp->w_dotline--; | |||||
| curwp->w_dotp = lback(curwp->w_dotp); | |||||
| } | |||||
| } | |||||
| /* force screen update */ | |||||
| curwp->w_rflag |= WFMOVE; | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Move to end of paragraph. | |||||
| * See comments for gotobop(). Same, but moving forwards. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| gotoeop(int f, int n) | |||||
| { | |||||
| int i; | |||||
| return(do_gotoeop(f, n, &i)); | |||||
| } | |||||
| int | |||||
| do_gotoeop(int f, int n, int *i) | |||||
| { | |||||
| int col, nospace, j = 0; | |||||
| /* the other way... */ | |||||
| if (n < 0) | |||||
| return (gotobop(f, -n)); | |||||
| /* for each one asked for */ | |||||
| while (n-- > 0) { | |||||
| *i = ++j; | |||||
| nospace = 0; | |||||
| while (lforw(curwp->w_dotp) != curbp->b_headp) { | |||||
| col = 0; | |||||
| curwp->w_doto = 0; | |||||
| while (col < llength(curwp->w_dotp) && | |||||
| (isspace(lgetc(curwp->w_dotp, col)))) | |||||
| col++; | |||||
| if (col >= llength(curwp->w_dotp)) { | |||||
| if (nospace) | |||||
| break; | |||||
| } else | |||||
| nospace = 1; | |||||
| curwp->w_dotp = lforw(curwp->w_dotp); | |||||
| curwp->w_dotline++; | |||||
| } | |||||
| } | |||||
| /* do not continue after end of buffer */ | |||||
| if (lforw(curwp->w_dotp) == curbp->b_headp) { | |||||
| gotoeol(FFRAND, 1); | |||||
| curwp->w_rflag |= WFMOVE; | |||||
| return (FALSE); | |||||
| } | |||||
| /* force screen update */ | |||||
| curwp->w_rflag |= WFMOVE; | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Justify a paragraph. Fill the current paragraph according to the current | |||||
| * fill column. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| fillpara(int f, int n) | |||||
| { | |||||
| int c; /* current char during scan */ | |||||
| int wordlen; /* length of current word */ | |||||
| int clength; /* position on line during fill */ | |||||
| int i; /* index during word copy */ | |||||
| int eopflag; /* Are we at the End-Of-Paragraph? */ | |||||
| int firstflag; /* first word? (needs no space) */ | |||||
| int newlength; /* tentative new line length */ | |||||
| int eolflag; /* was at end of line */ | |||||
| int retval; /* return value */ | |||||
| struct line *eopline; /* pointer to line just past EOP */ | |||||
| char wbuf[MAXWORD]; /* buffer for current word */ | |||||
| if (n == 0) | |||||
| return (TRUE); | |||||
| undo_boundary_enable(FFRAND, 0); | |||||
| /* record the pointer to the line just past the EOP */ | |||||
| (void)gotoeop(FFRAND, 1); | |||||
| if (curwp->w_doto != 0) { | |||||
| /* paragraph ends at end of buffer */ | |||||
| (void)lnewline(); | |||||
| eopline = lforw(curwp->w_dotp); | |||||
| } else | |||||
| eopline = curwp->w_dotp; | |||||
| /* and back top the beginning of the paragraph */ | |||||
| (void)gotobop(FFRAND, 1); | |||||
| /* initialize various info */ | |||||
| while (inword() == 0 && forwchar(FFRAND, 1)); | |||||
| clength = curwp->w_doto; | |||||
| wordlen = 0; | |||||
| /* scan through lines, filling words */ | |||||
| firstflag = TRUE; | |||||
| eopflag = FALSE; | |||||
| while (!eopflag) { | |||||
| /* get the next character in the paragraph */ | |||||
| if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) { | |||||
| c = ' '; | |||||
| if (lforw(curwp->w_dotp) == eopline) | |||||
| eopflag = TRUE; | |||||
| } else | |||||
| c = lgetc(curwp->w_dotp, curwp->w_doto); | |||||
| /* and then delete it */ | |||||
| if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) { | |||||
| retval = FALSE; | |||||
| goto cleanup; | |||||
| } | |||||
| /* if not a separator, just add it in */ | |||||
| if (c != ' ' && c != '\t') { | |||||
| if (wordlen < MAXWORD - 1) | |||||
| wbuf[wordlen++] = c; | |||||
| else { | |||||
| /* | |||||
| * You lose chars beyond MAXWORD if the word | |||||
| * is too long. I'm too lazy to fix it now; it | |||||
| * just silently truncated the word before, | |||||
| * so I get to feel smug. | |||||
| */ | |||||
| ewprintf("Word too long!"); | |||||
| } | |||||
| } else if (wordlen) { | |||||
| /* calculate tentative new length with word added */ | |||||
| newlength = clength + 1 + wordlen; | |||||
| /* | |||||
| * if at end of line or at doublespace and previous | |||||
| * character was one of '.','?','!' doublespace here. | |||||
| * behave the same way if a ')' is preceded by a | |||||
| * [.?!] and followed by a doublespace. | |||||
| */ | |||||
| if (dblspace && (!eopflag && ((eolflag || | |||||
| curwp->w_doto == llength(curwp->w_dotp) || | |||||
| (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' | |||||
| || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) || | |||||
| (wbuf[wordlen - 1] == ')' && wordlen >= 2 && | |||||
| ISEOSP(wbuf[wordlen - 2])))) && | |||||
| wordlen < MAXWORD - 1)) | |||||
| wbuf[wordlen++] = ' '; | |||||
| /* at a word break with a word waiting */ | |||||
| if (newlength <= fillcol) { | |||||
| /* add word to current line */ | |||||
| if (!firstflag) { | |||||
| (void)linsert(1, ' '); | |||||
| ++clength; | |||||
| } | |||||
| firstflag = FALSE; | |||||
| } else { | |||||
| if (curwp->w_doto > 0 && | |||||
| lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') { | |||||
| curwp->w_doto -= 1; | |||||
| (void)ldelete((RSIZE) 1, KNONE); | |||||
| } | |||||
| /* start a new line */ | |||||
| (void)lnewline(); | |||||
| clength = 0; | |||||
| } | |||||
| /* and add the word in in either case */ | |||||
| for (i = 0; i < wordlen; i++) { | |||||
| (void)linsert(1, wbuf[i]); | |||||
| ++clength; | |||||
| } | |||||
| wordlen = 0; | |||||
| } | |||||
| } | |||||
| /* and add a last newline for the end of our new paragraph */ | |||||
| (void)lnewline(); | |||||
| /* | |||||
| * We really should wind up where we started, (which is hard to keep | |||||
| * track of) but I think the end of the last line is better than the | |||||
| * beginning of the blank line. | |||||
| */ | |||||
| (void)backchar(FFRAND, 1); | |||||
| retval = TRUE; | |||||
| cleanup: | |||||
| undo_boundary_enable(FFRAND, 1); | |||||
| return (retval); | |||||
| } | |||||
| /* | |||||
| * Delete n paragraphs. Move to the beginning of the current paragraph, or if | |||||
| * the cursor is on an empty line, move down the buffer to the first line with | |||||
| * non-space characters. Then mark n paragraphs and delete. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| killpara(int f, int n) | |||||
| { | |||||
| int lineno, status; | |||||
| if (n == 0) | |||||
| return (TRUE); | |||||
| if (findpara() == FALSE) | |||||
| return (TRUE); | |||||
| /* go to the beginning of the paragraph */ | |||||
| (void)gotobop(FFRAND, 1); | |||||
| /* take a note of the line number for after deletions and set mark */ | |||||
| lineno = curwp->w_dotline; | |||||
| curwp->w_markp = curwp->w_dotp; | |||||
| curwp->w_marko = curwp->w_doto; | |||||
| (void)gotoeop(FFRAND, n); | |||||
| if ((status = killregion(FFRAND, 1)) != TRUE) | |||||
| return (status); | |||||
| curwp->w_dotline = lineno; | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Mark n paragraphs starting with the n'th and working our way backwards. | |||||
| * This leaves the cursor at the beginning of the paragraph where markpara() | |||||
| * was invoked. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| markpara(int f, int n) | |||||
| { | |||||
| int i = 0; | |||||
| if (n == 0) | |||||
| return (TRUE); | |||||
| clearmark(FFARG, 0); | |||||
| if (findpara() == FALSE) | |||||
| return (TRUE); | |||||
| (void)do_gotoeop(FFRAND, n, &i); | |||||
| /* set the mark here */ | |||||
| curwp->w_markp = curwp->w_dotp; | |||||
| curwp->w_marko = curwp->w_doto; | |||||
| (void)gotobop(FFRAND, i); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Transpose the current paragraph with the following paragraph. If invoked | |||||
| * multiple times, transpose to the n'th paragraph. If invoked between | |||||
| * paragraphs, move to the previous paragraph, then continue. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| transposepara(int f, int n) | |||||
| { | |||||
| int i = 0, status; | |||||
| char flg; | |||||
| if (n == 0) | |||||
| return (TRUE); | |||||
| undo_boundary_enable(FFRAND, 0); | |||||
| /* find a paragraph, set mark, then goto the end */ | |||||
| gotobop(FFRAND, 1); | |||||
| curwp->w_markp = curwp->w_dotp; | |||||
| curwp->w_marko = curwp->w_doto; | |||||
| (void)gotoeop(FFRAND, 1); | |||||
| /* take a note of buffer flags - we may need them */ | |||||
| flg = curbp->b_flag; | |||||
| /* clean out kill buffer then kill region */ | |||||
| kdelete(); | |||||
| if ((status = killregion(FFRAND, 1)) != TRUE) | |||||
| return (status); | |||||
| /* | |||||
| * Now step through n paragraphs. If we reach the end of buffer, | |||||
| * stop and paste the killed region back, then display a message. | |||||
| */ | |||||
| if (do_gotoeop(FFRAND, n, &i) == FALSE) { | |||||
| ewprintf("Cannot transpose paragraph, end of buffer reached."); | |||||
| (void)gotobop(FFRAND, i); | |||||
| (void)yank(FFRAND, 1); | |||||
| curbp->b_flag = flg; | |||||
| return (FALSE); | |||||
| } | |||||
| (void)yank(FFRAND, 1); | |||||
| undo_boundary_enable(FFRAND, 1); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Go down the buffer until we find a line with non-space characters. | |||||
| */ | |||||
| int | |||||
| findpara(void) | |||||
| { | |||||
| int col, nospace = 0; | |||||
| /* we move forward to find a para to mark */ | |||||
| do { | |||||
| curwp->w_doto = 0; | |||||
| col = 0; | |||||
| /* check if we are on a blank line */ | |||||
| while (col < llength(curwp->w_dotp)) { | |||||
| if (!isspace(lgetc(curwp->w_dotp, col))) | |||||
| nospace = 1; | |||||
| col++; | |||||
| } | |||||
| if (nospace) | |||||
| break; | |||||
| if (lforw(curwp->w_dotp) == curbp->b_headp) | |||||
| return (FALSE); | |||||
| curwp->w_dotp = lforw(curwp->w_dotp); | |||||
| curwp->w_dotline++; | |||||
| } while (1); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Insert char with work wrap. Check to see if we're past fillcol, and if so, | |||||
| * justify this line. As a last step, justify the line. | |||||
| */ | |||||
| /* ARGSUSED */ | |||||
| int | |||||
| fillword(int f, int n) | |||||
| { | |||||
| char c; | |||||
| int col, i, nce; | |||||
| for (i = col = 0; col <= fillcol; ++i, ++col) { | |||||
| if (i == curwp->w_doto) | |||||
| return selfinsert(f, n); | |||||
| c = lgetc(curwp->w_dotp, i); | |||||
| if (c == '\t' | |||||
| #ifdef NOTAB | |||||
| && !(curbp->b_flag & BFNOTAB) | |||||
| #endif | |||||
| ) | |||||
| col |= 0x07; | |||||
| else if (ISCTRL(c) != FALSE) | |||||
| ++col; | |||||
| } | |||||
| if (curwp->w_doto != llength(curwp->w_dotp)) { | |||||
| (void)selfinsert(f, n); | |||||
| nce = llength(curwp->w_dotp) - curwp->w_doto; | |||||
| } else | |||||
| nce = 0; | |||||
| curwp->w_doto = i; | |||||
| if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t') | |||||
| do { | |||||
| (void)backchar(FFRAND, 1); | |||||
| } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && | |||||
| c != '\t' && curwp->w_doto > 0); | |||||
| if (curwp->w_doto == 0) | |||||
| do { | |||||
| (void)forwchar(FFRAND, 1); | |||||
| } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && | |||||
| c != '\t' && curwp->w_doto < llength(curwp->w_dotp)); | |||||
| (void)delwhite(FFRAND, 1); | |||||
| (void)lnewline(); | |||||
| i = llength(curwp->w_dotp) - nce; | |||||
| curwp->w_doto = i > 0 ? i : 0; | |||||
| curwp->w_rflag |= WFMOVE; | |||||
| if (nce == 0 && curwp->w_doto != 0) | |||||
| return (fillword(f, n)); | |||||
| return (TRUE); | |||||
| } | |||||
| /* | |||||
| * Set fill column to n for justify. | |||||
| */ | |||||
| int | |||||
| setfillcol(int f, int n) | |||||
| { | |||||
| char buf[32], *rep; | |||||
| const char *es; | |||||
| int nfill; | |||||
| if ((f & FFARG) != 0) { | |||||
| fillcol = n; | |||||
| } else { | |||||
| if ((rep = eread("Set fill-column: ", buf, sizeof(buf), | |||||
| EFNEW | EFCR)) == NULL) | |||||
| return (ABORT); | |||||
| else if (rep[0] == '\0') | |||||
| return (FALSE); | |||||
| nfill = strtonum(rep, 0, INT_MAX, &es); | |||||
| if (es != NULL) { | |||||
| dobeep(); | |||||
| ewprintf("Invalid fill column: %s", rep); | |||||
| return (FALSE); | |||||
| } | |||||
| fillcol = nfill; | |||||
| ewprintf("Fill column set to %d", fillcol); | |||||
| } | |||||
| return (TRUE); | |||||
| } | |||||
| int | |||||
| sentencespace(int f, int n) | |||||
| { | |||||
| if (f & FFARG) | |||||
| dblspace = n > 1; | |||||
| else | |||||
| dblspace = !dblspace; | |||||
| return (TRUE); | |||||
| } | |||||