1 /* $OpenBSD: word.c,v 1.19 2015/12/30 20:51:51 lum Exp $ */ 2 3 /* This file is in the public domain. */ 4 5 /* 6 * Word mode commands. 7 * The routines in this file implement commands that work word at a time. 8 * There are all sorts of word mode commands. 9 */ 10 11 #include <sys/queue.h> 12 #include <signal.h> 13 #include <errno.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include "def.h" 19 20 RSIZE countfword(void); 21 int grabword(char **); 22 23 /* 24 * Move the cursor backward by "n" words. All of the details of motion are 25 * performed by the "backchar" and "forwchar" routines. 26 */ 27 /* ARGSUSED */ 28 int 29 backword(int f, int n) 30 { 31 if (n < 0) 32 return (forwword(f | FFRAND, -n)); 33 if (backchar(FFRAND, 1) == FALSE) 34 return (FALSE); 35 while (n--) { 36 while (inword() == FALSE) { 37 if (backchar(FFRAND, 1) == FALSE) 38 return (TRUE); 39 } 40 while (inword() != FALSE) { 41 if (backchar(FFRAND, 1) == FALSE) 42 return (TRUE); 43 } 44 } 45 return (forwchar(FFRAND, 1)); 46 } 47 48 /* 49 * Move the cursor forward by the specified number of words. All of the 50 * motion is done by "forwchar". 51 */ 52 /* ARGSUSED */ 53 int 54 forwword(int f, int n) 55 { 56 if (n < 0) 57 return (backword(f | FFRAND, -n)); 58 while (n--) { 59 while (inword() == FALSE) { 60 if (forwchar(FFRAND, 1) == FALSE) 61 return (TRUE); 62 } 63 while (inword() != FALSE) { 64 if (forwchar(FFRAND, 1) == FALSE) 65 return (TRUE); 66 } 67 } 68 return (TRUE); 69 } 70 71 /* 72 * Transpose 2 words. 73 * The function below is artifically restricted to only a maximum of 1 iteration 74 * at the moment because the 'undo' functionality within mg needs amended for 75 * multiple movements of point, backwards and forwards. 76 */ 77 int 78 transposeword(int f, int n) 79 { 80 struct line *tmp1_w_dotp = NULL; 81 struct line *tmp2_w_dotp = NULL; 82 int tmp2_w_doto = 0; 83 int tmp1_w_dotline = 0; 84 int tmp2_w_dotline = 0; 85 int tmp1_w_doto; 86 int i; /* start-of-line space counter */ 87 int ret, s; 88 int newline; 89 int leave = 0; 90 int tmp_len; 91 char *word1 = NULL; 92 char *word2 = NULL; 93 char *chr; 94 95 if (n == 0) 96 return (TRUE); 97 98 n = 1; /* remove this line to allow muliple-iterations */ 99 100 if ((s = checkdirty(curbp)) != TRUE) 101 return (s); 102 if (curbp->b_flag & BFREADONLY) { 103 dobeep(); 104 ewprintf("Buffer is read-only"); 105 return (FALSE); 106 } 107 undo_boundary_enable(FFRAND, 0); 108 109 /* go backwards to find the start of a word to transpose. */ 110 (void)backword(FFRAND, 1); 111 ret = grabword(&word1); 112 if (ret == ABORT) { 113 ewprintf("No word to the left to tranpose."); 114 return (FALSE); 115 } 116 if (ret < 0) { 117 dobeep(); 118 ewprintf("Error copying word: %s", strerror(ret)); 119 free(word1); 120 return (FALSE); 121 } 122 123 while (n-- > 0) { 124 i = 0; 125 newline = 0; 126 127 tmp1_w_doto = curwp->w_doto; 128 tmp1_w_dotline = curwp->w_dotline; 129 tmp1_w_dotp = curwp->w_dotp; 130 131 /* go forward and find next word. */ 132 while (inword() == FALSE) { 133 if (forwchar(FFRAND, 1) == FALSE) { 134 leave = 1; 135 if (tmp1_w_dotline < curwp->w_dotline) 136 curwp->w_dotline--; 137 ewprintf("Don't have two things to transpose"); 138 break; 139 } 140 if (curwp->w_doto == 0) { 141 newline = 1; 142 i = 0; 143 } else if (newline) 144 i++; 145 } 146 if (leave) { 147 tmp2_w_doto = tmp1_w_doto; 148 tmp2_w_dotline = tmp1_w_dotline; 149 tmp2_w_dotp = tmp1_w_dotp; 150 break; 151 } 152 tmp2_w_doto = curwp->w_doto; 153 tmp2_w_dotline = curwp->w_dotline; 154 tmp2_w_dotp = curwp->w_dotp; 155 156 ret = grabword(&word2); 157 if (ret < 0 || ret == ABORT) { 158 dobeep(); 159 ewprintf("Error copying word: %s", strerror(ret)); 160 free(word1); 161 return (FALSE); 162 } 163 tmp_len = strlen(word2); 164 tmp2_w_doto += tmp_len; 165 166 curwp->w_doto = tmp1_w_doto; 167 curwp->w_dotline = tmp1_w_dotline; 168 curwp->w_dotp = tmp1_w_dotp; 169 170 /* insert shuffled along word */ 171 for (chr = word2; *chr != '\0'; ++chr) 172 linsert(1, *chr); 173 174 if (newline) 175 tmp2_w_doto = i; 176 177 curwp->w_doto = tmp2_w_doto; 178 curwp->w_dotline = tmp2_w_dotline; 179 curwp->w_dotp = tmp2_w_dotp; 180 181 word2 = NULL; 182 } 183 curwp->w_doto = tmp2_w_doto; 184 curwp->w_dotline = tmp2_w_dotline; 185 curwp->w_dotp = tmp2_w_dotp; 186 187 /* insert very first word in its new position */ 188 for (chr = word1; *chr != '\0'; ++chr) 189 linsert(1, *chr); 190 191 if (leave) 192 (void)backword(FFRAND, 1); 193 194 free(word1); 195 free(word2); 196 197 undo_boundary_enable(FFRAND, 1); 198 199 return (TRUE); 200 } 201 202 /* 203 * copy and delete word. 204 */ 205 int 206 grabword(char **word) 207 { 208 int c; 209 210 while (inword() == TRUE) { 211 c = lgetc(curwp->w_dotp, curwp->w_doto); 212 if (*word == NULL) { 213 if (asprintf(word, "%c", c) == -1) 214 return (errno); 215 } else { 216 if (asprintf(word, "%s%c", *word, c) == -1) 217 return (errno); 218 } 219 (void)forwdel(FFRAND, 1); 220 } 221 if (*word == NULL) 222 return (ABORT); 223 return (TRUE); 224 } 225 226 /* 227 * Move the cursor forward by the specified number of words. As you move, 228 * convert any characters to upper case. 229 */ 230 /* ARGSUSED */ 231 int 232 upperword(int f, int n) 233 { 234 int c, s; 235 RSIZE size; 236 237 if ((s = checkdirty(curbp)) != TRUE) 238 return (s); 239 if (curbp->b_flag & BFREADONLY) { 240 dobeep(); 241 ewprintf("Buffer is read-only"); 242 return (FALSE); 243 } 244 245 if (n < 0) 246 return (FALSE); 247 while (n--) { 248 while (inword() == FALSE) { 249 if (forwchar(FFRAND, 1) == FALSE) 250 return (TRUE); 251 } 252 size = countfword(); 253 undo_add_change(curwp->w_dotp, curwp->w_doto, size); 254 255 while (inword() != FALSE) { 256 c = lgetc(curwp->w_dotp, curwp->w_doto); 257 if (ISLOWER(c) != FALSE) { 258 c = TOUPPER(c); 259 lputc(curwp->w_dotp, curwp->w_doto, c); 260 lchange(WFFULL); 261 } 262 if (forwchar(FFRAND, 1) == FALSE) 263 return (TRUE); 264 } 265 } 266 return (TRUE); 267 } 268 269 /* 270 * Move the cursor forward by the specified number of words. As you move 271 * convert characters to lower case. 272 */ 273 /* ARGSUSED */ 274 int 275 lowerword(int f, int n) 276 { 277 int c, s; 278 RSIZE size; 279 280 if ((s = checkdirty(curbp)) != TRUE) 281 return (s); 282 if (curbp->b_flag & BFREADONLY) { 283 dobeep(); 284 ewprintf("Buffer is read-only"); 285 return (FALSE); 286 } 287 if (n < 0) 288 return (FALSE); 289 while (n--) { 290 while (inword() == FALSE) { 291 if (forwchar(FFRAND, 1) == FALSE) 292 return (TRUE); 293 } 294 size = countfword(); 295 undo_add_change(curwp->w_dotp, curwp->w_doto, size); 296 297 while (inword() != FALSE) { 298 c = lgetc(curwp->w_dotp, curwp->w_doto); 299 if (ISUPPER(c) != FALSE) { 300 c = TOLOWER(c); 301 lputc(curwp->w_dotp, curwp->w_doto, c); 302 lchange(WFFULL); 303 } 304 if (forwchar(FFRAND, 1) == FALSE) 305 return (TRUE); 306 } 307 } 308 return (TRUE); 309 } 310 311 /* 312 * Move the cursor forward by the specified number of words. As you move 313 * convert the first character of the word to upper case, and subsequent 314 * characters to lower case. Error if you try to move past the end of the 315 * buffer. 316 */ 317 /* ARGSUSED */ 318 int 319 capword(int f, int n) 320 { 321 int c, s; 322 RSIZE size; 323 324 if ((s = checkdirty(curbp)) != TRUE) 325 return (s); 326 if (curbp->b_flag & BFREADONLY) { 327 dobeep(); 328 ewprintf("Buffer is read-only"); 329 return (FALSE); 330 } 331 332 if (n < 0) 333 return (FALSE); 334 while (n--) { 335 while (inword() == FALSE) { 336 if (forwchar(FFRAND, 1) == FALSE) 337 return (TRUE); 338 } 339 size = countfword(); 340 undo_add_change(curwp->w_dotp, curwp->w_doto, size); 341 342 if (inword() != FALSE) { 343 c = lgetc(curwp->w_dotp, curwp->w_doto); 344 if (ISLOWER(c) != FALSE) { 345 c = TOUPPER(c); 346 lputc(curwp->w_dotp, curwp->w_doto, c); 347 lchange(WFFULL); 348 } 349 if (forwchar(FFRAND, 1) == FALSE) 350 return (TRUE); 351 while (inword() != FALSE) { 352 c = lgetc(curwp->w_dotp, curwp->w_doto); 353 if (ISUPPER(c) != FALSE) { 354 c = TOLOWER(c); 355 lputc(curwp->w_dotp, curwp->w_doto, c); 356 lchange(WFFULL); 357 } 358 if (forwchar(FFRAND, 1) == FALSE) 359 return (TRUE); 360 } 361 } 362 } 363 return (TRUE); 364 } 365 366 /* 367 * Count characters in word, from current position 368 */ 369 RSIZE 370 countfword() 371 { 372 RSIZE size; 373 struct line *dotp; 374 int doto; 375 376 dotp = curwp->w_dotp; 377 doto = curwp->w_doto; 378 size = 0; 379 380 while (inword() != FALSE) { 381 if (forwchar(FFRAND, 1) == FALSE) 382 /* hit the end of the buffer */ 383 goto out; 384 ++size; 385 } 386 out: 387 curwp->w_dotp = dotp; 388 curwp->w_doto = doto; 389 return (size); 390 } 391 392 393 /* 394 * Kill forward by "n" words. 395 */ 396 /* ARGSUSED */ 397 int 398 delfword(int f, int n) 399 { 400 RSIZE size; 401 struct line *dotp; 402 int doto; 403 int s; 404 405 if ((s = checkdirty(curbp)) != TRUE) 406 return (s); 407 if (curbp->b_flag & BFREADONLY) { 408 dobeep(); 409 ewprintf("Buffer is read-only"); 410 return (FALSE); 411 } 412 if (n < 0) 413 return (FALSE); 414 415 /* purge kill buffer */ 416 if ((lastflag & CFKILL) == 0) 417 kdelete(); 418 419 thisflag |= CFKILL; 420 dotp = curwp->w_dotp; 421 doto = curwp->w_doto; 422 size = 0; 423 424 while (n--) { 425 while (inword() == FALSE) { 426 if (forwchar(FFRAND, 1) == FALSE) 427 /* hit the end of the buffer */ 428 goto out; 429 ++size; 430 } 431 while (inword() != FALSE) { 432 if (forwchar(FFRAND, 1) == FALSE) 433 /* hit the end of the buffer */ 434 goto out; 435 ++size; 436 } 437 } 438 out: 439 curwp->w_dotp = dotp; 440 curwp->w_doto = doto; 441 return (ldelete(size, KFORW)); 442 } 443 444 /* 445 * Kill backwards by "n" words. The rules for success and failure are now 446 * different, to prevent strange behavior at the start of the buffer. The 447 * command only fails if something goes wrong with the actual delete of the 448 * characters. It is successful even if no characters are deleted, or if you 449 * say delete 5 words, and there are only 4 words left. I considered making 450 * the first call to "backchar" special, but decided that that would just be 451 * weird. Normally this is bound to "M-Rubout" and to "M-Backspace". 452 */ 453 /* ARGSUSED */ 454 int 455 delbword(int f, int n) 456 { 457 RSIZE size; 458 int s; 459 460 if ((s = checkdirty(curbp)) != TRUE) 461 return (s); 462 if (curbp->b_flag & BFREADONLY) { 463 dobeep(); 464 ewprintf("Buffer is read-only"); 465 return (FALSE); 466 } 467 468 if (n < 0) 469 return (FALSE); 470 471 /* purge kill buffer */ 472 if ((lastflag & CFKILL) == 0) 473 kdelete(); 474 thisflag |= CFKILL; 475 if (backchar(FFRAND, 1) == FALSE) 476 /* hit buffer start */ 477 return (TRUE); 478 479 /* one deleted */ 480 size = 1; 481 while (n--) { 482 while (inword() == FALSE) { 483 if (backchar(FFRAND, 1) == FALSE) 484 /* hit buffer start */ 485 goto out; 486 ++size; 487 } 488 while (inword() != FALSE) { 489 if (backchar(FFRAND, 1) == FALSE) 490 /* hit buffer start */ 491 goto out; 492 ++size; 493 } 494 } 495 if (forwchar(FFRAND, 1) == FALSE) 496 return (FALSE); 497 498 /* undo assumed delete */ 499 --size; 500 out: 501 return (ldelete(size, KBACK)); 502 } 503 504 /* 505 * Return TRUE if the character at dot is a character that is considered to be 506 * part of a word. The word character list is hard coded. Should be settable. 507 */ 508 int 509 inword(void) 510 { 511 /* can't use lgetc in ISWORD due to bug in OSK cpp */ 512 return (curwp->w_doto != llength(curwp->w_dotp) && 513 ISWORD(curwp->w_dotp->l_text[curwp->w_doto])); 514 } 515