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