1 /* $OpenBSD: paragraph.c,v 1.36 2015/03/19 21:22:15 bcallah 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 <sys/queue.h> 11 #include <ctype.h> 12 #include <limits.h> 13 #include <signal.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 17 #include "def.h" 18 19 static int fillcol = 70; 20 21 #define MAXWORD 256 22 23 /* 24 * Move to start of paragraph. 25 * Move backwards by line, checking from the 1st character forwards for the 26 * existence a non-space. If a non-space character is found, move to the 27 * preceding line. Keep doing this until a line with only spaces is found or 28 * the start of buffer. 29 */ 30 /* ARGSUSED */ 31 int 32 gotobop(int f, int n) 33 { 34 int col, nospace; 35 36 /* the other way... */ 37 if (n < 0) 38 return (gotoeop(f, -n)); 39 40 while (n-- > 0) { 41 nospace = 0; 42 while (lback(curwp->w_dotp) != curbp->b_headp) { 43 curwp->w_doto = 0; 44 col = 0; 45 46 while (col < llength(curwp->w_dotp) && 47 (isspace(lgetc(curwp->w_dotp, col)))) 48 col++; 49 50 if (col >= llength(curwp->w_dotp)) { 51 if (nospace) 52 break; 53 } else 54 nospace = 1; 55 56 curwp->w_dotline--; 57 curwp->w_dotp = lback(curwp->w_dotp); 58 } 59 } 60 /* force screen update */ 61 curwp->w_rflag |= WFMOVE; 62 return (TRUE); 63 } 64 65 /* 66 * Move to end of paragraph. 67 * See comments for gotobop(). Same, but moving forwards. 68 */ 69 /* ARGSUSED */ 70 int 71 gotoeop(int f, int n) 72 { 73 int col, nospace; 74 75 /* the other way... */ 76 if (n < 0) 77 return (gotobop(f, -n)); 78 79 /* for each one asked for */ 80 while (n-- > 0) { 81 nospace = 0; 82 while (lforw(curwp->w_dotp) != curbp->b_headp) { 83 col = 0; 84 curwp->w_doto = 0; 85 86 while (col < llength(curwp->w_dotp) && 87 (isspace(lgetc(curwp->w_dotp, col)))) 88 col++; 89 90 if (col >= llength(curwp->w_dotp)) { 91 if (nospace) 92 break; 93 } else 94 nospace = 1; 95 96 curwp->w_dotp = lforw(curwp->w_dotp); 97 curwp->w_dotline++; 98 99 /* do not continue after end of buffer */ 100 if (lforw(curwp->w_dotp) == curbp->b_headp) { 101 gotoeol(FFRAND, 1); 102 curwp->w_rflag |= WFMOVE; 103 return (FALSE); 104 } 105 } 106 } 107 108 /* force screen update */ 109 curwp->w_rflag |= 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 lose chars beyond MAXWORD if the word 179 * is too long. I'm too 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 * behave the same way if a ')' is preceded by a 194 * [.?!] and followed by a doublespace. 195 */ 196 if ((eolflag || 197 curwp->w_doto == llength(curwp->w_dotp) || 198 (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' 199 || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) || 200 (wbuf[wordlen - 1] == ')' && wordlen >= 2 && 201 ISEOSP(wbuf[wordlen - 2]))) && 202 wordlen < MAXWORD - 1) 203 wbuf[wordlen++] = ' '; 204 205 /* at a word break with a word waiting */ 206 if (newlength <= fillcol) { 207 /* add word to current line */ 208 if (!firstflag) { 209 (void)linsert(1, ' '); 210 ++clength; 211 } 212 firstflag = FALSE; 213 } else { 214 if (curwp->w_doto > 0 && 215 lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') { 216 curwp->w_doto -= 1; 217 (void)ldelete((RSIZE) 1, KNONE); 218 } 219 /* start a new line */ 220 (void)lnewline(); 221 clength = 0; 222 } 223 224 /* and add the word in in either case */ 225 for (i = 0; i < wordlen; i++) { 226 (void)linsert(1, wbuf[i]); 227 ++clength; 228 } 229 wordlen = 0; 230 } 231 } 232 /* and add a last newline for the end of our new paragraph */ 233 (void)lnewline(); 234 235 /* 236 * We really should wind up where we started, (which is hard to keep 237 * track of) but I think the end of the last line is better than the 238 * beginning of the blank line. 239 */ 240 (void)backchar(FFRAND, 1); 241 retval = TRUE; 242 cleanup: 243 undo_boundary_enable(FFRAND, 1); 244 return (retval); 245 } 246 247 /* 248 * Delete a paragraph. Delete n paragraphs starting with the current one. 249 */ 250 /* ARGSUSED */ 251 int 252 killpara(int f, int n) 253 { 254 int status, end = FALSE; /* returned status of functions */ 255 256 /* for each paragraph to delete */ 257 while (n--) { 258 259 /* mark out the end and beginning of the para to delete */ 260 if (!gotoeop(FFRAND, 1)) 261 end = TRUE; 262 263 /* set the mark here */ 264 curwp->w_markp = curwp->w_dotp; 265 curwp->w_marko = curwp->w_doto; 266 267 /* go to the beginning of the paragraph */ 268 (void)gotobop(FFRAND, 1); 269 270 /* force us to the beginning of line */ 271 curwp->w_doto = 0; 272 273 /* and delete it */ 274 if ((status = killregion(FFRAND, 1)) != TRUE) 275 return (status); 276 277 if (end) 278 return (TRUE); 279 } 280 return (TRUE); 281 } 282 283 /* 284 * Insert char with work wrap. Check to see if we're past fillcol, and if so, 285 * justify this line. As a last step, justify the line. 286 */ 287 /* ARGSUSED */ 288 int 289 fillword(int f, int n) 290 { 291 char c; 292 int col, i, nce; 293 294 for (i = col = 0; col <= fillcol; ++i, ++col) { 295 if (i == curwp->w_doto) 296 return selfinsert(f, n); 297 c = lgetc(curwp->w_dotp, i); 298 if (c == '\t' 299 #ifdef NOTAB 300 && !(curbp->b_flag & BFNOTAB) 301 #endif 302 ) 303 col |= 0x07; 304 else if (ISCTRL(c) != FALSE) 305 ++col; 306 } 307 if (curwp->w_doto != llength(curwp->w_dotp)) { 308 (void)selfinsert(f, n); 309 nce = llength(curwp->w_dotp) - curwp->w_doto; 310 } else 311 nce = 0; 312 curwp->w_doto = i; 313 314 if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t') 315 do { 316 (void)backchar(FFRAND, 1); 317 } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && 318 c != '\t' && curwp->w_doto > 0); 319 320 if (curwp->w_doto == 0) 321 do { 322 (void)forwchar(FFRAND, 1); 323 } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && 324 c != '\t' && curwp->w_doto < llength(curwp->w_dotp)); 325 326 (void)delwhite(FFRAND, 1); 327 (void)lnewline(); 328 i = llength(curwp->w_dotp) - nce; 329 curwp->w_doto = i > 0 ? i : 0; 330 curwp->w_rflag |= WFMOVE; 331 if (nce == 0 && curwp->w_doto != 0) 332 return (fillword(f, n)); 333 return (TRUE); 334 } 335 336 /* 337 * Set fill column to n for justify. 338 */ 339 int 340 setfillcol(int f, int n) 341 { 342 char buf[32], *rep; 343 const char *es; 344 int nfill; 345 346 if ((f & FFARG) != 0) { 347 fillcol = n; 348 } else { 349 if ((rep = eread("Set fill-column: ", buf, sizeof(buf), 350 EFNEW | EFCR)) == NULL) 351 return (ABORT); 352 else if (rep[0] == '\0') 353 return (FALSE); 354 nfill = strtonum(rep, 0, INT_MAX, &es); 355 if (es != NULL) { 356 dobeep(); 357 ewprintf("Invalid fill column: %s", rep); 358 return (FALSE); 359 } 360 fillcol = nfill; 361 ewprintf("Fill column set to %d", fillcol); 362 } 363 return (TRUE); 364 } 365