Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1039 lines
28 KiB

  1. /* $Header: /nw/tony/src/stevie/src/RCS/search.c,v 1.16 89/08/06 09:50:51 tony Exp $
  2. *
  3. * This file contains various searching-related routines. These fall into
  4. * three groups: string searches (for /, ?, n, and N), character searches
  5. * within a single line (for f, F, t, T, etc), and "other" kinds of searches
  6. * like the '%' command, and 'word' searches.
  7. */
  8. #include "stevie.h"
  9. #include "regexp.h" /* Henry Spencer's (modified) reg. exp. routines */
  10. /*
  11. * String searches
  12. *
  13. * The actual searches are done using Henry Spencer's regular expression
  14. * library.
  15. */
  16. #define BEGWORD "([^a-zA-Z0-9_]|^)" /* replaces "\<" in search strings */
  17. #define ENDWORD "([^a-zA-Z0-9_]|$)" /* likewise replaces "\>" */
  18. #define BEGCHAR(c) (islower(c) || isupper(c) || isdigit(c) || ((c) == '_'))
  19. bool_t begword; /* does the search include a 'begin word' match */
  20. static LNPTR *bcksearch(), *fwdsearch();
  21. void HighlightLine();
  22. /*
  23. * mapstring(s) - map special backslash sequences
  24. */
  25. static char *
  26. mapstring(s)
  27. register char *s;
  28. {
  29. static char ns[80];
  30. register char *p;
  31. begword = FALSE;
  32. for (p = ns; *s ;s++) {
  33. if (*s != '\\') { /* not an escape */
  34. *p++ = *s;
  35. continue;
  36. }
  37. switch (*++s) {
  38. case '/':
  39. *p++ = '/';
  40. break;
  41. case '<':
  42. strcpy(p, BEGWORD);
  43. p += strlen(BEGWORD);
  44. begword = TRUE;
  45. break;
  46. case '>':
  47. strcpy(p, ENDWORD);
  48. p += strlen(ENDWORD);
  49. break;
  50. default:
  51. *p++ = '\\';
  52. *p++ = *s;
  53. break;
  54. }
  55. }
  56. *p++ = NUL;
  57. return ns;
  58. }
  59. static char *laststr = NULL;
  60. static int lastsdir;
  61. static LNPTR *
  62. ssearch(dir,str)
  63. int dir; /* FORWARD or BACKWARD */
  64. char *str;
  65. {
  66. LNPTR *pos;
  67. char *old_ls = laststr;
  68. reg_ic = P(P_IC); /* tell the regexp routines how to search */
  69. laststr = strsave(str);
  70. lastsdir = dir;
  71. if (old_ls != NULL)
  72. free(old_ls);
  73. if (dir == BACKWARD) {
  74. smsg("?%s", laststr);
  75. pos = bcksearch(mapstring(laststr));
  76. } else {
  77. smsg("/%s", laststr);
  78. pos = fwdsearch(mapstring(laststr));
  79. }
  80. /*
  81. * This is kind of a kludge, but its needed to make
  82. * 'beginning of word' searches land on the right place.
  83. */
  84. if (pos != NULL && begword) {
  85. if (pos->index != 0 || !BEGCHAR(pos->linep->s[0]))
  86. pos->index += 1;
  87. }
  88. return pos;
  89. }
  90. bool_t
  91. dosearch(dir,str)
  92. int dir;
  93. char *str;
  94. {
  95. LNPTR *p;
  96. if (str == NULL) {
  97. if (laststr == NULL) {
  98. msg("No previous regular expression");
  99. got_int = FALSE;
  100. return FALSE;
  101. }
  102. str = laststr;
  103. }
  104. got_int = FALSE;
  105. if ((p = ssearch(dir,str)) == NULL) {
  106. if (got_int)
  107. msg("Interrupt");
  108. else
  109. msg("Pattern not found");
  110. got_int = FALSE;
  111. return FALSE;
  112. } else {
  113. LNPTR savep;
  114. char string[256];
  115. unsigned long lno;
  116. unsigned long toplno;
  117. cursupdate();
  118. /*
  119. * if we're backing up, we make sure the line we're on
  120. * is on the screen.
  121. */
  122. setpcmark();
  123. *Curschar = savep = *p;
  124. set_want_col = TRUE;
  125. cursupdate();
  126. HighlightLine(0,
  127. Cursrow,
  128. p->linep->s);
  129. return TRUE;
  130. }
  131. }
  132. #define OTHERDIR(x) (((x) == FORWARD) ? BACKWARD : FORWARD)
  133. bool_t
  134. repsearch(flag)
  135. int flag;
  136. {
  137. int dir = lastsdir;
  138. bool_t found;
  139. if ( laststr == NULL ) {
  140. beep();
  141. return FALSE;
  142. }
  143. found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
  144. /*
  145. * We have to save and restore 'lastsdir' because it gets munged
  146. * by ssearch() and winds up saving the wrong direction from here
  147. * if 'flag' is true.
  148. */
  149. lastsdir = dir;
  150. return found;
  151. }
  152. /*
  153. * regerror - called by regexp routines when errors are detected.
  154. */
  155. void
  156. regerror(s)
  157. char *s;
  158. {
  159. emsg(s);
  160. }
  161. static LNPTR *
  162. fwdsearch(str)
  163. register char *str;
  164. {
  165. static LNPTR infile;
  166. register LNPTR *p;
  167. regexp *prog;
  168. register char *s;
  169. register int i;
  170. if ((prog = regcomp(str)) == NULL) {
  171. emsg("Invalid search string");
  172. return NULL;
  173. }
  174. p = Curschar;
  175. i = Curschar->index + 1;
  176. do {
  177. s = p->linep->s + i;
  178. if (regexec(prog, s, i == 0)) { /* got a match */
  179. infile.linep = p->linep;
  180. infile.index = (int) (prog->startp[0] - p->linep->s);
  181. free((char *)prog);
  182. return (&infile);
  183. }
  184. i = 0;
  185. if (got_int)
  186. goto fwdfail;
  187. } while ((p = nextline(p)) != NULL);
  188. /*
  189. * If wrapscan isn't set, then don't scan from the beginning
  190. * of the file. Just return failure here.
  191. */
  192. if (!P(P_WS))
  193. goto fwdfail;
  194. /* search from the beginning of the file to Curschar */
  195. for (p = Filemem; p != NULL ;p = nextline(p)) {
  196. s = p->linep->s;
  197. if (regexec(prog, s, TRUE)) { /* got a match */
  198. infile.linep = p->linep;
  199. infile.index = (int) (prog->startp[0] - s);
  200. free((char *)prog);
  201. return (&infile);
  202. }
  203. if (p->linep == Curschar->linep)
  204. break;
  205. if (got_int)
  206. goto fwdfail;
  207. }
  208. fwdfail:
  209. free((char *)prog);
  210. return NULL;
  211. }
  212. static LNPTR *
  213. bcksearch(str)
  214. char *str;
  215. {
  216. static LNPTR infile;
  217. register LNPTR *p = &infile;
  218. register char *s;
  219. register int i;
  220. register char *match;
  221. regexp *prog;
  222. /* make sure str isn't empty */
  223. if (str == NULL || *str == NUL)
  224. return NULL;
  225. if ((prog = regcomp(str)) == NULL) {
  226. emsg("Invalid search string");
  227. return NULL;
  228. }
  229. *p = *Curschar;
  230. if (dec(p) == -1) { /* already at start of file? */
  231. *p = *Fileend;
  232. p->index = strlen(p->linep->s) - 1;
  233. }
  234. if (begword) /* so we don't get stuck on one match */
  235. dec(p);
  236. i = p->index;
  237. do {
  238. s = p->linep->s;
  239. if (regexec(prog, s, TRUE)) { /* match somewhere on line */
  240. /*
  241. * Now, if there are multiple matches on this line,
  242. * we have to get the last one. Or the last one
  243. * before the cursor, if we're on that line.
  244. */
  245. match = prog->startp[0];
  246. while (regexec(prog, prog->endp[0], FALSE)) {
  247. if ((i >= 0) && ((prog->startp[0] - s) > i))
  248. break;
  249. match = prog->startp[0];
  250. }
  251. if ((i >= 0) && ((match - s) > i)) {
  252. i = -1;
  253. continue;
  254. }
  255. infile.linep = p->linep;
  256. infile.index = (int) (match - s);
  257. free((char *)prog);
  258. return (&infile);
  259. }
  260. i = -1;
  261. if (got_int)
  262. goto bckfail;
  263. } while ((p = prevline(p)) != NULL);
  264. /*
  265. * If wrapscan isn't set, bag the search now
  266. */
  267. if (!P(P_WS))
  268. goto bckfail;
  269. /* search backward from the end of the file */
  270. p = prevline(Fileend);
  271. do {
  272. s = p->linep->s;
  273. if (regexec(prog, s, TRUE)) { /* match somewhere on line */
  274. /*
  275. * Now, if there are multiple matches on this line,
  276. * we have to get the last one.
  277. */
  278. match = prog->startp[0];
  279. while (regexec(prog, prog->endp[0], FALSE))
  280. match = prog->startp[0];
  281. infile.linep = p->linep;
  282. infile.index = (int) (match - s);
  283. free((char *)prog);
  284. return (&infile);
  285. }
  286. if (p->linep == Curschar->linep)
  287. break;
  288. if (got_int)
  289. goto bckfail;
  290. } while ((p = prevline(p)) != NULL);
  291. bckfail:
  292. free((char *)prog);
  293. return NULL;
  294. }
  295. /*
  296. * dosub(lp, up, cmd)
  297. *
  298. * Perform a substitution from line 'lp' to line 'up' using the
  299. * command pointed to by 'cmd' which should be of the form:
  300. *
  301. * /pattern/substitution/g
  302. *
  303. * The trailing 'g' is optional and, if present, indicates that multiple
  304. * substitutions should be performed on each line, if applicable.
  305. * The usual escapes are supported as described in the regexp docs.
  306. */
  307. void
  308. dosub(lp, up, cmd)
  309. LNPTR *lp, *up;
  310. char *cmd;
  311. {
  312. LINE *cp;
  313. char *pat, *sub;
  314. regexp *prog;
  315. int nsubs;
  316. bool_t do_all; /* do multiple substitutions per line */
  317. /*
  318. * If no range was given, do the current line. If only one line
  319. * was given, just do that one.
  320. */
  321. if (lp->linep == NULL)
  322. *up = *lp = *Curschar;
  323. else {
  324. if (up->linep == NULL)
  325. *up = *lp;
  326. }
  327. pat = ++cmd; /* skip the initial '/' */
  328. while (*cmd) {
  329. if (*cmd == '\\') /* next char is quoted */
  330. cmd += 2;
  331. else if (*cmd == '/') { /* delimiter */
  332. *cmd++ = NUL;
  333. break;
  334. } else
  335. cmd++; /* regular character */
  336. }
  337. if (*pat == NUL) {
  338. if (laststr == NULL) {
  339. emsg("NULL pattern specified");
  340. return;
  341. }
  342. pat = laststr;
  343. } else {
  344. if (laststr != NULL) {
  345. free(laststr);
  346. }
  347. laststr = strsave(pat);
  348. }
  349. sub = cmd;
  350. do_all = FALSE;
  351. while (*cmd) {
  352. if (*cmd == '\\') /* next char is quoted */
  353. cmd += 2;
  354. else if (*cmd == '/') { /* delimiter */
  355. do_all = (cmd[1] == 'g');
  356. *cmd++ = NUL;
  357. break;
  358. } else
  359. cmd++; /* regular character */
  360. }
  361. reg_ic = P(P_IC); /* set "ignore case" flag appropriately */
  362. if ((prog = regcomp(pat)) == NULL) {
  363. emsg("Invalid search string");
  364. return;
  365. }
  366. nsubs = 0;
  367. for (cp = lp->linep; cp != NULL ;cp = cp->next) {
  368. if (regexec(prog, cp->s, TRUE)) { /* a match on this line */
  369. char *ns, *sns, *p;
  370. /*
  371. * Get some space for a temporary buffer
  372. * to do the substitution into.
  373. */
  374. sns = ns = alloc(2048);
  375. *sns = NUL;
  376. p = cp->s;
  377. do {
  378. for (ns = sns; *ns ;ns++)
  379. ;
  380. /*
  381. * copy up to the part that matched
  382. */
  383. while (p < prog->startp[0])
  384. *ns++ = *p++;
  385. regsub(prog, sub, ns);
  386. /*
  387. * continue searching after the match
  388. */
  389. p = prog->endp[0];
  390. } while (regexec(prog, p, FALSE) && do_all);
  391. for (ns = sns; *ns ;ns++)
  392. ;
  393. /*
  394. * copy the rest of the line, that didn't match
  395. */
  396. while (*p)
  397. *ns++ = *p++;
  398. *ns = NUL;
  399. free(cp->s); /* free the original line */
  400. cp->s = strsave(sns); /* and save the modified str */
  401. cp->size = strlen(cp->s) + 1;
  402. free(sns); /* free the temp buffer */
  403. nsubs++;
  404. CHANGED;
  405. }
  406. if (cp == up->linep)
  407. break;
  408. }
  409. if (nsubs) {
  410. updatescreen();
  411. if (nsubs >= P(P_RP))
  412. smsg("%d substitution%c", nsubs, (nsubs>1) ? 's' : ' ');
  413. } else
  414. msg("No match");
  415. free((char *)prog);
  416. }
  417. /*
  418. * doglob(cmd)
  419. *
  420. * Execute a global command of the form:
  421. *
  422. * g/pattern/X
  423. *
  424. * where 'x' is a command character, currently one of the following:
  425. *
  426. * d Delete all matching lines
  427. * p Print all matching lines
  428. *
  429. * The command character (as well as the trailing slash) is optional, and
  430. * is assumed to be 'p' if missing.
  431. */
  432. void
  433. doglob(lp, up, cmd)
  434. LNPTR *lp, *up;
  435. char *cmd;
  436. {
  437. LINE *cp;
  438. char *pat;
  439. regexp *prog;
  440. int ndone;
  441. char cmdchar = NUL; /* what to do with matching lines */
  442. /*
  443. * If no range was given, do every line. If only one line
  444. * was given, just do that one.
  445. */
  446. if (lp->linep == NULL) {
  447. *lp = *Filemem;
  448. *up = *Fileend;
  449. } else {
  450. if (up->linep == NULL)
  451. *up = *lp;
  452. }
  453. pat = ++cmd; /* skip the initial '/' */
  454. while (*cmd) {
  455. if (*cmd == '\\') /* next char is quoted */
  456. cmd += 2;
  457. else if (*cmd == '/') { /* delimiter */
  458. cmdchar = cmd[1];
  459. *cmd++ = NUL;
  460. break;
  461. } else
  462. cmd++; /* regular character */
  463. }
  464. if (cmdchar == NUL)
  465. cmdchar = 'p';
  466. reg_ic = P(P_IC); /* set "ignore case" flag appropriately */
  467. if (cmdchar != 'd' && cmdchar != 'p') {
  468. emsg("Invalid command character");
  469. return;
  470. }
  471. if (*pat == NUL) {
  472. //
  473. // Check and use previous expressions.
  474. //
  475. if (laststr != NULL) {
  476. pat = laststr;
  477. }
  478. } else {
  479. if (laststr != NULL) {
  480. free(laststr);
  481. }
  482. laststr = strsave(pat);
  483. }
  484. if ((prog = regcomp(pat)) == NULL) {
  485. emsg("Invalid search string");
  486. return;
  487. }
  488. msg("");
  489. ndone = 0;
  490. got_int = FALSE;
  491. for (cp = lp->linep; cp != NULL && !got_int ;cp = cp->next) {
  492. if (regexec(prog, cp->s, TRUE)) { /* a match on this line */
  493. switch (cmdchar) {
  494. case 'd': /* delete the line */
  495. if (Curschar->linep != cp) {
  496. LNPTR savep;
  497. savep = *Curschar;
  498. Curschar->linep = cp;
  499. Curschar->index = 0;
  500. delline(1, FALSE);
  501. *Curschar = savep;
  502. } else
  503. delline(1, FALSE);
  504. break;
  505. case 'p': /* print the line */
  506. prt_line(cp->s);
  507. outstr("\r\n");
  508. break;
  509. }
  510. ndone++;
  511. }
  512. if (cp == up->linep)
  513. break;
  514. }
  515. if (ndone) {
  516. switch (cmdchar) {
  517. case 'd':
  518. updatescreen();
  519. if (ndone >= P(P_RP) || got_int)
  520. smsg("%s%d fewer line%c",
  521. got_int ? "Interrupt: " : "",
  522. ndone,
  523. (ndone > 1) ? 's' : ' ');
  524. break;
  525. case 'p':
  526. wait_return();
  527. break;
  528. }
  529. } else {
  530. if (got_int)
  531. msg("Interrupt");
  532. else
  533. msg("No match");
  534. }
  535. got_int = FALSE;
  536. free((char *)prog);
  537. }
  538. /*
  539. * Character Searches
  540. */
  541. static char lastc = NUL; /* last character searched for */
  542. static int lastcdir; /* last direction of character search */
  543. static int lastctype; /* last type of search ("find" or "to") */
  544. /*
  545. * searchc(c, dir, type)
  546. *
  547. * Search for character 'c', in direction 'dir'. If type is 0, move to
  548. * the position of the character, otherwise move to just before the char.
  549. */
  550. bool_t
  551. searchc(c, dir, type)
  552. char c;
  553. int dir;
  554. int type;
  555. {
  556. LNPTR save;
  557. save = *Curschar; /* save position in case we fail */
  558. lastc = c;
  559. lastcdir = dir;
  560. lastctype = type;
  561. /*
  562. * On 'to' searches, skip one to start with so we can repeat
  563. * searches in the same direction and have it work right.
  564. */
  565. if (type)
  566. (dir == FORWARD) ? oneright() : oneleft();
  567. while ( (dir == FORWARD) ? oneright() : oneleft() ) {
  568. if (gchar(Curschar) == c) {
  569. if (type)
  570. (dir == FORWARD) ? oneleft() : oneright();
  571. return TRUE;
  572. }
  573. }
  574. *Curschar = save;
  575. return FALSE;
  576. }
  577. bool_t
  578. crepsearch(flag)
  579. int flag;
  580. {
  581. int dir = lastcdir;
  582. int rval;
  583. if (lastc == NUL)
  584. return FALSE;
  585. rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
  586. lastcdir = dir; /* restore dir., since it may have changed */
  587. return rval;
  588. }
  589. /*
  590. * "Other" Searches
  591. */
  592. /*
  593. * showmatch - move the cursor to the matching paren or brace
  594. */
  595. LNPTR *
  596. showmatch()
  597. {
  598. static LNPTR pos;
  599. int (*move)(), inc(), dec();
  600. char initc = (char)gchar(Curschar); /* initial char */
  601. char findc; /* terminating char */
  602. char c;
  603. int count = 0;
  604. pos = *Curschar; /* set starting point */
  605. switch (initc) {
  606. case '(':
  607. findc = ')';
  608. move = inc;
  609. break;
  610. case ')':
  611. findc = '(';
  612. move = dec;
  613. break;
  614. case '{':
  615. findc = '}';
  616. move = inc;
  617. break;
  618. case '}':
  619. findc = '{';
  620. move = dec;
  621. break;
  622. case '[':
  623. findc = ']';
  624. move = inc;
  625. break;
  626. case ']':
  627. findc = '[';
  628. move = dec;
  629. break;
  630. default:
  631. return (LNPTR *) NULL;
  632. }
  633. while ((*move)(&pos) != -1) { /* until end of file */
  634. c = (char)gchar(&pos);
  635. if (c == initc)
  636. count++;
  637. else if (c == findc) {
  638. if (count == 0)
  639. return &pos;
  640. count--;
  641. }
  642. }
  643. return (LNPTR *) NULL; /* never found it */
  644. }
  645. /*
  646. * findfunc(dir) - Find the next function in direction 'dir'
  647. *
  648. * Return TRUE if a function was found.
  649. */
  650. bool_t
  651. findfunc(dir)
  652. int dir;
  653. {
  654. LNPTR *curr;
  655. curr = Curschar;
  656. do {
  657. curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
  658. if (curr != NULL && curr->linep->s[0] == '{') {
  659. setpcmark();
  660. *Curschar = *curr;
  661. return TRUE;
  662. }
  663. } while (curr != NULL);
  664. return FALSE;
  665. }
  666. /*
  667. * The following routines do the word searches performed by the
  668. * 'w', 'W', 'b', 'B', 'e', and 'E' commands.
  669. */
  670. /*
  671. * To perform these searches, characters are placed into one of three
  672. * classes, and transitions between classes determine word boundaries.
  673. *
  674. * The classes are:
  675. *
  676. * 0 - white space
  677. * 1 - letters, digits, and underscore
  678. * 2 - everything else
  679. */
  680. static int stype; /* type of the word motion being performed */
  681. #define C0(c) (((c) == ' ') || ((c) == '\t') || ((c) == NUL))
  682. #define C1(c) (isalpha(c) || isdigit(c) || ((c) == '_'))
  683. /*
  684. * cls(c) - returns the class of character 'c'
  685. *
  686. * The 'type' of the current search modifies the classes of characters
  687. * if a 'W', 'B', or 'E' motion is being done. In this case, chars. from
  688. * class 2 are reported as class 1 since only white space boundaries are
  689. * of interest.
  690. */
  691. static int
  692. cls(c)
  693. char c;
  694. {
  695. if (C0(c))
  696. return 0;
  697. if (C1(c))
  698. return 1;
  699. /*
  700. * If stype is non-zero, report these as class 1.
  701. */
  702. return (stype == 0) ? 2 : 1;
  703. }
  704. /*
  705. * fwd_word(pos, type) - move forward one word
  706. *
  707. * Returns the resulting position, or NULL if EOF was reached.
  708. */
  709. LNPTR *
  710. fwd_word(p, type)
  711. LNPTR *p;
  712. int type;
  713. {
  714. static LNPTR pos;
  715. int sclass = cls(gchar(p)); /* starting class */
  716. pos = *p;
  717. stype = type;
  718. /*
  719. * We always move at least one character.
  720. */
  721. if (inc(&pos) == -1)
  722. return NULL;
  723. if (sclass != 0) {
  724. while (cls(gchar(&pos)) == sclass) {
  725. if (inc(&pos) == -1)
  726. return NULL;
  727. }
  728. /*
  729. * If we went from 1 -> 2 or 2 -> 1, return here.
  730. */
  731. if (cls(gchar(&pos)) != 0)
  732. return &pos;
  733. }
  734. /* We're in white space; go to next non-white */
  735. while (cls(gchar(&pos)) == 0) {
  736. /*
  737. * We'll stop if we land on a blank line
  738. */
  739. if (pos.index == 0 && pos.linep->s[0] == NUL)
  740. break;
  741. if (inc(&pos) == -1)
  742. return NULL;
  743. }
  744. return &pos;
  745. }
  746. /*
  747. * bck_word(pos, type) - move backward one word
  748. *
  749. * Returns the resulting position, or NULL if EOF was reached.
  750. */
  751. LNPTR *
  752. bck_word(p, type)
  753. LNPTR *p;
  754. int type;
  755. {
  756. static LNPTR pos;
  757. int sclass = cls(gchar(p)); /* starting class */
  758. pos = *p;
  759. stype = type;
  760. if (dec(&pos) == -1)
  761. return NULL;
  762. /*
  763. * If we're in the middle of a word, we just have to
  764. * back up to the start of it.
  765. */
  766. if (cls(gchar(&pos)) == sclass && sclass != 0) {
  767. /*
  768. * Move backward to start of the current word
  769. */
  770. while (cls(gchar(&pos)) == sclass) {
  771. if (dec(&pos) == -1)
  772. return NULL;
  773. }
  774. inc(&pos); /* overshot - forward one */
  775. return &pos;
  776. }
  777. /*
  778. * We were at the start of a word. Go back to the start
  779. * of the prior word.
  780. */
  781. while (cls(gchar(&pos)) == 0) { /* skip any white space */
  782. /*
  783. * We'll stop if we land on a blank line
  784. */
  785. if (pos.index == 0 && pos.linep->s[0] == NUL)
  786. return &pos;
  787. if (dec(&pos) == -1)
  788. return NULL;
  789. }
  790. sclass = cls(gchar(&pos));
  791. /*
  792. * Move backward to start of this word.
  793. */
  794. while (cls(gchar(&pos)) == sclass) {
  795. if (dec(&pos) == -1)
  796. return NULL;
  797. }
  798. inc(&pos); /* overshot - forward one */
  799. return &pos;
  800. }
  801. /*
  802. * end_word(pos, type, in_change) - move to the end of the word
  803. *
  804. * There is an apparent bug in the 'e' motion of the real vi. At least
  805. * on the System V Release 3 version for the 80386. Unlike 'b' and 'w',
  806. * the 'e' motion crosses blank lines. When the real vi crosses a blank
  807. * line in an 'e' motion, the cursor is placed on the FIRST character
  808. * of the next non-blank line. The 'E' command, however, works correctly.
  809. * Since this appears to be a bug, I have not duplicated it here.
  810. *
  811. * There's a strange special case here that the 'in_change' parameter
  812. * helps us deal with. Vi effectively turns 'cw' into 'ce'. If we're on
  813. * a word with only one character, we need to stick at the current
  814. * position so we don't change two words.
  815. *
  816. * Returns the resulting position, or NULL if EOF was reached.
  817. */
  818. LNPTR *
  819. end_word(p, type, in_change)
  820. LNPTR *p;
  821. int type;
  822. bool_t in_change;
  823. {
  824. static LNPTR pos;
  825. int sclass = cls(gchar(p)); /* starting class */
  826. pos = *p;
  827. stype = type;
  828. if (inc(&pos) == -1)
  829. return NULL;
  830. /*
  831. * If we're in the middle of a word, we just have to
  832. * move to the end of it.
  833. */
  834. if (cls(gchar(&pos)) == sclass && sclass != 0) {
  835. /*
  836. * Move forward to end of the current word
  837. */
  838. while (cls(gchar(&pos)) == sclass) {
  839. if (inc(&pos) == -1)
  840. return NULL;
  841. }
  842. dec(&pos); /* overshot - forward one */
  843. return &pos;
  844. }
  845. /*
  846. * We were at the end of a word. Go to the end of the next
  847. * word, unless we're doing a change. In that case we stick
  848. * at the end of the current word.
  849. */
  850. if (in_change)
  851. return p;
  852. while (cls(gchar(&pos)) == 0) { /* skip any white space */
  853. if (inc(&pos) == -1)
  854. return NULL;
  855. }
  856. sclass = cls(gchar(&pos));
  857. /*
  858. * Move forward to end of this word.
  859. */
  860. while (cls(gchar(&pos)) == sclass) {
  861. if (inc(&pos) == -1)
  862. return NULL;
  863. }
  864. dec(&pos); /* overshot - forward one */
  865. return &pos;
  866. }