1 /* $OpenBSD: paragraph.c,v 1.29 2013/06/15 19:58:39 lum Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Code for dealing with paragraphs and filling. Adapted from MicroEMACS 3.6 7 * and GNU-ified by mwm@ucbvax. Several bug fixes by blarson@usc-oberon. 8 */ 9 10 #include <ctype.h> 11 12 #include "def.h" 13 14 static int fillcol = 70; 15 16 #define MAXWORD 256 17 18 /* 19 * Move to start of paragraph. 20 * Move backwards by line, checking from the 1st character forwards for the 21 * existence a non-space. If a non-space character is found, move to the 22 * preceding line. Keep doing this until a line with only spaces is found or 23 * the start of buffer. 24 */ 25 /* ARGSUSED */ 26 int 27 gotobop(int f, int n) 28 { 29 int col, nospace; 30 31 /* the other way... */ 32 if (n < 0) 33 return (gotoeop(f, -n)); 34 35 while (n-- > 0) { 36 while (lback(curwp->w_dotp) != curbp->b_headp) { 37 curwp->w_dotp = lback(curwp->w_dotp); 38 curwp->w_dotline--; 39 curwp->w_doto = 0; 40 col = 0; 41 42 while (col < llength(curwp->w_dotp) && 43 (isspace(lgetc(curwp->w_dotp, col)))) 44 col++; 45 46 if (col >= llength(curwp->w_dotp)) { 47 if (nospace) 48 break; 49 } else 50 nospace = 1; 51 } 52 nospace = 0; 53 } 54 /* force screen update */ 55 curwp->w_rflag |= WFMOVE; 56 return (TRUE); 57 } 58 59 /* 60 * Move to end of paragraph. 61 * See comments for gotobop(). Same, but moving forwards. 62 */ 63 /* ARGSUSED */ 64 int 65 gotoeop(int f, int n) 66 { 67 int col, nospace; 68 69 /* the other way... */ 70 if (n < 0) 71 return (gotobop(f, -n)); 72 73 /* for each one asked for */ 74 while (n-- > 0) { 75 while (lforw(curwp->w_dotp) != curbp->b_headp) { 76 col = 0; 77 curwp->w_doto = 0; 78 79 while (col < llength(curwp->w_dotp) && 80 (isspace(lgetc(curwp->w_dotp, col)))) 81 col++; 82 83 if (col >= llength(curwp->w_dotp)) { 84 if (nospace) 85 break; 86 } else 87 nospace = 1; 88 89 curwp->w_dotp = lforw(curwp->w_dotp); 90 curwp->w_dotline++; 91 } 92 nospace = 0; 93 } 94 /* force screen update */ 95 curwp->w_rflag |= WFMOVE; 96 return (TRUE); 97 } 98 99 /* 100 * Justify a paragraph. Fill the current paragraph according to the current 101 * fill column. 102 */ 103 /* ARGSUSED */ 104 int 105 fillpara(int f, int n) 106 { 107 int c; /* current char during scan */ 108 int wordlen; /* length of current word */ 109 int clength; /* position on line during fill */ 110 int i; /* index during word copy */ 111 int eopflag; /* Are we at the End-Of-Paragraph? */ 112 int firstflag; /* first word? (needs no space) */ 113 int newlength; /* tentative new line length */ 114 int eolflag; /* was at end of line */ 115 int retval; /* return value */ 116 struct line *eopline; /* pointer to line just past EOP */ 117 char wbuf[MAXWORD]; /* buffer for current word */ 118 119 undo_boundary_enable(FFRAND, 0); 120 121 /* record the pointer to the line just past the EOP */ 122 (void)gotoeop(FFRAND, 1); 123 if (curwp->w_doto != 0) { 124 /* paragraph ends at end of buffer */ 125 (void)lnewline(); 126 eopline = lforw(curwp->w_dotp); 127 } else 128 eopline = curwp->w_dotp; 129 130 /* and back top the begining of the paragraph */ 131 (void)gotobop(FFRAND, 1); 132 133 /* initialize various info */ 134 while (inword() == 0 && forwchar(FFRAND, 1)); 135 136 clength = curwp->w_doto; 137 wordlen = 0; 138 139 /* scan through lines, filling words */ 140 firstflag = TRUE; 141 eopflag = FALSE; 142 while (!eopflag) { 143 144 /* get the next character in the paragraph */ 145 if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) { 146 c = ' '; 147 if (lforw(curwp->w_dotp) == eopline) 148 eopflag = TRUE; 149 } else 150 c = lgetc(curwp->w_dotp, curwp->w_doto); 151 152 /* and then delete it */ 153 if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) { 154 retval = FALSE; 155 goto cleanup; 156 } 157 158 /* if not a separator, just add it in */ 159 if (c != ' ' && c != '\t') { 160 if (wordlen < MAXWORD - 1) 161 wbuf[wordlen++] = c; 162 else { 163 /* 164 * You lose chars beyond MAXWORD if the word 165 * is too long. I'm too lazy to fix it now; it 166 * just silently truncated the word before, 167 * so I get to feel smug. 168 */ 169 ewprintf("Word too long!"); 170 } 171 } else if (wordlen) { 172 173 /* calculate tentative new length with word added */ 174 newlength = clength + 1 + wordlen; 175 176 /* 177 * if at end of line or at doublespace and previous 178 * character was one of '.','?','!' doublespace here. 179 * behave the same way if a ')' is preceded by a 180 * [.?!] and followed by a doublespace. 181 */ 182 if ((eolflag || 183 curwp->w_doto == llength(curwp->w_dotp) || 184 (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' 185 || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) || 186 (wbuf[wordlen - 1] == ')' && wordlen >= 2 && 187 ISEOSP(wbuf[wordlen - 2]))) && 188 wordlen < MAXWORD - 1) 189 wbuf[wordlen++] = ' '; 190 191 /* at a word break with a word waiting */ 192 if (newlength <= fillcol) { 193 /* add word to current line */ 194 if (!firstflag) { 195 (void)linsert(1, ' '); 196 ++clength; 197 } 198 firstflag = FALSE; 199 } else { 200 if (curwp->w_doto > 0 && 201 lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') { 202 curwp->w_doto -= 1; 203 (void)ldelete((RSIZE) 1, KNONE); 204 } 205 /* start a new line */ 206 (void)lnewline(); 207 clength = 0; 208 } 209 210 /* and add the word in in either case */ 211 for (i = 0; i < wordlen; i++) { 212 (void)linsert(1, wbuf[i]); 213 ++clength; 214 } 215 wordlen = 0; 216 } 217 } 218 /* and add a last newline for the end of our new paragraph */ 219 (void)lnewline(); 220 221 /* 222 * We really should wind up where we started, (which is hard to keep 223 * track of) but I think the end of the last line is better than the 224 * beginning of the blank line. 225 */ 226 (void)backchar(FFRAND, 1); 227 retval = TRUE; 228 cleanup: 229 undo_boundary_enable(FFRAND, 1); 230 return (retval); 231 } 232 233 /* 234 * Delete a paragraph. Delete n paragraphs starting with the current one. 235 */ 236 /* ARGSUSED */ 237 int 238 killpara(int f, int n) 239 { 240 int status; /* returned status of functions */ 241 242 /* for each paragraph to delete */ 243 while (n--) { 244 245 /* mark out the end and beginning of the para to delete */ 246 (void)gotoeop(FFRAND, 1); 247 248 /* set the mark here */ 249 curwp->w_markp = curwp->w_dotp; 250 curwp->w_marko = curwp->w_doto; 251 252 /* go to the beginning of the paragraph */ 253 (void)gotobop(FFRAND, 1); 254 255 /* force us to the beginning of line */ 256 curwp->w_doto = 0; 257 258 /* and delete it */ 259 if ((status = killregion(FFRAND, 1)) != TRUE) 260 return (status); 261 } 262 return (TRUE); 263 } 264 265 /* 266 * Insert char with work wrap. Check to see if we're past fillcol, and if so, 267 * justify this line. As a last step, justify the line. 268 */ 269 /* ARGSUSED */ 270 int 271 fillword(int f, int n) 272 { 273 char c; 274 int col, i, nce; 275 276 for (i = col = 0; col <= fillcol; ++i, ++col) { 277 if (i == curwp->w_doto) 278 return selfinsert(f, n); 279 c = lgetc(curwp->w_dotp, i); 280 if (c == '\t' 281 #ifdef NOTAB 282 && !(curbp->b_flag & BFNOTAB) 283 #endif 284 ) 285 col |= 0x07; 286 else if (ISCTRL(c) != FALSE) 287 ++col; 288 } 289 if (curwp->w_doto != llength(curwp->w_dotp)) { 290 (void)selfinsert(f, n); 291 nce = llength(curwp->w_dotp) - curwp->w_doto; 292 } else 293 nce = 0; 294 curwp->w_doto = i; 295 296 if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t') 297 do { 298 (void)backchar(FFRAND, 1); 299 } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && 300 c != '\t' && curwp->w_doto > 0); 301 302 if (curwp->w_doto == 0) 303 do { 304 (void)forwchar(FFRAND, 1); 305 } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && 306 c != '\t' && curwp->w_doto < llength(curwp->w_dotp)); 307 308 (void)delwhite(FFRAND, 1); 309 (void)lnewline(); 310 i = llength(curwp->w_dotp) - nce; 311 curwp->w_doto = i > 0 ? i : 0; 312 curwp->w_rflag |= WFMOVE; 313 if (nce == 0 && curwp->w_doto != 0) 314 return (fillword(f, n)); 315 return (TRUE); 316 } 317 318 /* 319 * Set fill column to n for justify. 320 */ 321 int 322 setfillcol(int f, int n) 323 { 324 char buf[32], *rep; 325 const char *es; 326 int nfill; 327 328 if ((f & FFARG) != 0) { 329 fillcol = n; 330 } else { 331 if ((rep = eread("Set fill-column: ", buf, sizeof(buf), 332 EFNEW | EFCR)) == NULL) 333 return (ABORT); 334 else if (rep[0] == '\0') 335 return (FALSE); 336 nfill = strtonum(rep, 0, INT_MAX, &es); 337 if (es != NULL) { 338 ewprintf("Invalid fill column: %s", rep); 339 return (FALSE); 340 } 341 fillcol = nfill; 342 ewprintf("Fill column set to %d", fillcol); 343 } 344 return (TRUE); 345 } 346