1 /* $OpenBSD: paragraph.c,v 1.34 2014/10/17 13:25:13 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 nospace = 0; 37 while (lback(curwp->w_dotp) != curbp->b_headp) { 38 curwp->w_doto = 0; 39 col = 0; 40 41 while (col < llength(curwp->w_dotp) && 42 (isspace(lgetc(curwp->w_dotp, col)))) 43 col++; 44 45 if (col >= llength(curwp->w_dotp)) { 46 if (nospace) 47 break; 48 } else 49 nospace = 1; 50 51 curwp->w_dotline--; 52 curwp->w_dotp = lback(curwp->w_dotp); 53 } 54 } 55 /* force screen update */ 56 curwp->w_rflag |= WFMOVE; 57 return (TRUE); 58 } 59 60 /* 61 * Move to end of paragraph. 62 * See comments for gotobop(). Same, but moving forwards. 63 */ 64 /* ARGSUSED */ 65 int 66 gotoeop(int f, int n) 67 { 68 int col, nospace; 69 70 /* the other way... */ 71 if (n < 0) 72 return (gotobop(f, -n)); 73 74 /* for each one asked for */ 75 while (n-- > 0) { 76 nospace = 0; 77 while (lforw(curwp->w_dotp) != curbp->b_headp) { 78 col = 0; 79 curwp->w_doto = 0; 80 81 while (col < llength(curwp->w_dotp) && 82 (isspace(lgetc(curwp->w_dotp, col)))) 83 col++; 84 85 if (col >= llength(curwp->w_dotp)) { 86 if (nospace) 87 break; 88 } else 89 nospace = 1; 90 91 curwp->w_dotp = lforw(curwp->w_dotp); 92 curwp->w_dotline++; 93 94 /* do not continue after end of buffer */ 95 if (lforw(curwp->w_dotp) == curbp->b_headp) { 96 gotoeol(FFRAND, 1); 97 curwp->w_rflag |= WFMOVE; 98 return (FALSE); 99 } 100 } 101 } 102 103 /* force screen update */ 104 curwp->w_rflag |= WFMOVE; 105 return (TRUE); 106 } 107 108 /* 109 * Justify a paragraph. Fill the current paragraph according to the current 110 * fill column. 111 */ 112 /* ARGSUSED */ 113 int 114 fillpara(int f, int n) 115 { 116 int c; /* current char during scan */ 117 int wordlen; /* length of current word */ 118 int clength; /* position on line during fill */ 119 int i; /* index during word copy */ 120 int eopflag; /* Are we at the End-Of-Paragraph? */ 121 int firstflag; /* first word? (needs no space) */ 122 int newlength; /* tentative new line length */ 123 int eolflag; /* was at end of line */ 124 int retval; /* return value */ 125 struct line *eopline; /* pointer to line just past EOP */ 126 char wbuf[MAXWORD]; /* buffer for current word */ 127 128 undo_boundary_enable(FFRAND, 0); 129 130 /* record the pointer to the line just past the EOP */ 131 (void)gotoeop(FFRAND, 1); 132 if (curwp->w_doto != 0) { 133 /* paragraph ends at end of buffer */ 134 (void)lnewline(); 135 eopline = lforw(curwp->w_dotp); 136 } else 137 eopline = curwp->w_dotp; 138 139 /* and back top the begining of the paragraph */ 140 (void)gotobop(FFRAND, 1); 141 142 /* initialize various info */ 143 while (inword() == 0 && forwchar(FFRAND, 1)); 144 145 clength = curwp->w_doto; 146 wordlen = 0; 147 148 /* scan through lines, filling words */ 149 firstflag = TRUE; 150 eopflag = FALSE; 151 while (!eopflag) { 152 153 /* get the next character in the paragraph */ 154 if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) { 155 c = ' '; 156 if (lforw(curwp->w_dotp) == eopline) 157 eopflag = TRUE; 158 } else 159 c = lgetc(curwp->w_dotp, curwp->w_doto); 160 161 /* and then delete it */ 162 if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) { 163 retval = FALSE; 164 goto cleanup; 165 } 166 167 /* if not a separator, just add it in */ 168 if (c != ' ' && c != '\t') { 169 if (wordlen < MAXWORD - 1) 170 wbuf[wordlen++] = c; 171 else { 172 /* 173 * You lose chars beyond MAXWORD if the word 174 * is too long. I'm too lazy to fix it now; it 175 * just silently truncated the word before, 176 * so I get to feel smug. 177 */ 178 ewprintf("Word too long!"); 179 } 180 } else if (wordlen) { 181 182 /* calculate tentative new length with word added */ 183 newlength = clength + 1 + wordlen; 184 185 /* 186 * if at end of line or at doublespace and previous 187 * character was one of '.','?','!' doublespace here. 188 * behave the same way if a ')' is preceded by a 189 * [.?!] and followed by a doublespace. 190 */ 191 if ((eolflag || 192 curwp->w_doto == llength(curwp->w_dotp) || 193 (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' 194 || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) || 195 (wbuf[wordlen - 1] == ')' && wordlen >= 2 && 196 ISEOSP(wbuf[wordlen - 2]))) && 197 wordlen < MAXWORD - 1) 198 wbuf[wordlen++] = ' '; 199 200 /* at a word break with a word waiting */ 201 if (newlength <= fillcol) { 202 /* add word to current line */ 203 if (!firstflag) { 204 (void)linsert(1, ' '); 205 ++clength; 206 } 207 firstflag = FALSE; 208 } else { 209 if (curwp->w_doto > 0 && 210 lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') { 211 curwp->w_doto -= 1; 212 (void)ldelete((RSIZE) 1, KNONE); 213 } 214 /* start a new line */ 215 (void)lnewline(); 216 clength = 0; 217 } 218 219 /* and add the word in in either case */ 220 for (i = 0; i < wordlen; i++) { 221 (void)linsert(1, wbuf[i]); 222 ++clength; 223 } 224 wordlen = 0; 225 } 226 } 227 /* and add a last newline for the end of our new paragraph */ 228 (void)lnewline(); 229 230 /* 231 * We really should wind up where we started, (which is hard to keep 232 * track of) but I think the end of the last line is better than the 233 * beginning of the blank line. 234 */ 235 (void)backchar(FFRAND, 1); 236 retval = TRUE; 237 cleanup: 238 undo_boundary_enable(FFRAND, 1); 239 return (retval); 240 } 241 242 /* 243 * Delete a paragraph. Delete n paragraphs starting with the current one. 244 */ 245 /* ARGSUSED */ 246 int 247 killpara(int f, int n) 248 { 249 int status, end = FALSE; /* returned status of functions */ 250 251 /* for each paragraph to delete */ 252 while (n--) { 253 254 /* mark out the end and beginning of the para to delete */ 255 if (!gotoeop(FFRAND, 1)) 256 end = TRUE; 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 if (end) 273 return (TRUE); 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_rflag |= 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 int nfill; 340 341 if ((f & FFARG) != 0) { 342 fillcol = n; 343 } else { 344 if ((rep = eread("Set fill-column: ", buf, sizeof(buf), 345 EFNEW | EFCR)) == NULL) 346 return (ABORT); 347 else if (rep[0] == '\0') 348 return (FALSE); 349 nfill = strtonum(rep, 0, INT_MAX, &es); 350 if (es != NULL) { 351 dobeep(); 352 ewprintf("Invalid fill column: %s", rep); 353 return (FALSE); 354 } 355 fillcol = nfill; 356 ewprintf("Fill column set to %d", fillcol); 357 } 358 return (TRUE); 359 } 360