Annotation of src/usr.bin/vim/search.c, Revision 1.1
1.1 ! downsj 1: /* $OpenBSD$ */
! 2: /* vi:set ts=4 sw=4:
! 3: *
! 4: * VIM - Vi IMproved by Bram Moolenaar
! 5: *
! 6: * Do ":help uganda" in Vim to read copying and usage conditions.
! 7: * Do ":help credits" in Vim to see a list of people who contributed.
! 8: */
! 9: /*
! 10: * search.c: code for normal mode searching commands
! 11: */
! 12:
! 13: #include "vim.h"
! 14: #include "globals.h"
! 15: #include "proto.h"
! 16: #include "option.h"
! 17: #include "ops.h" /* for op_inclusive */
! 18:
! 19: /* modified Henry Spencer's regular expression routines */
! 20: #include "regexp.h"
! 21:
! 22: static int inmacro __ARGS((char_u *, char_u *));
! 23: static int cls __ARGS((void));
! 24: static int skip_chars __ARGS((int, int));
! 25: static void back_in_line __ARGS((void));
! 26: static void find_first_blank __ARGS((FPOS *));
! 27: static void show_pat_in_path __ARGS((char_u *, int,
! 28: int, int, FILE *, linenr_t *, long));
! 29: static void give_warning __ARGS((char_u *));
! 30:
! 31: static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
! 32: static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";
! 33:
! 34: /*
! 35: * This file contains various searching-related routines. These fall into
! 36: * three groups:
! 37: * 1. string searches (for /, ?, n, and N)
! 38: * 2. character searches within a single line (for f, F, t, T, etc)
! 39: * 3. "other" kinds of searches like the '%' command, and 'word' searches.
! 40: */
! 41:
! 42: /*
! 43: * String searches
! 44: *
! 45: * The string search functions are divided into two levels:
! 46: * lowest: searchit(); called by do_search() and edit().
! 47: * Highest: do_search(); changes curwin->w_cursor, called by normal().
! 48: *
! 49: * The last search pattern is remembered for repeating the same search.
! 50: * This pattern is shared between the :g, :s, ? and / commands.
! 51: * This is in myregcomp().
! 52: *
! 53: * The actual string matching is done using a heavily modified version of
! 54: * Henry Spencer's regular expression library.
! 55: */
! 56:
! 57: /*
! 58: * Two search patterns are remembered: One for the :substitute command and
! 59: * one for other searches. last_pattern points to the one that was
! 60: * used the last time.
! 61: */
! 62: static char_u *search_pattern = NULL;
! 63: static int search_magic = TRUE;
! 64: static int search_no_scs = FALSE;
! 65: static char_u *subst_pattern = NULL;
! 66: static int subst_magic = TRUE;
! 67: static int subst_no_scs = FALSE;
! 68: static char_u *last_pattern = NULL;
! 69: static int last_magic = TRUE;
! 70: static int last_no_scs = FALSE;
! 71: static char_u *mr_pattern = NULL; /* pattern used by myregcomp() */
! 72:
! 73: static int want_start; /* looking for start of line? */
! 74:
! 75: /*
! 76: * Type used by find_pattern_in_path() to remember which included files have
! 77: * been searched already.
! 78: */
! 79: typedef struct SearchedFile
! 80: {
! 81: FILE *fp; /* File pointer */
! 82: char_u *name; /* Full name of file */
! 83: linenr_t lnum; /* Line we were up to in file */
! 84: int matched; /* Found a match in this file */
! 85: } SearchedFile;
! 86:
! 87: /*
! 88: * translate search pattern for vim_regcomp()
! 89: *
! 90: * sub_cmd == RE_SEARCH: save pat in search_pattern (normal search command)
! 91: * sub_cmd == RE_SUBST: save pat in subst_pattern (:substitute command)
! 92: * sub_cmd == RE_BOTH: save pat in both patterns (:global command)
! 93: * which_pat == RE_SEARCH: use previous search pattern if "pat" is NULL
! 94: * which_pat == RE_SUBST: use previous sustitute pattern if "pat" is NULL
! 95: * which_pat == RE_LAST: use last used pattern if "pat" is NULL
! 96: * options & SEARCH_HIS: put search string in history
! 97: * options & SEARCH_KEEP: keep previous search pattern
! 98: *
! 99: */
! 100: regexp *
! 101: myregcomp(pat, sub_cmd, which_pat, options)
! 102: char_u *pat;
! 103: int sub_cmd;
! 104: int which_pat;
! 105: int options;
! 106: {
! 107: rc_did_emsg = FALSE;
! 108: reg_magic = p_magic;
! 109: if (pat == NULL || *pat == NUL) /* use previous search pattern */
! 110: {
! 111: if (which_pat == RE_SEARCH)
! 112: {
! 113: if (search_pattern == NULL)
! 114: {
! 115: emsg(e_noprevre);
! 116: rc_did_emsg = TRUE;
! 117: return (regexp *) NULL;
! 118: }
! 119: pat = search_pattern;
! 120: reg_magic = search_magic;
! 121: no_smartcase = search_no_scs;
! 122: }
! 123: else if (which_pat == RE_SUBST)
! 124: {
! 125: if (subst_pattern == NULL)
! 126: {
! 127: emsg(e_nopresub);
! 128: rc_did_emsg = TRUE;
! 129: return (regexp *) NULL;
! 130: }
! 131: pat = subst_pattern;
! 132: reg_magic = subst_magic;
! 133: no_smartcase = subst_no_scs;
! 134: }
! 135: else /* which_pat == RE_LAST */
! 136: {
! 137: if (last_pattern == NULL)
! 138: {
! 139: emsg(e_noprevre);
! 140: rc_did_emsg = TRUE;
! 141: return (regexp *) NULL;
! 142: }
! 143: pat = last_pattern;
! 144: reg_magic = last_magic;
! 145: no_smartcase = last_no_scs;
! 146: }
! 147: }
! 148: else if (options & SEARCH_HIS)
! 149: add_to_history(1, pat); /* put new pattern in history */
! 150:
! 151: mr_pattern = pat;
! 152:
! 153: /*
! 154: * save the currently used pattern in the appropriate place,
! 155: * unless the pattern should not be remembered
! 156: */
! 157: if (!(options & SEARCH_KEEP))
! 158: {
! 159: /*
! 160: * search or global command
! 161: */
! 162: if (sub_cmd == RE_SEARCH || sub_cmd == RE_BOTH)
! 163: {
! 164: if (search_pattern != pat)
! 165: {
! 166: vim_free(search_pattern);
! 167: search_pattern = strsave(pat);
! 168: last_pattern = search_pattern;
! 169: search_magic = reg_magic;
! 170: last_magic = reg_magic; /* Magic sticks with the r.e. */
! 171: search_no_scs = no_smartcase;
! 172: last_no_scs = no_smartcase;
! 173: }
! 174: }
! 175: /*
! 176: * substitute or global command
! 177: */
! 178: if (sub_cmd == RE_SUBST || sub_cmd == RE_BOTH)
! 179: {
! 180: if (subst_pattern != pat)
! 181: {
! 182: vim_free(subst_pattern);
! 183: subst_pattern = strsave(pat);
! 184: last_pattern = subst_pattern;
! 185: subst_magic = reg_magic;
! 186: last_magic = reg_magic; /* Magic sticks with the r.e. */
! 187: subst_no_scs = no_smartcase;
! 188: last_no_scs = no_smartcase;
! 189: }
! 190: }
! 191: }
! 192:
! 193: want_start = (*pat == '^'); /* looking for start of line? */
! 194: set_reg_ic(pat); /* tell the vim_regexec routine how to search */
! 195: return vim_regcomp(pat);
! 196: }
! 197:
! 198: /*
! 199: * Set reg_ic according to p_ic, p_scs and the search pattern.
! 200: */
! 201: void
! 202: set_reg_ic(pat)
! 203: char_u *pat;
! 204: {
! 205: char_u *p;
! 206:
! 207: reg_ic = p_ic;
! 208: if (!no_smartcase && p_scs
! 209: #ifdef INSERT_EXPAND
! 210: && !(ctrl_x_mode && curbuf->b_p_inf)
! 211: #endif
! 212: )
! 213: {
! 214: /* don't ignore case if pattern has uppercase */
! 215: for (p = pat; *p; )
! 216: if (isupper(*p++))
! 217: reg_ic = FALSE;
! 218: }
! 219: no_smartcase = FALSE;
! 220: }
! 221:
! 222: /*
! 223: * lowest level search function.
! 224: * Search for 'count'th occurrence of 'str' in direction 'dir'.
! 225: * Start at position 'pos' and return the found position in 'pos'.
! 226: *
! 227: * if (options & SEARCH_MSG) == 0 don't give any messages
! 228: * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
! 229: * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
! 230: * if (options & SEARCH_HIS) put search pattern in history
! 231: * if (options & SEARCH_END) return position at end of match
! 232: * if (options & SEARCH_START) accept match at pos itself
! 233: * if (options & SEARCH_KEEP) keep previous search pattern
! 234: *
! 235: * Return OK for success, FAIL for failure.
! 236: */
! 237: int
! 238: searchit(pos, dir, str, count, options, which_pat)
! 239: FPOS *pos;
! 240: int dir;
! 241: char_u *str;
! 242: long count;
! 243: int options;
! 244: int which_pat;
! 245: {
! 246: int found;
! 247: linenr_t lnum; /* no init to shut up Apollo cc */
! 248: regexp *prog;
! 249: char_u *ptr;
! 250: register char_u *match = NULL, *matchend = NULL; /* init for GCC */
! 251: int loop;
! 252: FPOS start_pos;
! 253: int at_first_line;
! 254: int extra_col;
! 255: int match_ok;
! 256: char_u *p;
! 257:
! 258: if ((prog = myregcomp(str, RE_SEARCH, which_pat,
! 259: (options & (SEARCH_HIS + SEARCH_KEEP)))) == NULL)
! 260: {
! 261: if ((options & SEARCH_MSG) && !rc_did_emsg)
! 262: emsg2((char_u *)"Invalid search string: %s", mr_pattern);
! 263: return FAIL;
! 264: }
! 265:
! 266: if (options & SEARCH_START)
! 267: extra_col = 0;
! 268: else
! 269: extra_col = 1;
! 270:
! 271:
! 272: /*
! 273: * find the string
! 274: */
! 275: do /* loop for count */
! 276: {
! 277: start_pos = *pos; /* remember start pos for detecting no match */
! 278: found = 0; /* default: not found */
! 279:
! 280: /*
! 281: * Start searching in current line, unless searching backwards and
! 282: * we're in column 0 or searching forward and we're past the end of
! 283: * the line
! 284: */
! 285: if (dir == BACKWARD && start_pos.col == 0)
! 286: {
! 287: lnum = pos->lnum - 1;
! 288: at_first_line = FALSE;
! 289: }
! 290: else
! 291: {
! 292: lnum = pos->lnum;
! 293: at_first_line = TRUE;
! 294: }
! 295:
! 296: for (loop = 0; loop <= 1; ++loop) /* loop twice if 'wrapscan' set */
! 297: {
! 298: for ( ; lnum > 0 && lnum <= curbuf->b_ml.ml_line_count;
! 299: lnum += dir, at_first_line = FALSE)
! 300: {
! 301: ptr = ml_get(lnum);
! 302: /* forward search, first line */
! 303: if (dir == FORWARD && at_first_line && start_pos.col > 0 &&
! 304: want_start)
! 305: continue; /* match not possible */
! 306:
! 307: /*
! 308: * Look for a match somewhere in the line.
! 309: */
! 310: if (vim_regexec(prog, ptr, TRUE))
! 311: {
! 312: match = prog->startp[0];
! 313: matchend = prog->endp[0];
! 314:
! 315: /*
! 316: * Forward search in the first line: match should be after
! 317: * the start position. If not, continue at the end of the
! 318: * match (this is vi compatible).
! 319: */
! 320: if (dir == FORWARD && at_first_line)
! 321: {
! 322: match_ok = TRUE;
! 323: /*
! 324: * When *match == NUL the cursor will be put one back
! 325: * afterwards, compare with that position, otherwise
! 326: * "/$" will get stuck on end of line. Same for
! 327: * matchend.
! 328: */
! 329: while ((options & SEARCH_END) ?
! 330: ((int)(matchend - ptr) - 1 - (int)(*matchend == NUL) <
! 331: (int)start_pos.col + extra_col) :
! 332: ((int)(match - ptr) - (int)(*match == NUL) <
! 333: (int)start_pos.col + extra_col))
! 334: {
! 335: /*
! 336: * If vi-compatible searching, continue at the end
! 337: * of the match, otherwise continue one position
! 338: * forward.
! 339: */
! 340: if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
! 341: {
! 342: p = matchend;
! 343: if (match == p && *p != NUL)
! 344: ++p;
! 345: }
! 346: else
! 347: {
! 348: p = match;
! 349: if (*p != NUL)
! 350: ++p;
! 351: }
! 352: if (*p != NUL && vim_regexec(prog, p, FALSE))
! 353: {
! 354: match = prog->startp[0];
! 355: matchend = prog->endp[0];
! 356: }
! 357: else
! 358: {
! 359: match_ok = FALSE;
! 360: break;
! 361: }
! 362: }
! 363: if (!match_ok)
! 364: continue;
! 365: }
! 366: if (dir == BACKWARD && !want_start)
! 367: {
! 368: /*
! 369: * Now, if there are multiple matches on this line,
! 370: * we have to get the last one. Or the last one before
! 371: * the cursor, if we're on that line.
! 372: * When putting the new cursor at the end, compare
! 373: * relative to the end of the match.
! 374: */
! 375: match_ok = FALSE;
! 376: for (;;)
! 377: {
! 378: if (!at_first_line || ((options & SEARCH_END) ?
! 379: ((prog->endp[0] - ptr) - 1 + extra_col
! 380: <= (int)start_pos.col) :
! 381: ((prog->startp[0] - ptr) + extra_col
! 382: <= (int)start_pos.col)))
! 383: {
! 384: match_ok = TRUE;
! 385: match = prog->startp[0];
! 386: matchend = prog->endp[0];
! 387: }
! 388: else
! 389: break;
! 390: /*
! 391: * If vi-compatible searching, continue at the end
! 392: * of the match, otherwise continue one position
! 393: * forward.
! 394: */
! 395: if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
! 396: {
! 397: p = matchend;
! 398: if (p == match && *p != NUL)
! 399: ++p;
! 400: }
! 401: else
! 402: {
! 403: p = match;
! 404: if (*p != NUL)
! 405: ++p;
! 406: }
! 407: if (*p == NUL || !vim_regexec(prog, p, (int)FALSE))
! 408: break;
! 409: }
! 410:
! 411: /*
! 412: * If there is only a match after the cursor, skip
! 413: * this match.
! 414: */
! 415: if (!match_ok)
! 416: continue;
! 417: }
! 418:
! 419: pos->lnum = lnum;
! 420: if (options & SEARCH_END && !(options & SEARCH_NOOF))
! 421: pos->col = (int) (matchend - ptr - 1);
! 422: else
! 423: pos->col = (int) (match - ptr);
! 424: found = 1;
! 425: break;
! 426: }
! 427: line_breakcheck(); /* stop if ctrl-C typed */
! 428: if (got_int)
! 429: break;
! 430:
! 431: if (loop && lnum == start_pos.lnum)
! 432: break; /* if second loop, stop where started */
! 433: }
! 434: at_first_line = FALSE;
! 435:
! 436: /*
! 437: * stop the search if wrapscan isn't set, after an interrupt and
! 438: * after a match
! 439: */
! 440: if (!p_ws || got_int || found)
! 441: break;
! 442:
! 443: /*
! 444: * If 'wrapscan' is set we continue at the other end of the file.
! 445: * If 'shortmess' does not contain 's', we give a message.
! 446: * This message is also remembered in keep_msg for when the screen
! 447: * is redrawn. The keep_msg is cleared whenever another message is
! 448: * written.
! 449: */
! 450: if (dir == BACKWARD) /* start second loop at the other end */
! 451: {
! 452: lnum = curbuf->b_ml.ml_line_count;
! 453: if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
! 454: give_warning(top_bot_msg);
! 455: }
! 456: else
! 457: {
! 458: lnum = 1;
! 459: if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
! 460: give_warning(bot_top_msg);
! 461: }
! 462: }
! 463: if (got_int)
! 464: break;
! 465: }
! 466: while (--count > 0 && found); /* stop after count matches or no match */
! 467:
! 468: vim_free(prog);
! 469:
! 470: if (!found) /* did not find it */
! 471: {
! 472: if (got_int)
! 473: emsg(e_interr);
! 474: else if ((options & SEARCH_MSG) == SEARCH_MSG)
! 475: {
! 476: if (p_ws)
! 477: EMSG2("Pattern not found: %s", mr_pattern);
! 478: else if (lnum == 0)
! 479: EMSG2("search hit TOP without match for: %s", mr_pattern);
! 480: else
! 481: EMSG2("search hit BOTTOM without match for: %s", mr_pattern);
! 482: }
! 483: return FAIL;
! 484: }
! 485: search_match_len = matchend - match;
! 486:
! 487: return OK;
! 488: }
! 489:
! 490: /*
! 491: * Highest level string search function.
! 492: * Search for the 'count'th occurence of string 'str' in direction 'dirc'
! 493: * If 'dirc' is 0: use previous dir.
! 494: * If 'str' is NULL or empty : use previous string.
! 495: * If 'options & SEARCH_REV' : go in reverse of previous dir.
! 496: * If 'options & SEARCH_ECHO': echo the search command and handle options
! 497: * If 'options & SEARCH_MSG' : may give error message
! 498: * If 'options & SEARCH_OPT' : interpret optional flags
! 499: * If 'options & SEARCH_HIS' : put search pattern in history
! 500: * If 'options & SEARCH_NOOF': don't add offset to position
! 501: * If 'options & SEARCH_MARK': set previous context mark
! 502: * If 'options & SEARCH_KEEP': keep previous search pattern
! 503: *
! 504: * Careful: If lastoffline == TRUE and lastoff == 0 this makes the
! 505: * movement linewise without moving the match position.
! 506: *
! 507: * return 0 for failure, 1 for found, 2 for found and line offset added
! 508: */
! 509: int
! 510: do_search(dirc, str, count, options)
! 511: int dirc;
! 512: char_u *str;
! 513: long count;
! 514: int options;
! 515: {
! 516: FPOS pos; /* position of the last match */
! 517: char_u *searchstr;
! 518: static int lastsdir = '/'; /* previous search direction */
! 519: static int lastoffline;/* previous/current search has line offset */
! 520: static int lastend; /* previous/current search set cursor at end */
! 521: static long lastoff; /* previous/current line or char offset */
! 522: int old_lastsdir;
! 523: int old_lastoffline;
! 524: int old_lastend;
! 525: long old_lastoff;
! 526: int retval; /* Return value */
! 527: register char_u *p;
! 528: register long c;
! 529: char_u *dircp;
! 530: int i = 0; /* init for GCC */
! 531:
! 532: /*
! 533: * A line offset is not remembered, this is vi compatible.
! 534: */
! 535: if (lastoffline && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
! 536: {
! 537: lastoffline = FALSE;
! 538: lastoff = 0;
! 539: }
! 540:
! 541: /*
! 542: * Save the values for when (options & SEARCH_KEEP) is used.
! 543: * (there is no "if ()" around this because gcc wants them initialized)
! 544: */
! 545: old_lastsdir = lastsdir;
! 546: old_lastoffline = lastoffline;
! 547: old_lastend = lastend;
! 548: old_lastoff = lastoff;
! 549:
! 550: pos = curwin->w_cursor; /* start searching at the cursor position */
! 551:
! 552: /*
! 553: * Find out the direction of the search.
! 554: */
! 555: if (dirc == 0)
! 556: dirc = lastsdir;
! 557: else
! 558: lastsdir = dirc;
! 559: if (options & SEARCH_REV)
! 560: {
! 561: #ifdef WIN32
! 562: /* There is a bug in the Visual C++ 2.2 compiler which means that
! 563: * dirc always ends up being '/' */
! 564: dirc = (dirc == '/') ? '?' : '/';
! 565: #else
! 566: if (dirc == '/')
! 567: dirc = '?';
! 568: else
! 569: dirc = '/';
! 570: #endif
! 571: }
! 572:
! 573: /*
! 574: * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
! 575: */
! 576: for (;;)
! 577: {
! 578: searchstr = str;
! 579: dircp = NULL;
! 580: /* use previous pattern */
! 581: if (str == NULL || *str == NUL || *str == dirc)
! 582: {
! 583: if (search_pattern == NULL) /* no previous pattern */
! 584: {
! 585: emsg(e_noprevre);
! 586: retval = 0;
! 587: goto end_do_search;
! 588: }
! 589: searchstr = (char_u *)""; /* make myregcomp() use search_pattern */
! 590: }
! 591:
! 592: if (str != NULL && *str != NUL) /* look for (new) offset */
! 593: {
! 594: /*
! 595: * Find end of regular expression.
! 596: * If there is a matching '/' or '?', toss it.
! 597: */
! 598: p = skip_regexp(str, dirc);
! 599: if (*p == dirc)
! 600: {
! 601: dircp = p; /* remember where we put the NUL */
! 602: *p++ = NUL;
! 603: }
! 604: lastoffline = FALSE;
! 605: lastend = FALSE;
! 606: lastoff = 0;
! 607: /*
! 608: * Check for a line offset or a character offset.
! 609: * For get_address (echo off) we don't check for a character
! 610: * offset, because it is meaningless and the 's' could be a
! 611: * substitute command.
! 612: */
! 613: if (*p == '+' || *p == '-' || isdigit(*p))
! 614: lastoffline = TRUE;
! 615: else if ((options & SEARCH_OPT) &&
! 616: (*p == 'e' || *p == 's' || *p == 'b'))
! 617: {
! 618: if (*p == 'e') /* end */
! 619: lastend = SEARCH_END;
! 620: ++p;
! 621: }
! 622: if (isdigit(*p) || *p == '+' || *p == '-') /* got an offset */
! 623: {
! 624: if (isdigit(*p) || isdigit(*(p + 1)))
! 625: lastoff = atol((char *)p); /* 'nr' or '+nr' or '-nr' */
! 626: else if (*p == '-') /* single '-' */
! 627: lastoff = -1;
! 628: else /* single '+' */
! 629: lastoff = 1;
! 630: ++p;
! 631: while (isdigit(*p)) /* skip number */
! 632: ++p;
! 633: }
! 634: searchcmdlen = p - str; /* compute length of search command
! 635: for get_address() */
! 636: str = p; /* put str after search command */
! 637: }
! 638:
! 639: if (options & SEARCH_ECHO)
! 640: {
! 641: msg_start();
! 642: msg_outchar(dirc);
! 643: msg_outtrans(*searchstr == NUL ? search_pattern : searchstr);
! 644: if (lastoffline || lastend || lastoff)
! 645: {
! 646: msg_outchar(dirc);
! 647: if (lastend)
! 648: msg_outchar('e');
! 649: else if (!lastoffline)
! 650: msg_outchar('s');
! 651: if (lastoff < 0)
! 652: {
! 653: msg_outchar('-');
! 654: msg_outnum((long)-lastoff);
! 655: }
! 656: else if (lastoff > 0 || lastoffline)
! 657: {
! 658: msg_outchar('+');
! 659: msg_outnum((long)lastoff);
! 660: }
! 661: }
! 662: msg_clr_eos();
! 663: (void)msg_check();
! 664:
! 665: gotocmdline(FALSE);
! 666: flushbuf();
! 667: }
! 668:
! 669: /*
! 670: * If there is a character offset, subtract it from the current
! 671: * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
! 672: * This is not done for a line offset, because then we would not be vi
! 673: * compatible.
! 674: */
! 675: if (!lastoffline && lastoff)
! 676: {
! 677: if (lastoff > 0)
! 678: {
! 679: c = lastoff;
! 680: while (c--)
! 681: if ((i = dec(&pos)) != 0)
! 682: break;
! 683: if (i == -1) /* at start of buffer */
! 684: goto_endofbuf(&pos);
! 685: }
! 686: else
! 687: {
! 688: c = -lastoff;
! 689: while (c--)
! 690: if ((i = inc(&pos)) != 0)
! 691: break;
! 692: if (i == -1) /* at end of buffer */
! 693: {
! 694: pos.lnum = 1;
! 695: pos.col = 0;
! 696: }
! 697: }
! 698: }
! 699:
! 700: c = searchit(&pos, dirc == '/' ? FORWARD : BACKWARD, searchstr, count,
! 701: lastend + (options &
! 702: (SEARCH_KEEP + SEARCH_HIS + SEARCH_MSG +
! 703: ((str != NULL && *str == ';') ? 0 : SEARCH_NOOF))),
! 704: 2);
! 705: if (dircp != NULL)
! 706: *dircp = dirc; /* put second '/' or '?' back for normal() */
! 707: if (c == FAIL)
! 708: {
! 709: retval = 0;
! 710: goto end_do_search;
! 711: }
! 712: if (lastend)
! 713: op_inclusive = TRUE; /* 'e' includes last character */
! 714:
! 715: retval = 1; /* pattern found */
! 716:
! 717: /*
! 718: * Add character and/or line offset
! 719: */
! 720: if (!(options & SEARCH_NOOF) || *str == ';')
! 721: {
! 722: if (lastoffline) /* Add the offset to the line number. */
! 723: {
! 724: c = pos.lnum + lastoff;
! 725: if (c < 1)
! 726: pos.lnum = 1;
! 727: else if (c > curbuf->b_ml.ml_line_count)
! 728: pos.lnum = curbuf->b_ml.ml_line_count;
! 729: else
! 730: pos.lnum = c;
! 731: pos.col = 0;
! 732:
! 733: retval = 2; /* pattern found, line offset added */
! 734: }
! 735: else
! 736: {
! 737: if (lastoff > 0) /* to the right, check for end of line */
! 738: {
! 739: p = ml_get_pos(&pos) + 1;
! 740: c = lastoff;
! 741: while (c-- && *p++ != NUL)
! 742: ++pos.col;
! 743: }
! 744: else /* to the left, check for start of line */
! 745: {
! 746: if ((c = pos.col + lastoff) < 0)
! 747: c = 0;
! 748: pos.col = c;
! 749: }
! 750: }
! 751: }
! 752:
! 753: /*
! 754: * The search command can be followed by a ';' to do another search.
! 755: * For example: "/pat/;/foo/+3;?bar"
! 756: * This is like doing another search command, except:
! 757: * - The remembered direction '/' or '?' is from the first search.
! 758: * - When an error happens the cursor isn't moved at all.
! 759: * Don't do this when called by get_address() (it handles ';' itself).
! 760: */
! 761: if (!(options & SEARCH_OPT) || str == NULL || *str != ';')
! 762: break;
! 763:
! 764: dirc = *++str;
! 765: if (dirc != '?' && dirc != '/')
! 766: {
! 767: retval = 0;
! 768: EMSG("Expected '?' or '/' after ';'");
! 769: goto end_do_search;
! 770: }
! 771: ++str;
! 772: }
! 773:
! 774: if (options & SEARCH_MARK)
! 775: setpcmark();
! 776: curwin->w_cursor = pos;
! 777: curwin->w_set_curswant = TRUE;
! 778:
! 779: end_do_search:
! 780: if (options & SEARCH_KEEP)
! 781: {
! 782: lastsdir = old_lastsdir;
! 783: lastoffline = old_lastoffline;
! 784: lastend = old_lastend;
! 785: lastoff = old_lastoff;
! 786: }
! 787: return retval;
! 788: }
! 789:
! 790: /*
! 791: * search_for_exact_line(pos, dir, pat)
! 792: *
! 793: * Search for a line starting with the given pattern (ignoring leading
! 794: * white-space), starting from pos and going in direction dir. pos will
! 795: * contain the position of the match found. Blank lines will never match.
! 796: * Return OK for success, or FAIL if no line found.
! 797: */
! 798: int
! 799: search_for_exact_line(pos, dir, pat)
! 800: FPOS *pos;
! 801: int dir;
! 802: char_u *pat;
! 803: {
! 804: linenr_t start = 0;
! 805: char_u *ptr;
! 806: char_u *p;
! 807:
! 808: if (curbuf->b_ml.ml_line_count == 0)
! 809: return FAIL;
! 810: for (;;)
! 811: {
! 812: pos->lnum += dir;
! 813: if (pos->lnum < 1)
! 814: {
! 815: if (p_ws)
! 816: {
! 817: pos->lnum = curbuf->b_ml.ml_line_count;
! 818: if (!shortmess(SHM_SEARCH))
! 819: give_warning(top_bot_msg);
! 820: }
! 821: else
! 822: {
! 823: pos->lnum = 1;
! 824: break;
! 825: }
! 826: }
! 827: else if (pos->lnum > curbuf->b_ml.ml_line_count)
! 828: {
! 829: if (p_ws)
! 830: {
! 831: pos->lnum = 1;
! 832: if (!shortmess(SHM_SEARCH))
! 833: give_warning(bot_top_msg);
! 834: }
! 835: else
! 836: {
! 837: pos->lnum = 1;
! 838: break;
! 839: }
! 840: }
! 841: if (pos->lnum == start)
! 842: break;
! 843: if (start == 0)
! 844: start = pos->lnum;
! 845: ptr = ml_get(pos->lnum);
! 846: p = skipwhite(ptr);
! 847: pos->col = p - ptr;
! 848: if (*p != NUL && STRNCMP(p, pat, STRLEN(pat)) == 0)
! 849: return OK;
! 850: else if (*p != NUL && p_ic)
! 851: {
! 852: ptr = pat;
! 853: while (*p && TO_LOWER(*p) == TO_LOWER(*ptr))
! 854: {
! 855: ++p;
! 856: ++ptr;
! 857: }
! 858: if (*ptr == NUL)
! 859: return OK;
! 860: }
! 861: }
! 862: return FAIL;
! 863: }
! 864:
! 865: /*
! 866: * Character Searches
! 867: */
! 868:
! 869: /*
! 870: * searchc(c, dir, type, count)
! 871: *
! 872: * Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
! 873: * position of the character, otherwise move to just before the char.
! 874: * Repeat this 'count' times.
! 875: */
! 876: int
! 877: searchc(c, dir, type, count)
! 878: int c;
! 879: register int dir;
! 880: int type;
! 881: long count;
! 882: {
! 883: static int lastc = NUL; /* last character searched for */
! 884: static int lastcdir; /* last direction of character search */
! 885: static int lastctype; /* last type of search ("find" or "to") */
! 886: register int col;
! 887: char_u *p;
! 888: int len;
! 889:
! 890: if (c != NUL) /* normal search: remember args for repeat */
! 891: {
! 892: if (!KeyStuffed) /* don't remember when redoing */
! 893: {
! 894: lastc = c;
! 895: lastcdir = dir;
! 896: lastctype = type;
! 897: }
! 898: }
! 899: else /* repeat previous search */
! 900: {
! 901: if (lastc == NUL)
! 902: return FALSE;
! 903: if (dir) /* repeat in opposite direction */
! 904: dir = -lastcdir;
! 905: else
! 906: dir = lastcdir;
! 907: type = lastctype;
! 908: c = lastc;
! 909: }
! 910:
! 911: p = ml_get_curline();
! 912: col = curwin->w_cursor.col;
! 913: len = STRLEN(p);
! 914:
! 915: while (count--)
! 916: {
! 917: for (;;)
! 918: {
! 919: if ((col += dir) < 0 || col >= len)
! 920: return FALSE;
! 921: if (p[col] == c)
! 922: break;
! 923: }
! 924: }
! 925: if (type)
! 926: col -= dir;
! 927: curwin->w_cursor.col = col;
! 928: return TRUE;
! 929: }
! 930:
! 931: /*
! 932: * "Other" Searches
! 933: */
! 934:
! 935: /*
! 936: * findmatch - find the matching paren or brace
! 937: *
! 938: * Improvement over vi: Braces inside quotes are ignored.
! 939: */
! 940: FPOS *
! 941: findmatch(initc)
! 942: int initc;
! 943: {
! 944: return findmatchlimit(initc, 0, 0);
! 945: }
! 946:
! 947: /*
! 948: * findmatchlimit -- find the matching paren or brace, if it exists within
! 949: * maxtravel lines of here. A maxtravel of 0 means search until you fall off
! 950: * the edge of the file.
! 951: *
! 952: * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#')
! 953: * FM_FORWARD search forwards (when initc is '/', '*' or '#')
! 954: * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0)
! 955: * FM_SKIPCOMM skip comments (not implemented yet!)
! 956: */
! 957:
! 958: FPOS *
! 959: findmatchlimit(initc, flags, maxtravel)
! 960: int initc;
! 961: int flags;
! 962: int maxtravel;
! 963: {
! 964: static FPOS pos; /* current search position */
! 965: int findc; /* matching brace */
! 966: int c;
! 967: int count = 0; /* cumulative number of braces */
! 968: int idx = 0; /* init for gcc */
! 969: static char_u table[6] = {'(', ')', '[', ']', '{', '}'};
! 970: #ifdef RIGHTLEFT
! 971: static char_u table_rl[6] = {')', '(', ']', '[', '}', '{'};
! 972: #endif
! 973: int inquote = FALSE; /* TRUE when inside quotes */
! 974: register char_u *linep; /* pointer to current line */
! 975: char_u *ptr;
! 976: int do_quotes; /* check for quotes in current line */
! 977: int at_start; /* do_quotes value at start position */
! 978: int hash_dir = 0; /* Direction searched for # things */
! 979: int comment_dir = 0; /* Direction searched for comments */
! 980: FPOS match_pos; /* Where last slash-star was found */
! 981: int start_in_quotes; /* start position is in quotes */
! 982: int traveled = 0; /* how far we've searched so far */
! 983: int ignore_cend = FALSE; /* ignore comment end */
! 984: int cpo_match; /* vi compatible matching */
! 985: int dir; /* Direction to search */
! 986:
! 987: pos = curwin->w_cursor;
! 988: linep = ml_get(pos.lnum);
! 989:
! 990: cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
! 991:
! 992: /* Direction to search when initc is '/', '*' or '#' */
! 993: if (flags & FM_BACKWARD)
! 994: dir = BACKWARD;
! 995: else if (flags & FM_FORWARD)
! 996: dir = FORWARD;
! 997: else
! 998: dir = 0;
! 999:
! 1000: /*
! 1001: * if initc given, look in the table for the matching character
! 1002: * '/' and '*' are special cases: look for start or end of comment.
! 1003: * When '/' is used, we ignore running backwards into an star-slash, for
! 1004: * "[*" command, we just want to find any comment.
! 1005: */
! 1006: if (initc == '/' || initc == '*')
! 1007: {
! 1008: comment_dir = dir;
! 1009: if (initc == '/')
! 1010: ignore_cend = TRUE;
! 1011: idx = (dir == FORWARD) ? 0 : 1;
! 1012: initc = NUL;
! 1013: }
! 1014: else if (initc != '#' && initc != NUL)
! 1015: {
! 1016: for (idx = 0; idx < 6; ++idx)
! 1017: #ifdef RIGHTLEFT
! 1018: if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
! 1019: {
! 1020: initc = (curwin->w_p_rl ? table_rl : table)[idx = idx ^ 1];
! 1021:
! 1022: #else
! 1023: if (table[idx] == initc)
! 1024: {
! 1025: initc = table[idx = idx ^ 1];
! 1026: #endif
! 1027: break;
! 1028: }
! 1029: if (idx == 6) /* invalid initc! */
! 1030: return NULL;
! 1031: }
! 1032: /*
! 1033: * Either initc is '#', or no initc was given and we need to look under the
! 1034: * cursor.
! 1035: */
! 1036: else
! 1037: {
! 1038: if (initc == '#')
! 1039: {
! 1040: hash_dir = dir;
! 1041: }
! 1042: else
! 1043: {
! 1044: /*
! 1045: * initc was not given, must look for something to match under
! 1046: * or near the cursor.
! 1047: */
! 1048: if (linep[0] == '#' && pos.col == 0)
! 1049: {
! 1050: /* If it's not #if, #else etc, we should look for a brace
! 1051: * instead */
! 1052: for (c = 1; vim_iswhite(linep[c]); c++)
! 1053: ;
! 1054: if (STRNCMP(linep + c, "if", (size_t)2) == 0 ||
! 1055: STRNCMP(linep + c, "endif", (size_t)5) == 0 ||
! 1056: STRNCMP(linep + c, "el", (size_t)2) == 0)
! 1057: hash_dir = 1;
! 1058: }
! 1059:
! 1060: /*
! 1061: * Are we on a comment?
! 1062: */
! 1063: else if (linep[pos.col] == '/')
! 1064: {
! 1065: if (linep[pos.col + 1] == '*')
! 1066: {
! 1067: comment_dir = 1;
! 1068: idx = 0;
! 1069: pos.col++;
! 1070: }
! 1071: else if (pos.col > 0 && linep[pos.col - 1] == '*')
! 1072: {
! 1073: comment_dir = -1;
! 1074: idx = 1;
! 1075: pos.col--;
! 1076: }
! 1077: }
! 1078: else if (linep[pos.col] == '*')
! 1079: {
! 1080: if (linep[pos.col + 1] == '/')
! 1081: {
! 1082: comment_dir = -1;
! 1083: idx = 1;
! 1084: }
! 1085: else if (pos.col > 0 && linep[pos.col - 1] == '/')
! 1086: {
! 1087: comment_dir = 1;
! 1088: idx = 0;
! 1089: }
! 1090: }
! 1091:
! 1092: /*
! 1093: * If we are not on a comment or the # at the start of a line, then
! 1094: * look for brace anywhere on this line after the cursor.
! 1095: */
! 1096: if (!hash_dir && !comment_dir)
! 1097: {
! 1098: /*
! 1099: * find the brace under or after the cursor
! 1100: */
! 1101: linep = ml_get(pos.lnum);
! 1102: idx = 6; /* error if this line is empty */
! 1103: for (;;)
! 1104: {
! 1105: initc = linep[pos.col];
! 1106: if (initc == NUL)
! 1107: break;
! 1108:
! 1109: for (idx = 0; idx < 6; ++idx)
! 1110: #ifdef RIGHTLEFT
! 1111: if ((curwin->w_p_rl ? table_rl : table)[idx] == initc)
! 1112: #else
! 1113: if (table[idx] == initc)
! 1114: #endif
! 1115: break;
! 1116: if (idx != 6)
! 1117: break;
! 1118: ++pos.col;
! 1119: }
! 1120: if (idx == 6)
! 1121: {
! 1122: if (linep[0] == '#')
! 1123: hash_dir = 1;
! 1124: else
! 1125: return NULL;
! 1126: }
! 1127: }
! 1128: }
! 1129: if (hash_dir)
! 1130: {
! 1131: /*
! 1132: * Look for matching #if, #else, #elif, or #endif
! 1133: */
! 1134: op_motion_type = MLINE; /* Linewise for this case only */
! 1135: if (initc != '#')
! 1136: {
! 1137: ptr = skipwhite(linep + 1);
! 1138: if (STRNCMP(ptr, "if", (size_t)2) == 0 ||
! 1139: STRNCMP(ptr, "el", (size_t)2) == 0)
! 1140: hash_dir = 1;
! 1141: else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
! 1142: hash_dir = -1;
! 1143: else
! 1144: return NULL;
! 1145: }
! 1146: pos.col = 0;
! 1147: while (!got_int)
! 1148: {
! 1149: if (hash_dir > 0)
! 1150: {
! 1151: if (pos.lnum == curbuf->b_ml.ml_line_count)
! 1152: break;
! 1153: }
! 1154: else if (pos.lnum == 1)
! 1155: break;
! 1156: pos.lnum += hash_dir;
! 1157: linep = ml_get(pos.lnum);
! 1158: line_breakcheck();
! 1159: if (linep[0] != '#')
! 1160: continue;
! 1161: ptr = linep + 1;
! 1162: while (*ptr == ' ' || *ptr == TAB)
! 1163: ptr++;
! 1164: if (hash_dir > 0)
! 1165: {
! 1166: if (STRNCMP(ptr, "if", (size_t)2) == 0)
! 1167: count++;
! 1168: else if (STRNCMP(ptr, "el", (size_t)2) == 0)
! 1169: {
! 1170: if (count == 0)
! 1171: return &pos;
! 1172: }
! 1173: else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
! 1174: {
! 1175: if (count == 0)
! 1176: return &pos;
! 1177: count--;
! 1178: }
! 1179: }
! 1180: else
! 1181: {
! 1182: if (STRNCMP(ptr, "if", (size_t)2) == 0)
! 1183: {
! 1184: if (count == 0)
! 1185: return &pos;
! 1186: count--;
! 1187: }
! 1188: else if (initc == '#' && STRNCMP(ptr, "el", (size_t)2) == 0)
! 1189: {
! 1190: if (count == 0)
! 1191: return &pos;
! 1192: }
! 1193: else if (STRNCMP(ptr, "endif", (size_t)5) == 0)
! 1194: count++;
! 1195: }
! 1196: }
! 1197: return NULL;
! 1198: }
! 1199: }
! 1200:
! 1201: #ifdef RIGHTLEFT
! 1202: findc = (curwin->w_p_rl ? table_rl : table)[idx ^ 1];
! 1203: #else
! 1204: findc = table[idx ^ 1]; /* get matching brace */
! 1205: #endif
! 1206: idx &= 1;
! 1207:
! 1208: do_quotes = -1;
! 1209: start_in_quotes = MAYBE;
! 1210: while (!got_int)
! 1211: {
! 1212: /*
! 1213: * Go to the next position, forward or backward. We could use
! 1214: * inc() and dec() here, but that is much slower
! 1215: */
! 1216: if (idx) /* backward search */
! 1217: {
! 1218: if (pos.col == 0) /* at start of line, go to prev. one */
! 1219: {
! 1220: if (pos.lnum == 1) /* start of file */
! 1221: break;
! 1222: --pos.lnum;
! 1223:
! 1224: if (maxtravel && traveled++ > maxtravel)
! 1225: break;
! 1226:
! 1227: linep = ml_get(pos.lnum);
! 1228: pos.col = STRLEN(linep); /* put pos.col on trailing NUL */
! 1229: do_quotes = -1;
! 1230: line_breakcheck();
! 1231: }
! 1232: else
! 1233: --pos.col;
! 1234: }
! 1235: else /* forward search */
! 1236: {
! 1237: if (linep[pos.col] == NUL) /* at end of line, go to next one */
! 1238: {
! 1239: if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
! 1240: break;
! 1241: ++pos.lnum;
! 1242:
! 1243: if (maxtravel && traveled++ > maxtravel)
! 1244: break;
! 1245:
! 1246: linep = ml_get(pos.lnum);
! 1247: pos.col = 0;
! 1248: do_quotes = -1;
! 1249: line_breakcheck();
! 1250: }
! 1251: else
! 1252: ++pos.col;
! 1253: }
! 1254:
! 1255: /*
! 1256: * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
! 1257: */
! 1258: if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
! 1259: (linep[0] == '{' || linep[0] == '}'))
! 1260: {
! 1261: if (linep[0] == findc && count == 0) /* match! */
! 1262: return &pos;
! 1263: break; /* out of scope */
! 1264: }
! 1265:
! 1266: if (comment_dir)
! 1267: {
! 1268: /* Note: comments do not nest, and we ignore quotes in them */
! 1269: if (comment_dir == 1)
! 1270: {
! 1271: if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
! 1272: {
! 1273: pos.col++;
! 1274: return &pos;
! 1275: }
! 1276: }
! 1277: else /* Searching backwards */
! 1278: {
! 1279: /*
! 1280: * A comment may contain slash-star, it may also start or end
! 1281: * with slash-star-slash. I'm not using real examples though
! 1282: * because "gcc -Wall" would complain -- webb
! 1283: */
! 1284: if (pos.col == 0)
! 1285: continue;
! 1286: else if (linep[pos.col - 1] == '/' && linep[pos.col] == '*')
! 1287: {
! 1288: count++;
! 1289: match_pos = pos;
! 1290: match_pos.col--;
! 1291: }
! 1292: else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
! 1293: {
! 1294: if (count > 0)
! 1295: pos = match_pos;
! 1296: else if (pos.col > 1 && linep[pos.col - 2] == '/')
! 1297: pos.col -= 2;
! 1298: else if (ignore_cend)
! 1299: continue;
! 1300: else
! 1301: return NULL;
! 1302: return &pos;
! 1303: }
! 1304: }
! 1305: continue;
! 1306: }
! 1307:
! 1308: /*
! 1309: * If smart matching ('cpoptions' does not contain '%'), braces inside
! 1310: * of quotes are ignored, but only if there is an even number of
! 1311: * quotes in the line.
! 1312: */
! 1313: if (cpo_match)
! 1314: do_quotes = 0;
! 1315: else if (do_quotes == -1)
! 1316: {
! 1317: /*
! 1318: * count the number of quotes in the line, skipping \" and '"'
! 1319: */
! 1320: at_start = do_quotes;
! 1321: for (ptr = linep; *ptr; ++ptr)
! 1322: {
! 1323: if (ptr == linep + curwin->w_cursor.col)
! 1324: at_start = (do_quotes & 1);
! 1325: if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
! 1326: (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
! 1327: ++do_quotes;
! 1328: }
! 1329: do_quotes &= 1; /* result is 1 with even number of quotes */
! 1330:
! 1331: /*
! 1332: * If we find an uneven count, check current line and previous
! 1333: * one for a '\' at the end.
! 1334: */
! 1335: if (!do_quotes)
! 1336: {
! 1337: inquote = FALSE;
! 1338: if (ptr[-1] == '\\')
! 1339: {
! 1340: do_quotes = 1;
! 1341: if (start_in_quotes == MAYBE)
! 1342: {
! 1343: inquote = !at_start;
! 1344: if (inquote)
! 1345: start_in_quotes = TRUE;
! 1346: }
! 1347: else if (idx) /* backward search */
! 1348: inquote = TRUE;
! 1349: }
! 1350: if (pos.lnum > 1)
! 1351: {
! 1352: ptr = ml_get(pos.lnum - 1);
! 1353: if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
! 1354: {
! 1355: do_quotes = 1;
! 1356: if (start_in_quotes == MAYBE)
! 1357: {
! 1358: inquote = at_start;
! 1359: if (inquote)
! 1360: start_in_quotes = TRUE;
! 1361: }
! 1362: else if (!idx) /* forward search */
! 1363: inquote = TRUE;
! 1364: }
! 1365: }
! 1366: }
! 1367: }
! 1368: if (start_in_quotes == MAYBE)
! 1369: start_in_quotes = FALSE;
! 1370:
! 1371: /*
! 1372: * If 'smartmatch' is set:
! 1373: * Things inside quotes are ignored by setting 'inquote'. If we
! 1374: * find a quote without a preceding '\' invert 'inquote'. At the
! 1375: * end of a line not ending in '\' we reset 'inquote'.
! 1376: *
! 1377: * In lines with an uneven number of quotes (without preceding '\')
! 1378: * we do not know which part to ignore. Therefore we only set
! 1379: * inquote if the number of quotes in a line is even, unless this
! 1380: * line or the previous one ends in a '\'. Complicated, isn't it?
! 1381: */
! 1382: switch (c = linep[pos.col])
! 1383: {
! 1384: case NUL:
! 1385: /* at end of line without trailing backslash, reset inquote */
! 1386: if (pos.col == 0 || linep[pos.col - 1] != '\\')
! 1387: {
! 1388: inquote = FALSE;
! 1389: start_in_quotes = FALSE;
! 1390: }
! 1391: break;
! 1392:
! 1393: case '"':
! 1394: /* a quote that is preceded with a backslash is ignored */
! 1395: if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
! 1396: {
! 1397: inquote = !inquote;
! 1398: start_in_quotes = FALSE;
! 1399: }
! 1400: break;
! 1401:
! 1402: /*
! 1403: * If smart matching ('cpoptions' does not contain '%'):
! 1404: * Skip things in single quotes: 'x' or '\x'. Be careful for single
! 1405: * single quotes, eg jon's. Things like '\233' or '\x3f' are not
! 1406: * skipped, there is never a brace in them.
! 1407: */
! 1408: case '\'':
! 1409: if (vim_strchr(p_cpo, CPO_MATCH) == NULL)
! 1410: {
! 1411: if (idx) /* backward search */
! 1412: {
! 1413: if (pos.col > 1)
! 1414: {
! 1415: if (linep[pos.col - 2] == '\'')
! 1416: pos.col -= 2;
! 1417: else if (linep[pos.col - 2] == '\\' &&
! 1418: pos.col > 2 && linep[pos.col - 3] == '\'')
! 1419: pos.col -= 3;
! 1420: }
! 1421: }
! 1422: else if (linep[pos.col + 1]) /* forward search */
! 1423: {
! 1424: if (linep[pos.col + 1] == '\\' &&
! 1425: linep[pos.col + 2] && linep[pos.col + 3] == '\'')
! 1426: pos.col += 3;
! 1427: else if (linep[pos.col + 2] == '\'')
! 1428: pos.col += 2;
! 1429: }
! 1430: }
! 1431: break;
! 1432:
! 1433: default:
! 1434: /* Check for match outside of quotes, and inside of
! 1435: * quotes when the start is also inside of quotes */
! 1436: if (!inquote || start_in_quotes == TRUE)
! 1437: {
! 1438: if (c == initc)
! 1439: count++;
! 1440: else if (c == findc)
! 1441: {
! 1442: if (count == 0)
! 1443: return &pos;
! 1444: count--;
! 1445: }
! 1446: }
! 1447: }
! 1448: }
! 1449:
! 1450: if (comment_dir == -1 && count > 0)
! 1451: {
! 1452: pos = match_pos;
! 1453: return &pos;
! 1454: }
! 1455: return (FPOS *) NULL; /* never found it */
! 1456: }
! 1457:
! 1458: /*
! 1459: * Move cursor briefly to character matching the one under the cursor.
! 1460: * Show the match only if it is visible on the screen.
! 1461: */
! 1462: void
! 1463: showmatch()
! 1464: {
! 1465: FPOS *lpos, csave;
! 1466: colnr_t vcol;
! 1467:
! 1468: if ((lpos = findmatch(NUL)) == NULL) /* no match, so beep */
! 1469: beep_flush();
! 1470: else if (lpos->lnum >= curwin->w_topline)
! 1471: {
! 1472: if (!curwin->w_p_wrap)
! 1473: getvcol(curwin, lpos, NULL, &vcol, NULL);
! 1474: if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol &&
! 1475: vcol < curwin->w_leftcol + Columns))
! 1476: {
! 1477: updateScreen(VALID_TO_CURSCHAR); /* show the new char first */
! 1478: csave = curwin->w_cursor;
! 1479: curwin->w_cursor = *lpos; /* move to matching char */
! 1480: cursupdate();
! 1481: showruler(0);
! 1482: setcursor();
! 1483: cursor_on(); /* make sure that the cursor is shown */
! 1484: flushbuf();
! 1485:
! 1486: /*
! 1487: * brief pause, unless 'm' is present in 'cpo' and a character is
! 1488: * available.
! 1489: */
! 1490: if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
! 1491: mch_delay(500L, TRUE);
! 1492: else if (!char_avail())
! 1493: mch_delay(500L, FALSE);
! 1494: curwin->w_cursor = csave; /* restore cursor position */
! 1495: cursupdate();
! 1496: }
! 1497: }
! 1498: }
! 1499:
! 1500: /*
! 1501: * findsent(dir, count) - Find the start of the next sentence in direction
! 1502: * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
! 1503: * space or a line break. Also stop at an empty line.
! 1504: * Return OK if the next sentence was found.
! 1505: */
! 1506: int
! 1507: findsent(dir, count)
! 1508: int dir;
! 1509: long count;
! 1510: {
! 1511: FPOS pos, tpos;
! 1512: register int c;
! 1513: int (*func) __PARMS((FPOS *));
! 1514: int startlnum;
! 1515: int noskip = FALSE; /* do not skip blanks */
! 1516:
! 1517: pos = curwin->w_cursor;
! 1518: if (dir == FORWARD)
! 1519: func = incl;
! 1520: else
! 1521: func = decl;
! 1522:
! 1523: while (count--)
! 1524: {
! 1525: /*
! 1526: * if on an empty line, skip upto a non-empty line
! 1527: */
! 1528: if (gchar(&pos) == NUL)
! 1529: {
! 1530: do
! 1531: if ((*func)(&pos) == -1)
! 1532: break;
! 1533: while (gchar(&pos) == NUL);
! 1534: if (dir == FORWARD)
! 1535: goto found;
! 1536: }
! 1537: /*
! 1538: * if on the start of a paragraph or a section and searching forward,
! 1539: * go to the next line
! 1540: */
! 1541: else if (dir == FORWARD && pos.col == 0 &&
! 1542: startPS(pos.lnum, NUL, FALSE))
! 1543: {
! 1544: if (pos.lnum == curbuf->b_ml.ml_line_count)
! 1545: return FAIL;
! 1546: ++pos.lnum;
! 1547: goto found;
! 1548: }
! 1549: else if (dir == BACKWARD)
! 1550: decl(&pos);
! 1551:
! 1552: /* go back to the previous non-blank char */
! 1553: while ((c = gchar(&pos)) == ' ' || c == '\t' ||
! 1554: (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
! 1555: {
! 1556: if (decl(&pos) == -1)
! 1557: break;
! 1558: /* when going forward: Stop in front of empty line */
! 1559: if (lineempty(pos.lnum) && dir == FORWARD)
! 1560: {
! 1561: incl(&pos);
! 1562: goto found;
! 1563: }
! 1564: }
! 1565:
! 1566: /* remember the line where the search started */
! 1567: startlnum = pos.lnum;
! 1568:
! 1569: for (;;) /* find end of sentence */
! 1570: {
! 1571: c = gchar(&pos);
! 1572: if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
! 1573: {
! 1574: if (dir == BACKWARD && pos.lnum != startlnum)
! 1575: ++pos.lnum;
! 1576: break;
! 1577: }
! 1578: if (c == '.' || c == '!' || c == '?')
! 1579: {
! 1580: tpos = pos;
! 1581: do
! 1582: if ((c = inc(&tpos)) == -1)
! 1583: break;
! 1584: while (vim_strchr((char_u *)")]\"'", c = gchar(&tpos)) != NULL);
! 1585: if (c == -1 || c == ' ' || c == '\t' || c == NUL)
! 1586: {
! 1587: pos = tpos;
! 1588: if (gchar(&pos) == NUL) /* skip NUL at EOL */
! 1589: inc(&pos);
! 1590: break;
! 1591: }
! 1592: }
! 1593: if ((*func)(&pos) == -1)
! 1594: {
! 1595: if (count)
! 1596: return FAIL;
! 1597: noskip = TRUE;
! 1598: break;
! 1599: }
! 1600: }
! 1601: found:
! 1602: /* skip white space */
! 1603: while (!noskip && ((c = gchar(&pos)) == ' ' || c == '\t'))
! 1604: if (incl(&pos) == -1)
! 1605: break;
! 1606: }
! 1607:
! 1608: setpcmark();
! 1609: curwin->w_cursor = pos;
! 1610: return OK;
! 1611: }
! 1612:
! 1613: /*
! 1614: * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
! 1615: * Paragraphs are currently supposed to be separated by empty lines.
! 1616: * Return TRUE if the next paragraph was found.
! 1617: * If 'what' is '{' or '}' we go to the next section.
! 1618: * If 'both' is TRUE also stop at '}'.
! 1619: */
! 1620: int
! 1621: findpar(dir, count, what, both)
! 1622: register int dir;
! 1623: long count;
! 1624: int what;
! 1625: int both;
! 1626: {
! 1627: register linenr_t curr;
! 1628: int did_skip; /* TRUE after separating lines have
! 1629: been skipped */
! 1630: int first; /* TRUE on first line */
! 1631:
! 1632: curr = curwin->w_cursor.lnum;
! 1633:
! 1634: while (count--)
! 1635: {
! 1636: did_skip = FALSE;
! 1637: for (first = TRUE; ; first = FALSE)
! 1638: {
! 1639: if (*ml_get(curr) != NUL)
! 1640: did_skip = TRUE;
! 1641:
! 1642: if (!first && did_skip && startPS(curr, what, both))
! 1643: break;
! 1644:
! 1645: if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
! 1646: {
! 1647: if (count)
! 1648: return FALSE;
! 1649: curr -= dir;
! 1650: break;
! 1651: }
! 1652: }
! 1653: }
! 1654: setpcmark();
! 1655: if (both && *ml_get(curr) == '}') /* include line with '}' */
! 1656: ++curr;
! 1657: curwin->w_cursor.lnum = curr;
! 1658: if (curr == curbuf->b_ml.ml_line_count)
! 1659: {
! 1660: if ((curwin->w_cursor.col = STRLEN(ml_get(curr))) != 0)
! 1661: {
! 1662: --curwin->w_cursor.col;
! 1663: op_inclusive = TRUE;
! 1664: }
! 1665: }
! 1666: else
! 1667: curwin->w_cursor.col = 0;
! 1668: return TRUE;
! 1669: }
! 1670:
! 1671: /*
! 1672: * check if the string 's' is a nroff macro that is in option 'opt'
! 1673: */
! 1674: static int
! 1675: inmacro(opt, s)
! 1676: char_u *opt;
! 1677: register char_u *s;
! 1678: {
! 1679: register char_u *macro;
! 1680:
! 1681: for (macro = opt; macro[0]; ++macro)
! 1682: {
! 1683: if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ') &&
! 1684: (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
! 1685: break;
! 1686: ++macro;
! 1687: if (macro[0] == NUL)
! 1688: break;
! 1689: }
! 1690: return (macro[0] != NUL);
! 1691: }
! 1692:
! 1693: /*
! 1694: * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
! 1695: * If 'para' is '{' or '}' only check for sections.
! 1696: * If 'both' is TRUE also stop at '}'
! 1697: */
! 1698: int
! 1699: startPS(lnum, para, both)
! 1700: linenr_t lnum;
! 1701: int para;
! 1702: int both;
! 1703: {
! 1704: register char_u *s;
! 1705:
! 1706: s = ml_get(lnum);
! 1707: if (*s == para || *s == '\f' || (both && *s == '}'))
! 1708: return TRUE;
! 1709: if (*s == '.' && (inmacro(p_sections, s + 1) ||
! 1710: (!para && inmacro(p_para, s + 1))))
! 1711: return TRUE;
! 1712: return FALSE;
! 1713: }
! 1714:
! 1715: /*
! 1716: * The following routines do the word searches performed by the 'w', 'W',
! 1717: * 'b', 'B', 'e', and 'E' commands.
! 1718: */
! 1719:
! 1720: /*
! 1721: * To perform these searches, characters are placed into one of three
! 1722: * classes, and transitions between classes determine word boundaries.
! 1723: *
! 1724: * The classes are:
! 1725: *
! 1726: * 0 - white space
! 1727: * 1 - keyword charactes (letters, digits and underscore)
! 1728: * 2 - everything else
! 1729: */
! 1730:
! 1731: static int stype; /* type of the word motion being performed */
! 1732:
! 1733: /*
! 1734: * cls() - returns the class of character at curwin->w_cursor
! 1735: *
! 1736: * The 'type' of the current search modifies the classes of characters if a
! 1737: * 'W', 'B', or 'E' motion is being done. In this case, chars. from class 2
! 1738: * are reported as class 1 since only white space boundaries are of interest.
! 1739: */
! 1740: static int
! 1741: cls()
! 1742: {
! 1743: register int c;
! 1744:
! 1745: c = gchar_cursor();
! 1746: if (c == ' ' || c == '\t' || c == NUL)
! 1747: return 0;
! 1748:
! 1749: if (iswordchar(c))
! 1750: return 1;
! 1751:
! 1752: /*
! 1753: * If stype is non-zero, report these as class 1.
! 1754: */
! 1755: return (stype == 0) ? 2 : 1;
! 1756: }
! 1757:
! 1758:
! 1759: /*
! 1760: * fwd_word(count, type, eol) - move forward one word
! 1761: *
! 1762: * Returns FAIL if the cursor was already at the end of the file.
! 1763: * If eol is TRUE, last word stops at end of line (for operators).
! 1764: */
! 1765: int
! 1766: fwd_word(count, type, eol)
! 1767: long count;
! 1768: int type;
! 1769: int eol;
! 1770: {
! 1771: int sclass; /* starting class */
! 1772: int i;
! 1773: int last_line;
! 1774:
! 1775: stype = type;
! 1776: while (--count >= 0)
! 1777: {
! 1778: sclass = cls();
! 1779:
! 1780: /*
! 1781: * We always move at least one character, unless on the last character
! 1782: * in the buffer.
! 1783: */
! 1784: last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
! 1785: i = inc_cursor();
! 1786: if (i == -1 || (i == 1 && last_line))
! 1787: /* started at last char in file */
! 1788: return FAIL;
! 1789: if (i == 1 && eol && count == 0) /* started at last char in line */
! 1790: return OK;
! 1791:
! 1792: /*
! 1793: * Go one char past end of current word (if any)
! 1794: */
! 1795: if (sclass != 0)
! 1796: while (cls() == sclass)
! 1797: {
! 1798: i = inc_cursor();
! 1799: if (i == -1 || (i == 1 && eol && count == 0))
! 1800: return OK;
! 1801: }
! 1802:
! 1803: /*
! 1804: * go to next non-white
! 1805: */
! 1806: while (cls() == 0)
! 1807: {
! 1808: /*
! 1809: * We'll stop if we land on a blank line
! 1810: */
! 1811: if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
! 1812: break;
! 1813:
! 1814: i = inc_cursor();
! 1815: if (i == -1 || (i == 1 && eol && count == 0))
! 1816: return OK;
! 1817: }
! 1818: }
! 1819: return OK;
! 1820: }
! 1821:
! 1822: /*
! 1823: * bck_word() - move backward 'count' words
! 1824: *
! 1825: * If stop is TRUE and we are already on the start of a word, move one less.
! 1826: *
! 1827: * Returns FAIL if top of the file was reached.
! 1828: */
! 1829: int
! 1830: bck_word(count, type, stop)
! 1831: long count;
! 1832: int type;
! 1833: int stop;
! 1834: {
! 1835: int sclass; /* starting class */
! 1836:
! 1837: stype = type;
! 1838: while (--count >= 0)
! 1839: {
! 1840: sclass = cls();
! 1841: if (dec_cursor() == -1) /* started at start of file */
! 1842: return FAIL;
! 1843:
! 1844: if (!stop || sclass == cls() || sclass == 0)
! 1845: {
! 1846: /*
! 1847: * Skip white space before the word.
! 1848: * Stop on an empty line.
! 1849: */
! 1850: while (cls() == 0)
! 1851: {
! 1852: if (curwin->w_cursor.col == 0 &&
! 1853: lineempty(curwin->w_cursor.lnum))
! 1854: goto finished;
! 1855:
! 1856: if (dec_cursor() == -1) /* hit start of file, stop here */
! 1857: return OK;
! 1858: }
! 1859:
! 1860: /*
! 1861: * Move backward to start of this word.
! 1862: */
! 1863: if (skip_chars(cls(), BACKWARD))
! 1864: return OK;
! 1865: }
! 1866:
! 1867: inc_cursor(); /* overshot - forward one */
! 1868: finished:
! 1869: stop = FALSE;
! 1870: }
! 1871: return OK;
! 1872: }
! 1873:
! 1874: /*
! 1875: * end_word() - move to the end of the word
! 1876: *
! 1877: * There is an apparent bug in the 'e' motion of the real vi. At least on the
! 1878: * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
! 1879: * motion crosses blank lines. When the real vi crosses a blank line in an
! 1880: * 'e' motion, the cursor is placed on the FIRST character of the next
! 1881: * non-blank line. The 'E' command, however, works correctly. Since this
! 1882: * appears to be a bug, I have not duplicated it here.
! 1883: *
! 1884: * Returns FAIL if end of the file was reached.
! 1885: *
! 1886: * If stop is TRUE and we are already on the end of a word, move one less.
! 1887: * If empty is TRUE stop on an empty line.
! 1888: */
! 1889: int
! 1890: end_word(count, type, stop, empty)
! 1891: long count;
! 1892: int type;
! 1893: int stop;
! 1894: int empty;
! 1895: {
! 1896: int sclass; /* starting class */
! 1897:
! 1898: stype = type;
! 1899: while (--count >= 0)
! 1900: {
! 1901: sclass = cls();
! 1902: if (inc_cursor() == -1)
! 1903: return FAIL;
! 1904:
! 1905: /*
! 1906: * If we're in the middle of a word, we just have to move to the end
! 1907: * of it.
! 1908: */
! 1909: if (cls() == sclass && sclass != 0)
! 1910: {
! 1911: /*
! 1912: * Move forward to end of the current word
! 1913: */
! 1914: if (skip_chars(sclass, FORWARD))
! 1915: return FAIL;
! 1916: }
! 1917: else if (!stop || sclass == 0)
! 1918: {
! 1919: /*
! 1920: * We were at the end of a word. Go to the end of the next word.
! 1921: * First skip white space, if 'empty' is TRUE, stop at empty line.
! 1922: */
! 1923: while (cls() == 0)
! 1924: {
! 1925: if (empty && curwin->w_cursor.col == 0 &&
! 1926: lineempty(curwin->w_cursor.lnum))
! 1927: goto finished;
! 1928: if (inc_cursor() == -1) /* hit end of file, stop here */
! 1929: return FAIL;
! 1930: }
! 1931:
! 1932: /*
! 1933: * Move forward to the end of this word.
! 1934: */
! 1935: if (skip_chars(cls(), FORWARD))
! 1936: return FAIL;
! 1937: }
! 1938: dec_cursor(); /* overshot - one char backward */
! 1939: finished:
! 1940: stop = FALSE; /* we move only one word less */
! 1941: }
! 1942: return OK;
! 1943: }
! 1944:
! 1945: /*
! 1946: * bckend_word(count, type) - move back to the end of the word
! 1947: *
! 1948: * If 'eol' is TRUE, stop at end of line.
! 1949: *
! 1950: * Returns FAIL if start of the file was reached.
! 1951: */
! 1952: int
! 1953: bckend_word(count, type, eol)
! 1954: long count;
! 1955: int type;
! 1956: int eol;
! 1957: {
! 1958: int sclass; /* starting class */
! 1959: int i;
! 1960:
! 1961: stype = type;
! 1962: while (--count >= 0)
! 1963: {
! 1964: sclass = cls();
! 1965: if ((i = dec_cursor()) == -1)
! 1966: return FAIL;
! 1967: if (eol && i == 1)
! 1968: return OK;
! 1969:
! 1970: /*
! 1971: * Move backward to before the start of this word.
! 1972: */
! 1973: if (sclass != 0)
! 1974: {
! 1975: while (cls() == sclass)
! 1976: if ((i = dec_cursor()) == -1 || (eol && i == 1))
! 1977: return OK;
! 1978: }
! 1979:
! 1980: /*
! 1981: * Move backward to end of the previous word
! 1982: */
! 1983: while (cls() == 0)
! 1984: {
! 1985: if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
! 1986: break;
! 1987: if ((i = dec_cursor()) == -1 || (eol && i == 1))
! 1988: return OK;
! 1989: }
! 1990: }
! 1991: return OK;
! 1992: }
! 1993:
! 1994: static int
! 1995: skip_chars(cclass, dir)
! 1996: int cclass;
! 1997: int dir;
! 1998: {
! 1999: while (cls() == cclass)
! 2000: if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
! 2001: return TRUE;
! 2002: return FALSE;
! 2003: }
! 2004:
! 2005: /*
! 2006: * Go back to the start of the word or the start of white space
! 2007: */
! 2008: static void
! 2009: back_in_line()
! 2010: {
! 2011: int sclass; /* starting class */
! 2012:
! 2013: sclass = cls();
! 2014: for (;;)
! 2015: {
! 2016: if (curwin->w_cursor.col == 0) /* stop at start of line */
! 2017: break;
! 2018: --curwin->w_cursor.col;
! 2019: if (cls() != sclass) /* stop at start of word */
! 2020: {
! 2021: ++curwin->w_cursor.col;
! 2022: break;
! 2023: }
! 2024: }
! 2025: }
! 2026:
! 2027: /*
! 2028: * Find word under cursor, cursor at end
! 2029: */
! 2030: int
! 2031: current_word(count, type)
! 2032: long count;
! 2033: int type;
! 2034: {
! 2035: FPOS start;
! 2036: FPOS pos;
! 2037: int inclusive = TRUE;
! 2038:
! 2039: stype = type;
! 2040:
! 2041: /*
! 2042: * When visual area is bigger than one character: Extend it.
! 2043: */
! 2044: if (VIsual_active && !equal(curwin->w_cursor, VIsual))
! 2045: {
! 2046: if (lt(curwin->w_cursor, VIsual))
! 2047: {
! 2048: if (decl(&curwin->w_cursor) == -1)
! 2049: return FAIL;
! 2050: if (cls() == 0)
! 2051: {
! 2052: if (bckend_word(count, type, TRUE) == FAIL)
! 2053: return FAIL;
! 2054: (void)incl(&curwin->w_cursor);
! 2055: }
! 2056: else
! 2057: {
! 2058: if (bck_word(count, type, TRUE) == FAIL)
! 2059: return FAIL;
! 2060: }
! 2061: }
! 2062: else
! 2063: {
! 2064: if (incl(&curwin->w_cursor) == -1)
! 2065: return FAIL;
! 2066: if (cls() == 0)
! 2067: {
! 2068: if (fwd_word(count, type, TRUE) == FAIL)
! 2069: return FAIL;
! 2070: (void)oneleft();
! 2071: }
! 2072: else
! 2073: {
! 2074: if (end_word(count, type, TRUE, TRUE) == FAIL)
! 2075: return FAIL;
! 2076: }
! 2077: }
! 2078: return OK;
! 2079: }
! 2080:
! 2081: /*
! 2082: * Go to start of current word or white space.
! 2083: */
! 2084: back_in_line();
! 2085: start = curwin->w_cursor;
! 2086:
! 2087: /*
! 2088: * If the start is on white space, find end of word.
! 2089: * Otherwise find start of next word.
! 2090: */
! 2091: if (cls() == 0)
! 2092: {
! 2093: if (end_word(count, type, TRUE, TRUE) == FAIL)
! 2094: return FAIL;
! 2095: }
! 2096: else
! 2097: {
! 2098: if (fwd_word(count, type, TRUE) == FAIL)
! 2099: return FAIL;
! 2100: /*
! 2101: * If end is just past a new-line, we don't want to include the first
! 2102: * character on the line
! 2103: */
! 2104: if (oneleft() == FAIL) /* put cursor on last char of area */
! 2105: inclusive = FALSE;
! 2106: else
! 2107: {
! 2108: pos = curwin->w_cursor; /* save cursor position */
! 2109: /*
! 2110: * If we don't include white space at the end, move the start to
! 2111: * include some white space there. This makes "d." work better on
! 2112: * the last word in a sentence. Don't delete white space at start
! 2113: * of line (indent).
! 2114: */
! 2115: if (cls() != 0)
! 2116: {
! 2117: curwin->w_cursor = start;
! 2118: if (oneleft() == OK)
! 2119: {
! 2120: back_in_line();
! 2121: if (cls() == 0 && curwin->w_cursor.col > 0)
! 2122: start = curwin->w_cursor;
! 2123: }
! 2124: }
! 2125: curwin->w_cursor = pos; /* put cursor back at end */
! 2126: }
! 2127: }
! 2128: if (VIsual_active)
! 2129: {
! 2130: /* should do something when inclusive == FALSE ! */
! 2131: VIsual = start;
! 2132: VIsual_mode = 'v';
! 2133: update_curbuf(NOT_VALID); /* update the inversion */
! 2134: }
! 2135: else
! 2136: {
! 2137: curbuf->b_op_start = start;
! 2138: op_motion_type = MCHAR;
! 2139: op_inclusive = inclusive;
! 2140: }
! 2141: return OK;
! 2142: }
! 2143:
! 2144: /*
! 2145: * Find sentence under the cursor, cursor at end.
! 2146: */
! 2147: int
! 2148: current_sent(count)
! 2149: long count;
! 2150: {
! 2151: FPOS start;
! 2152: FPOS pos;
! 2153: int start_blank;
! 2154: int c;
! 2155:
! 2156: pos = curwin->w_cursor;
! 2157: start = pos;
! 2158:
! 2159: /*
! 2160: * When visual area is bigger than one character: Extend it.
! 2161: */
! 2162: if (VIsual_active && !equal(curwin->w_cursor, VIsual))
! 2163: {
! 2164: if (lt(pos, VIsual))
! 2165: {
! 2166: /*
! 2167: * Do a "sentence backward" on the next character.
! 2168: * If we end up on the same position, we were already at the start
! 2169: * of a sentence
! 2170: */
! 2171: if (incl(&curwin->w_cursor) == -1)
! 2172: return FAIL;
! 2173: findsent(BACKWARD, 1L);
! 2174: start = curwin->w_cursor;
! 2175: if (count > 1)
! 2176: findsent(BACKWARD, count - 1);
! 2177: /*
! 2178: * When at start of sentence: Include blanks in front of sentence.
! 2179: * Use current_word() to cross line boundaries.
! 2180: * If we don't end up on a blank or on an empty line, go back to
! 2181: * the start of the previous sentence.
! 2182: */
! 2183: if (equal(pos, start))
! 2184: {
! 2185: current_word(1L, 0);
! 2186: c = gchar_cursor();
! 2187: if (c != NUL && !vim_iswhite(c))
! 2188: findsent(BACKWARD, 1L);
! 2189: }
! 2190:
! 2191: }
! 2192: else
! 2193: {
! 2194: /*
! 2195: * When one char before start of sentence: Don't include blanks in
! 2196: * front of next sentence.
! 2197: * Else go count sentences forward.
! 2198: */
! 2199: findsent(FORWARD, 1L);
! 2200: incl(&pos);
! 2201: if (equal(pos, curwin->w_cursor))
! 2202: {
! 2203: findsent(FORWARD, count);
! 2204: find_first_blank(&curwin->w_cursor);
! 2205: }
! 2206: else if (count > 1)
! 2207: findsent(FORWARD, count - 1);
! 2208: decl(&curwin->w_cursor);
! 2209: }
! 2210: return OK;
! 2211: }
! 2212:
! 2213: /*
! 2214: * Find start of next sentence.
! 2215: */
! 2216: findsent(FORWARD, 1L);
! 2217:
! 2218: /*
! 2219: * If cursor started on blank, check if it is just before the start of the
! 2220: * next sentence.
! 2221: */
! 2222: while (vim_iswhite(gchar(&pos)))
! 2223: incl(&pos);
! 2224: if (equal(pos, curwin->w_cursor))
! 2225: {
! 2226: start_blank = TRUE;
! 2227: /*
! 2228: * go back to first blank
! 2229: */
! 2230: while (decl(&start) != -1)
! 2231: {
! 2232: if (!vim_iswhite(gchar(&start)))
! 2233: {
! 2234: incl(&start);
! 2235: break;
! 2236: }
! 2237: }
! 2238: }
! 2239: else
! 2240: {
! 2241: start_blank = FALSE;
! 2242: findsent(BACKWARD, 1L);
! 2243: start = curwin->w_cursor;
! 2244: }
! 2245: findsent(FORWARD, count);
! 2246:
! 2247: /*
! 2248: * If the blank in front of the sentence is included, exclude the blanks
! 2249: * at the end of the sentence, go back to the first blank.
! 2250: */
! 2251: if (start_blank)
! 2252: find_first_blank(&curwin->w_cursor);
! 2253: else
! 2254: {
! 2255: /*
! 2256: * If there are no trailing blanks, try to include leading blanks
! 2257: */
! 2258: pos = curwin->w_cursor;
! 2259: decl(&pos);
! 2260: if (!vim_iswhite(gchar(&pos)))
! 2261: find_first_blank(&start);
! 2262: }
! 2263:
! 2264: if (VIsual_active)
! 2265: {
! 2266: VIsual = start;
! 2267: VIsual_mode = 'v';
! 2268: decl(&curwin->w_cursor); /* don't include the cursor char */
! 2269: update_curbuf(NOT_VALID); /* update the inversion */
! 2270: }
! 2271: else
! 2272: {
! 2273: curbuf->b_op_start = start;
! 2274: op_motion_type = MCHAR;
! 2275: op_inclusive = FALSE;
! 2276: }
! 2277: return OK;
! 2278: }
! 2279:
! 2280: int
! 2281: current_block(what, count)
! 2282: int what; /* '(' or '{' */
! 2283: long count;
! 2284: {
! 2285: FPOS old_pos;
! 2286: FPOS *pos = NULL;
! 2287: FPOS start_pos;
! 2288: FPOS *end_pos;
! 2289: FPOS old_start, old_end;
! 2290: int inclusive = FALSE;
! 2291: int other;
! 2292:
! 2293: old_pos = curwin->w_cursor;
! 2294: if (what == '{')
! 2295: other = '}';
! 2296: else
! 2297: other = ')';
! 2298:
! 2299: old_end = curwin->w_cursor; /* remember where we started */
! 2300: old_start = old_end;
! 2301:
! 2302: /*
! 2303: * If we start on '(', '{', ')' or '}', use the whole block inclusive.
! 2304: */
! 2305: if (!VIsual_active || (VIsual.lnum == curwin->w_cursor.lnum &&
! 2306: VIsual.col == curwin->w_cursor.col))
! 2307: {
! 2308: if (what == '{') /* ignore indent */
! 2309: while (inindent(1))
! 2310: if (inc_cursor() != 0)
! 2311: break;
! 2312: if (gchar_cursor() == what) /* cursor on '(' or '{' */
! 2313: {
! 2314: ++curwin->w_cursor.col;
! 2315: inclusive = TRUE;
! 2316: }
! 2317: else if (gchar_cursor() == other) /* cursor on ')' or '}' */
! 2318: inclusive = TRUE;
! 2319: }
! 2320: else if (lt(VIsual, curwin->w_cursor))
! 2321: {
! 2322: old_start = VIsual;
! 2323: curwin->w_cursor = VIsual; /* cursor at low end of Visual */
! 2324: }
! 2325: else
! 2326: old_end = VIsual;
! 2327:
! 2328: /*
! 2329: * Search backwards for unclosed '(' or '{'.
! 2330: * put this position in start_pos.
! 2331: */
! 2332: while (count--)
! 2333: {
! 2334: if ((pos = findmatch(what)) == NULL)
! 2335: break;
! 2336: curwin->w_cursor = *pos;
! 2337: start_pos = *pos; /* the findmatch for end_pos will overwrite *pos */
! 2338: }
! 2339:
! 2340: /*
! 2341: * Search for matching ')' or '}'.
! 2342: * Put this position in curwin->w_cursor.
! 2343: */
! 2344: if (pos == NULL || (end_pos = findmatch(other)) == NULL)
! 2345: {
! 2346: curwin->w_cursor = old_pos;
! 2347: return FAIL;
! 2348: }
! 2349: curwin->w_cursor = *end_pos;
! 2350:
! 2351: /*
! 2352: * Try to exclude the '(', '{', ')' and '}'.
! 2353: * If the ending '}' is only preceded by indent, skip that indent.
! 2354: * But only if the resulting area is not smaller than what we started with.
! 2355: */
! 2356: if (!inclusive)
! 2357: {
! 2358: incl(&start_pos);
! 2359: old_pos = curwin->w_cursor;
! 2360: decl(&curwin->w_cursor);
! 2361: if (what == '{')
! 2362: while (inindent(0))
! 2363: if (decl(&curwin->w_cursor) != 0)
! 2364: break;
! 2365: if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor))
! 2366: {
! 2367: decl(&start_pos);
! 2368: curwin->w_cursor = old_pos;
! 2369: }
! 2370: }
! 2371:
! 2372: if (VIsual_active)
! 2373: {
! 2374: VIsual = start_pos;
! 2375: VIsual_mode = 'v';
! 2376: update_curbuf(NOT_VALID); /* update the inversion */
! 2377: showmode();
! 2378: }
! 2379: else
! 2380: {
! 2381: curbuf->b_op_start = start_pos;
! 2382: op_motion_type = MCHAR;
! 2383: op_inclusive = TRUE;
! 2384: }
! 2385:
! 2386: return OK;
! 2387: }
! 2388:
! 2389: int
! 2390: current_par(type, count)
! 2391: int type; /* 'p' for paragraph, 'S' for section */
! 2392: long count;
! 2393: {
! 2394: linenr_t start;
! 2395: linenr_t end;
! 2396: int white_in_front;
! 2397: int dir;
! 2398: int start_is_white;
! 2399: int retval = OK;
! 2400:
! 2401: if (type == 'S') /* not implemented yet */
! 2402: return FAIL;
! 2403:
! 2404: start = curwin->w_cursor.lnum;
! 2405:
! 2406: /*
! 2407: * When visual area is more than one line: extend it.
! 2408: */
! 2409: if (VIsual_active && start != VIsual.lnum)
! 2410: {
! 2411: if (start < VIsual.lnum)
! 2412: dir = BACKWARD;
! 2413: else
! 2414: dir = FORWARD;
! 2415: while (count--)
! 2416: {
! 2417: if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
! 2418: retval = FAIL;
! 2419:
! 2420: start_is_white = -1;
! 2421: for (;;)
! 2422: {
! 2423: if (start == (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
! 2424: break;
! 2425: if (start_is_white >= 0 &&
! 2426: (start_is_white != linewhite(start + dir) ||
! 2427: startPS(start + (dir > 0 ? 1 : 0), 0, 0)))
! 2428: break;
! 2429: start += dir;
! 2430: if (start_is_white < 0)
! 2431: start_is_white = linewhite(start);
! 2432: }
! 2433: }
! 2434: curwin->w_cursor.lnum = start;
! 2435: curwin->w_cursor.col = 0;
! 2436: return retval;
! 2437: }
! 2438:
! 2439: /*
! 2440: * First move back to the start of the paragraph or white lines
! 2441: */
! 2442: white_in_front = linewhite(start);
! 2443: while (start > 1)
! 2444: {
! 2445: if (white_in_front) /* stop at first white line */
! 2446: {
! 2447: if (!linewhite(start - 1))
! 2448: break;
! 2449: }
! 2450: else /* stop at first non-white line of start of paragraph */
! 2451: {
! 2452: if (linewhite(start - 1) || startPS(start, 0, 0))
! 2453: break;
! 2454: }
! 2455: --start;
! 2456: }
! 2457:
! 2458: /*
! 2459: * Move past the end of the white lines.
! 2460: */
! 2461: end = start;
! 2462: while (linewhite(end) && end < curbuf->b_ml.ml_line_count)
! 2463: ++end;
! 2464:
! 2465: --end;
! 2466: while (count--)
! 2467: {
! 2468: if (end == curbuf->b_ml.ml_line_count)
! 2469: return FAIL;
! 2470:
! 2471: ++end;
! 2472: /*
! 2473: * skip to end of paragraph
! 2474: */
! 2475: while (end < curbuf->b_ml.ml_line_count &&
! 2476: !linewhite(end + 1) && !startPS(end + 1, 0, 0))
! 2477: ++end;
! 2478:
! 2479: if (count == 0 && white_in_front)
! 2480: break;
! 2481:
! 2482: /*
! 2483: * skip to end of white lines after paragraph
! 2484: */
! 2485: while (end < curbuf->b_ml.ml_line_count && linewhite(end + 1))
! 2486: ++end;
! 2487: }
! 2488:
! 2489: /*
! 2490: * If there are no empty lines at the end, try to find some empty lines at
! 2491: * the start (unless that has been done already).
! 2492: */
! 2493: if (!white_in_front && !linewhite(end))
! 2494: while (start > 1 && linewhite(start - 1))
! 2495: --start;
! 2496:
! 2497: if (VIsual_active)
! 2498: {
! 2499: VIsual.lnum = start;
! 2500: VIsual_mode = 'V';
! 2501: update_curbuf(NOT_VALID); /* update the inversion */
! 2502: showmode();
! 2503: }
! 2504: else
! 2505: {
! 2506: curbuf->b_op_start.lnum = start;
! 2507: op_motion_type = MLINE;
! 2508: }
! 2509: curwin->w_cursor.lnum = end;
! 2510: curwin->w_cursor.col = 0;
! 2511:
! 2512: return OK;
! 2513: }
! 2514:
! 2515: /*
! 2516: * linewhite -- return TRUE if line 'lnum' is empty or has white chars only.
! 2517: */
! 2518: int
! 2519: linewhite(lnum)
! 2520: linenr_t lnum;
! 2521: {
! 2522: char_u *p;
! 2523:
! 2524: p = skipwhite(ml_get(lnum));
! 2525: return (*p == NUL);
! 2526: }
! 2527:
! 2528: static void
! 2529: find_first_blank(posp)
! 2530: FPOS *posp;
! 2531: {
! 2532: while (decl(posp) != -1)
! 2533: {
! 2534: if (!vim_iswhite(gchar(posp)))
! 2535: {
! 2536: incl(posp);
! 2537: break;
! 2538: }
! 2539: }
! 2540: }
! 2541:
! 2542: void
! 2543: find_pattern_in_path(ptr, len, whole, skip_comments,
! 2544: type, count, action, start_lnum, end_lnum)
! 2545: char_u *ptr; /* pointer to search pattern */
! 2546: int len; /* length of search pattern */
! 2547: int whole; /* match whole words only */
! 2548: int skip_comments; /* don't match inside comments */
! 2549: int type; /* Type of search; are we looking for a type? a
! 2550: macro? */
! 2551: long count;
! 2552: int action; /* What to do when we find it */
! 2553: linenr_t start_lnum; /* first line to start searching */
! 2554: linenr_t end_lnum; /* last line for searching */
! 2555: {
! 2556: SearchedFile *files; /* Stack of included files */
! 2557: SearchedFile *bigger; /* When we need more space */
! 2558: int max_path_depth = 50;
! 2559: long match_count = 1;
! 2560:
! 2561: char_u *pat;
! 2562: char_u *new_fname;
! 2563: char_u *curr_fname = curbuf->b_xfilename;
! 2564: char_u *prev_fname = NULL;
! 2565: linenr_t lnum;
! 2566: int depth;
! 2567: int depth_displayed; /* For type==CHECK_PATH */
! 2568: int old_files;
! 2569: int already_searched;
! 2570: char_u *file_line;
! 2571: char_u *line;
! 2572: char_u *p;
! 2573: char_u *p2 = NUL; /* Init for gcc */
! 2574: char_u save_char = NUL;
! 2575: int define_matched;
! 2576: struct regexp *prog = NULL;
! 2577: struct regexp *include_prog = NULL;
! 2578: struct regexp *define_prog = NULL;
! 2579: int matched = FALSE;
! 2580: int did_show = FALSE;
! 2581: int found = FALSE;
! 2582: int i;
! 2583:
! 2584: file_line = alloc(LSIZE);
! 2585: if (file_line == NULL)
! 2586: return;
! 2587:
! 2588: reg_magic = p_magic;
! 2589: if (type != CHECK_PATH)
! 2590: {
! 2591: pat = alloc(len + 5);
! 2592: if (pat == NULL)
! 2593: goto fpip_end;
! 2594: sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
! 2595: set_reg_ic(pat); /* set reg_ic according to p_ic, p_scs and pat */
! 2596: prog = vim_regcomp(pat);
! 2597: vim_free(pat);
! 2598: if (prog == NULL)
! 2599: goto fpip_end;
! 2600: }
! 2601: reg_ic = FALSE; /* don't ignore case in include and define patterns */
! 2602: if (*p_inc != NUL)
! 2603: {
! 2604: include_prog = vim_regcomp(p_inc);
! 2605: if (include_prog == NULL)
! 2606: goto fpip_end;
! 2607: }
! 2608: if (type == FIND_DEFINE && *p_def != NUL)
! 2609: {
! 2610: define_prog = vim_regcomp(p_def);
! 2611: if (define_prog == NULL)
! 2612: goto fpip_end;
! 2613: }
! 2614: files = (SearchedFile *)lalloc(max_path_depth * sizeof(SearchedFile), TRUE);
! 2615: if (files == NULL)
! 2616: goto fpip_end;
! 2617: for (i = 0; i < max_path_depth; i++)
! 2618: {
! 2619: files[i].fp = NULL;
! 2620: files[i].name = NULL;
! 2621: files[i].lnum = 0;
! 2622: files[i].matched = FALSE;
! 2623: }
! 2624: old_files = max_path_depth;
! 2625: depth = depth_displayed = -1;
! 2626:
! 2627: lnum = start_lnum;
! 2628: if (end_lnum > curbuf->b_ml.ml_line_count)
! 2629: end_lnum = curbuf->b_ml.ml_line_count;
! 2630: if (lnum > end_lnum) /* do at least one line */
! 2631: lnum = end_lnum;
! 2632: line = ml_get(lnum);
! 2633:
! 2634: for (;;)
! 2635: {
! 2636: if (include_prog != NULL && vim_regexec(include_prog, line, TRUE))
! 2637: {
! 2638: new_fname = get_file_name_in_path(include_prog->endp[0] + 1,
! 2639: 0, FNAME_EXP);
! 2640: already_searched = FALSE;
! 2641: if (new_fname != NULL)
! 2642: {
! 2643: /* Check whether we have already searched in this file */
! 2644: for (i = 0;; i++)
! 2645: {
! 2646: if (i == depth + 1)
! 2647: i = old_files;
! 2648: if (i == max_path_depth)
! 2649: break;
! 2650: if (STRCMP(new_fname, files[i].name) == 0)
! 2651: {
! 2652: if (type != CHECK_PATH &&
! 2653: action == ACTION_SHOW_ALL && files[i].matched)
! 2654: {
! 2655: msg_outchar('\n'); /* cursor below last one */
! 2656: if (!got_int) /* don't display if 'q'
! 2657: typed at "--more--"
! 2658: mesage */
! 2659: {
! 2660: set_highlight('d'); /* Same as for dirs */
! 2661: start_highlight();
! 2662: msg_home_replace(new_fname);
! 2663: stop_highlight();
! 2664: MSG_OUTSTR(" (includes previously listed match)");
! 2665: prev_fname = NULL;
! 2666: }
! 2667: }
! 2668: vim_free(new_fname);
! 2669: new_fname = NULL;
! 2670: already_searched = TRUE;
! 2671: break;
! 2672: }
! 2673: }
! 2674: }
! 2675:
! 2676: if (type == CHECK_PATH && (action == ACTION_SHOW_ALL ||
! 2677: (new_fname == NULL && !already_searched)))
! 2678: {
! 2679: if (did_show)
! 2680: msg_outchar('\n'); /* cursor below last one */
! 2681: else
! 2682: {
! 2683: gotocmdline(TRUE); /* cursor at status line */
! 2684: set_highlight('t'); /* Highlight title */
! 2685: start_highlight();
! 2686: MSG_OUTSTR("--- Included files ");
! 2687: if (action != ACTION_SHOW_ALL)
! 2688: MSG_OUTSTR("not found ");
! 2689: MSG_OUTSTR("in path ---\n");
! 2690: stop_highlight();
! 2691: }
! 2692: did_show = TRUE;
! 2693: while (depth_displayed < depth && !got_int)
! 2694: {
! 2695: ++depth_displayed;
! 2696: for (i = 0; i < depth_displayed; i++)
! 2697: MSG_OUTSTR(" ");
! 2698: msg_home_replace(files[depth_displayed].name);
! 2699: MSG_OUTSTR(" -->\n");
! 2700: }
! 2701: if (!got_int) /* don't display if 'q' typed
! 2702: for "--more--" message */
! 2703: {
! 2704: for (i = 0; i <= depth_displayed; i++)
! 2705: MSG_OUTSTR(" ");
! 2706: set_highlight('d'); /* Same as for directories */
! 2707: start_highlight();
! 2708: /*
! 2709: * Isolate the file name.
! 2710: * Include the surrounding "" or <> if present.
! 2711: */
! 2712: for (p = include_prog->endp[0] + 1; !isfilechar(*p); p++)
! 2713: ;
! 2714: for (i = 0; isfilechar(p[i]); i++)
! 2715: ;
! 2716: if (p[-1] == '"' || p[-1] == '<')
! 2717: {
! 2718: --p;
! 2719: ++i;
! 2720: }
! 2721: if (p[i] == '"' || p[i] == '>')
! 2722: ++i;
! 2723: save_char = p[i];
! 2724: p[i] = NUL;
! 2725: msg_outstr(p);
! 2726: p[i] = save_char;
! 2727: stop_highlight();
! 2728: if (new_fname == NULL && action == ACTION_SHOW_ALL)
! 2729: {
! 2730: if (already_searched)
! 2731: MSG_OUTSTR(" (Already listed)");
! 2732: else
! 2733: MSG_OUTSTR(" NOT FOUND");
! 2734: }
! 2735: }
! 2736: flushbuf(); /* output each line directly */
! 2737: }
! 2738:
! 2739: if (new_fname != NULL)
! 2740: {
! 2741: /* Push the new file onto the file stack */
! 2742: if (depth + 1 == old_files)
! 2743: {
! 2744: bigger = (SearchedFile *)lalloc(max_path_depth * 2
! 2745: * sizeof(SearchedFile), TRUE);
! 2746: if (bigger != NULL)
! 2747: {
! 2748: for (i = 0; i <= depth; i++)
! 2749: bigger[i] = files[i];
! 2750: for (i = depth + 1; i < old_files + max_path_depth; i++)
! 2751: {
! 2752: bigger[i].fp = NULL;
! 2753: bigger[i].name = NULL;
! 2754: bigger[i].lnum = 0;
! 2755: bigger[i].matched = FALSE;
! 2756: }
! 2757: for (i = old_files; i < max_path_depth; i++)
! 2758: bigger[i + max_path_depth] = files[i];
! 2759: old_files += max_path_depth;
! 2760: max_path_depth *= 2;
! 2761: vim_free(files);
! 2762: files = bigger;
! 2763: }
! 2764: }
! 2765: if ((files[depth + 1].fp = fopen((char *)new_fname, "r"))
! 2766: == NULL)
! 2767: vim_free(new_fname);
! 2768: else
! 2769: {
! 2770: if (++depth == old_files)
! 2771: {
! 2772: /*
! 2773: * lalloc() for 'bigger' must have failed above. We
! 2774: * will forget one of our already visited files now.
! 2775: */
! 2776: vim_free(files[old_files].name);
! 2777: ++old_files;
! 2778: }
! 2779: files[depth].name = curr_fname = new_fname;
! 2780: files[depth].lnum = 0;
! 2781: files[depth].matched = FALSE;
! 2782: }
! 2783: }
! 2784: }
! 2785: else
! 2786: {
! 2787: /*
! 2788: * Check if the line is a define (type == FIND_DEFINE)
! 2789: */
! 2790: p = line;
! 2791: define_matched = FALSE;
! 2792: if (define_prog != NULL && vim_regexec(define_prog, line, TRUE))
! 2793: {
! 2794: /*
! 2795: * Pattern must be first identifier after 'define', so skip
! 2796: * to that position before checking for match of pattern. Also
! 2797: * don't let it match beyond the end of this identifier.
! 2798: */
! 2799: p = define_prog->endp[0] + 1;
! 2800: while (*p && !isidchar(*p))
! 2801: p++;
! 2802: p2 = p;
! 2803: while (*p2 && isidchar(*p2))
! 2804: p2++;
! 2805: save_char = *p2;
! 2806: *p2 = NUL;
! 2807: define_matched = TRUE;
! 2808: }
! 2809:
! 2810: /*
! 2811: * Look for a match. Don't do this if we are looking for a
! 2812: * define and this line didn't match define_prog above.
! 2813: */
! 2814: if ((define_prog == NULL || define_matched) &&
! 2815: prog != NULL && vim_regexec(prog, p, p == line))
! 2816: {
! 2817: matched = TRUE;
! 2818: /*
! 2819: * Check if the line is not a comment line (unless we are
! 2820: * looking for a define). A line starting with "# define" is
! 2821: * not considered to be a comment line.
! 2822: */
! 2823: if (!define_matched && skip_comments)
! 2824: {
! 2825: fo_do_comments = TRUE;
! 2826: if ((*line != '#' ||
! 2827: STRNCMP(skipwhite(line + 1), "define", 6) != 0) &&
! 2828: get_leader_len(line, NULL))
! 2829: matched = FALSE;
! 2830:
! 2831: /*
! 2832: * Also check for a "/ *" or "/ /" before the match.
! 2833: * Skips lines like "int idx; / * normal index * /" when
! 2834: * looking for "normal".
! 2835: */
! 2836: else
! 2837: for (p = line; *p && p < prog->startp[0]; ++p)
! 2838: if (p[0] == '/' && (p[1] == '*' || p[1] == '/'))
! 2839: {
! 2840: matched = FALSE;
! 2841: break;
! 2842: }
! 2843: fo_do_comments = FALSE;
! 2844: }
! 2845: }
! 2846: if (define_matched)
! 2847: *p2 = save_char;
! 2848: }
! 2849: if (matched)
! 2850: {
! 2851: #ifdef INSERT_EXPAND
! 2852: if (action == ACTION_EXPAND)
! 2853: {
! 2854: if (depth == -1 && lnum == curwin->w_cursor.lnum)
! 2855: break;
! 2856: found = TRUE;
! 2857: p = prog->startp[0];
! 2858: while (iswordchar(*p))
! 2859: ++p;
! 2860: if (add_completion_and_infercase(prog->startp[0],
! 2861: (int)(p - prog->startp[0]),
! 2862: curr_fname == curbuf->b_xfilename ? NULL : curr_fname,
! 2863: FORWARD) == RET_ERROR)
! 2864: break;
! 2865: }
! 2866: else
! 2867: #endif
! 2868: if (action == ACTION_SHOW_ALL)
! 2869: {
! 2870: found = TRUE;
! 2871: if (!did_show)
! 2872: gotocmdline(TRUE); /* cursor at status line */
! 2873: if (curr_fname != prev_fname)
! 2874: {
! 2875: if (did_show)
! 2876: msg_outchar('\n'); /* cursor below last one */
! 2877: if (!got_int) /* don't display if 'q' typed
! 2878: at "--more--" mesage */
! 2879: {
! 2880: set_highlight('d'); /* Same as for directories */
! 2881: start_highlight();
! 2882: msg_home_replace(curr_fname);
! 2883: stop_highlight();
! 2884: }
! 2885: prev_fname = curr_fname;
! 2886: }
! 2887: did_show = TRUE;
! 2888: if (!got_int)
! 2889: show_pat_in_path(line, type, TRUE, action,
! 2890: (depth == -1) ? NULL : files[depth].fp,
! 2891: (depth == -1) ? &lnum : &files[depth].lnum,
! 2892: match_count++);
! 2893:
! 2894: /* Set matched flag for this file and all the ones that
! 2895: * include it */
! 2896: for (i = 0; i <= depth; ++i)
! 2897: files[i].matched = TRUE;
! 2898: }
! 2899: else if (--count <= 0)
! 2900: {
! 2901: found = TRUE;
! 2902: if (depth == -1 && lnum == curwin->w_cursor.lnum)
! 2903: EMSG("Match is on current line");
! 2904: else if (action == ACTION_SHOW)
! 2905: {
! 2906: show_pat_in_path(line, type, did_show, action,
! 2907: (depth == -1) ? NULL : files[depth].fp,
! 2908: (depth == -1) ? &lnum : &files[depth].lnum, 1L);
! 2909: did_show = TRUE;
! 2910: }
! 2911: else
! 2912: {
! 2913: if (action == ACTION_SPLIT)
! 2914: {
! 2915: if (win_split(0, FALSE) == FAIL)
! 2916: break;
! 2917: }
! 2918: if (depth == -1)
! 2919: {
! 2920: setpcmark();
! 2921: curwin->w_cursor.lnum = lnum;
! 2922: }
! 2923: else
! 2924: if (getfile(0, files[depth].name, NULL, TRUE,
! 2925: files[depth].lnum) > 0)
! 2926: break; /* failed to jump to file */
! 2927: }
! 2928: if (action != ACTION_SHOW)
! 2929: {
! 2930: curwin->w_cursor.col = prog->startp[0] - line;
! 2931: curwin->w_set_curswant = TRUE;
! 2932: }
! 2933: break;
! 2934: }
! 2935: matched = FALSE;
! 2936: }
! 2937: line_breakcheck();
! 2938: if (got_int)
! 2939: break;
! 2940: while (depth >= 0)
! 2941: {
! 2942: if (!vim_fgets(line = file_line, LSIZE, files[depth].fp))
! 2943: {
! 2944: ++files[depth].lnum;
! 2945: break;
! 2946: }
! 2947: fclose(files[depth].fp);
! 2948: --old_files;
! 2949: files[old_files].name = files[depth].name;
! 2950: files[old_files].matched = files[depth].matched;
! 2951: --depth;
! 2952: curr_fname = (depth == -1) ? curbuf->b_xfilename
! 2953: : files[depth].name;
! 2954: if (depth < depth_displayed)
! 2955: depth_displayed = depth;
! 2956: }
! 2957: if (depth < 0)
! 2958: {
! 2959: if (++lnum > end_lnum)
! 2960: break;
! 2961: line = ml_get(lnum);
! 2962: }
! 2963: }
! 2964: for (i = 0; i <= depth; i++)
! 2965: {
! 2966: fclose(files[i].fp);
! 2967: vim_free(files[i].name);
! 2968: }
! 2969: for (i = old_files; i < max_path_depth; i++)
! 2970: vim_free(files[i].name);
! 2971: vim_free(files);
! 2972:
! 2973: if (type == CHECK_PATH)
! 2974: {
! 2975: if (!did_show)
! 2976: {
! 2977: if (action != ACTION_SHOW_ALL)
! 2978: MSG("All included files were found");
! 2979: else
! 2980: MSG("No included files");
! 2981: }
! 2982: }
! 2983: else if (!found
! 2984: #ifdef INSERT_EXPAND
! 2985: && action != ACTION_EXPAND
! 2986: #endif
! 2987: )
! 2988: {
! 2989: if (got_int)
! 2990: emsg(e_interr);
! 2991: else if (type == FIND_DEFINE)
! 2992: EMSG("Couldn't find definition");
! 2993: else
! 2994: EMSG("Couldn't find pattern");
! 2995: }
! 2996: if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
! 2997: msg_end();
! 2998:
! 2999: fpip_end:
! 3000: vim_free(file_line);
! 3001: vim_free(prog);
! 3002: vim_free(include_prog);
! 3003: vim_free(define_prog);
! 3004: }
! 3005:
! 3006: static void
! 3007: show_pat_in_path(line, type, did_show, action, fp, lnum, count)
! 3008: char_u *line;
! 3009: int type;
! 3010: int did_show;
! 3011: int action;
! 3012: FILE *fp;
! 3013: linenr_t *lnum;
! 3014: long count;
! 3015: {
! 3016: char_u *p;
! 3017:
! 3018: if (did_show)
! 3019: msg_outchar('\n'); /* cursor below last one */
! 3020: else
! 3021: gotocmdline(TRUE); /* cursor at status line */
! 3022: if (got_int) /* 'q' typed at "--more--" message */
! 3023: return;
! 3024: for (;;)
! 3025: {
! 3026: p = line + STRLEN(line) - 1;
! 3027: if (fp != NULL)
! 3028: {
! 3029: /* We used fgets(), so get rid of newline at end */
! 3030: if (p >= line && *p == '\n')
! 3031: --p;
! 3032: if (p >= line && *p == '\r')
! 3033: --p;
! 3034: *(p + 1) = NUL;
! 3035: }
! 3036: if (action == ACTION_SHOW_ALL)
! 3037: {
! 3038: sprintf((char *)IObuff, "%3ld: ", count); /* show match nr */
! 3039: msg_outstr(IObuff);
! 3040: set_highlight('n'); /* Highlight line numbers */
! 3041: start_highlight();
! 3042: sprintf((char *)IObuff, "%4ld", *lnum); /* show line nr */
! 3043: msg_outstr(IObuff);
! 3044: stop_highlight();
! 3045: MSG_OUTSTR(" ");
! 3046: }
! 3047: msg_prt_line(line);
! 3048: flushbuf(); /* show one line at a time */
! 3049:
! 3050: /* Definition continues until line that doesn't end with '\' */
! 3051: if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
! 3052: break;
! 3053:
! 3054: if (fp != NULL)
! 3055: {
! 3056: if (vim_fgets(line, LSIZE, fp)) /* end of file */
! 3057: break;
! 3058: ++*lnum;
! 3059: }
! 3060: else
! 3061: {
! 3062: if (++*lnum > curbuf->b_ml.ml_line_count)
! 3063: break;
! 3064: line = ml_get(*lnum);
! 3065: }
! 3066: msg_outchar('\n');
! 3067: }
! 3068: }
! 3069:
! 3070: #ifdef VIMINFO
! 3071: int
! 3072: read_viminfo_search_pattern(line, fp, force)
! 3073: char_u *line;
! 3074: FILE *fp;
! 3075: int force;
! 3076: {
! 3077: char_u *lp;
! 3078: char_u **pattern;
! 3079:
! 3080: lp = line;
! 3081: if (lp[0] == '~')
! 3082: lp++;
! 3083: if (lp[0] == '/')
! 3084: pattern = &search_pattern;
! 3085: else
! 3086: pattern = &subst_pattern;
! 3087: if (*pattern != NULL && force)
! 3088: vim_free(*pattern);
! 3089: if (force || *pattern == NULL)
! 3090: {
! 3091: viminfo_readstring(lp);
! 3092: *pattern = strsave(lp + 1);
! 3093: if (line[0] == '~')
! 3094: last_pattern = *pattern;
! 3095: }
! 3096: return vim_fgets(line, LSIZE, fp);
! 3097: }
! 3098:
! 3099: void
! 3100: write_viminfo_search_pattern(fp)
! 3101: FILE *fp;
! 3102: {
! 3103: if (get_viminfo_parameter('/') != 0)
! 3104: {
! 3105: if (search_pattern != NULL)
! 3106: {
! 3107: fprintf(fp, "\n# Last Search Pattern:\n");
! 3108: fprintf(fp, "%s/", (last_pattern == search_pattern) ? "~" : "");
! 3109: viminfo_writestring(fp, search_pattern);
! 3110: }
! 3111: if (subst_pattern != NULL)
! 3112: {
! 3113: fprintf(fp, "\n# Last Substitute Search Pattern:\n");
! 3114: fprintf(fp, "%s&", (last_pattern == subst_pattern) ? "~" : "");
! 3115: viminfo_writestring(fp, subst_pattern);
! 3116: }
! 3117: }
! 3118: }
! 3119: #endif /* VIMINFO */
! 3120:
! 3121: /*
! 3122: * Give a warning message.
! 3123: * Use 'w' highlighting and may repeat the message after redrawing
! 3124: */
! 3125: static void
! 3126: give_warning(message)
! 3127: char_u *message;
! 3128: {
! 3129: (void)set_highlight('w');
! 3130: msg_highlight = TRUE;
! 3131: if (msg(message) && !msg_scroll)
! 3132: {
! 3133: keep_msg = message;
! 3134: keep_msg_highlight = 'w';
! 3135: }
! 3136: msg_didout = FALSE; /* overwrite this message */
! 3137: msg_col = 0;
! 3138: }