|
|
1.1 ! root 1: /* String search routines for GNU Emacs. ! 2: Copyright (C) 1985 Richard M. Stallman. ! 3: ! 4: This file is part of GNU Emacs. ! 5: ! 6: GNU Emacs is distributed in the hope that it will be useful, ! 7: but WITHOUT ANY WARRANTY. No author or distributor ! 8: accepts responsibility to anyone for the consequences of using it ! 9: or for whether it serves any particular purpose or works at all, ! 10: unless he says so in writing. Refer to the GNU Emacs General Public ! 11: License for full details. ! 12: ! 13: Everyone is granted permission to copy, modify and redistribute ! 14: GNU Emacs, but only under the conditions described in the ! 15: GNU Emacs General Public License. A copy of this license is ! 16: supposed to have been given to you along with GNU Emacs so you ! 17: can know your rights and responsibilities. It should be in a ! 18: file named COPYING. Among other things, the copyright notice ! 19: and this notice must be preserved on all copies. */ ! 20: ! 21: ! 22: #include <ctype.h> ! 23: #include "config.h" ! 24: #include "lisp.h" ! 25: #include "syntax.h" ! 26: #include "buffer.h" ! 27: #include "commands.h" ! 28: #include "regex.h" ! 29: ! 30: unsigned char downcase_table[0400] = {0}; /* folds upper to lower case */ ! 31: ! 32: /* We compile regexps into this buffer and then use it for searching. */ ! 33: ! 34: static struct re_pattern_buffer searchbuf; ! 35: ! 36: char search_fastmap[0400]; ! 37: ! 38: /* Last regexp we compiled */ ! 39: ! 40: Lisp_Object last_regexp; ! 41: ! 42: /* Every call to re_match, etc., must pass &search_regs as the regs argument ! 43: unless you can show it is unnecessary (i.e., if re_match is certainly going ! 44: to be called again before region-around-match can be called). */ ! 45: ! 46: static struct re_registers search_regs; ! 47: ! 48: /* error condition signalled when regexp compile_pattern fails */ ! 49: ! 50: Lisp_Object Qinvalid_regexp; ! 51: ! 52: /* Compile a regexp and signal a Lisp error if anything goes wrong. */ ! 53: ! 54: compile_pattern (pattern, bufp, translate) ! 55: Lisp_Object pattern; ! 56: struct re_pattern_buffer *bufp; ! 57: char *translate; ! 58: { ! 59: char *val; ! 60: Lisp_Object dummy; ! 61: ! 62: if (EQ (pattern, last_regexp) ! 63: && translate == bufp->translate) ! 64: return; ! 65: last_regexp = Qnil; ! 66: bufp->translate = translate; ! 67: val = re_compile_pattern (XSTRING (pattern)->data, ! 68: XSTRING (pattern)->size, ! 69: bufp); ! 70: if (val) ! 71: { ! 72: dummy = build_string (val); ! 73: while (1) ! 74: Fsignal (Qinvalid_regexp, Fcons (dummy, Qnil)); ! 75: } ! 76: last_regexp = pattern; ! 77: return; ! 78: } ! 79: ! 80: /* Error condition used for failing searches */ ! 81: Lisp_Object Qsearch_failed; ! 82: ! 83: Lisp_Object ! 84: signal_failure (arg) ! 85: Lisp_Object arg; ! 86: { ! 87: Fsignal (Qsearch_failed, Fcons (arg, Qnil)); ! 88: return Qnil; ! 89: } ! 90: ! 91: DEFUN ("looking-at", Flooking_at, Slooking_at, 1, 1, 0, ! 92: "t if text after point matches regular expression PAT.") ! 93: (string) ! 94: Lisp_Object string; ! 95: { ! 96: Lisp_Object val; ! 97: unsigned char *p1, *p2; ! 98: int s1, s2; ! 99: register int i; ! 100: ! 101: CHECK_STRING (string, 0); ! 102: compile_pattern (string, &searchbuf, ! 103: !NULL (bf_cur->case_fold_search) ? (char *) downcase_table : 0); ! 104: ! 105: immediate_quit = 1; ! 106: QUIT; /* Do a pending quit right away, to avoid paradoxical behavior */ ! 107: ! 108: /* Get pointers and sizes of the two strings ! 109: that make up the visible portion of the buffer. */ ! 110: ! 111: p1 = bf_p1 + bf_head_clip; ! 112: s1 = bf_s1 - (bf_head_clip - 1); ! 113: p2 = bf_p2 + bf_s1 + 1; ! 114: s2 = bf_s2 - bf_tail_clip; ! 115: if (s1 < 0) ! 116: { ! 117: p2 -= s1; ! 118: s2 += s1; ! 119: s1 = 0; ! 120: } ! 121: if (s2 < 0) ! 122: { ! 123: s1 += s2; ! 124: s2 = 0; ! 125: } ! 126: ! 127: val = (0 <= re_match_2 (&searchbuf, p1, s1, p2, s2, ! 128: point - FirstCharacter, &search_regs, ! 129: NumCharacters + 1 - FirstCharacter) ! 130: ? Qt : Qnil); ! 131: for (i = 0; i < RE_NREGS; i++) ! 132: { ! 133: search_regs.start[i] += FirstCharacter - 1; ! 134: search_regs.end[i] += FirstCharacter - 1; ! 135: } ! 136: immediate_quit = 0; ! 137: return val; ! 138: } ! 139: ! 140: DEFUN ("string-match", Fstring_match, Sstring_match, 2, 3, 0, ! 141: "Return index of start of first match for REGEXP in STRING, or nil.\n\ ! 142: If third arg START is non-nil, start search at that index in STRING.\n\ ! 143: For index of first char beyond the match, do (match-end 0).\n\ ! 144: match-end and match-beginning also give indices of substrings\n\ ! 145: matched by parenthesis constructs in the pattern.") ! 146: (regexp, string, start) ! 147: Lisp_Object regexp, string, start; ! 148: { ! 149: int val; ! 150: int s; ! 151: ! 152: CHECK_STRING (regexp, 0); ! 153: CHECK_STRING (string, 1); ! 154: ! 155: if (NULL (start)) ! 156: s = 0; ! 157: else ! 158: { ! 159: CHECK_NUMBER (start, 2); ! 160: s = XINT (start); ! 161: } ! 162: ! 163: compile_pattern (regexp, &searchbuf, ! 164: !NULL (bf_cur->case_fold_search) ? (char *) downcase_table : 0); ! 165: val = re_search (&searchbuf, XSTRING (string)->data, XSTRING (string)->size, ! 166: s, XSTRING (string)->size - s, &search_regs); ! 167: /* Correct for propensity of match-beginning and match-end ! 168: to add 1 to each of these (which is correct for buffer positions ! 169: since they are origin-1, but not for indices in strings). */ ! 170: for (s = 0; s < RE_NREGS; s++) ! 171: { ! 172: search_regs.start[s]--; ! 173: search_regs.end[s]--; ! 174: } ! 175: if (val < 0) return Qnil; ! 176: return make_number (val); ! 177: } ! 178: ! 179: DEFUN ("scan-buffer", Fscan_buffer, Sscan_buffer, 3, 3, 0, ! 180: "Scan from character number FROM for COUNT occurrences of character C.\n\ ! 181: Returns the character number of the position after the character found.\n\ ! 182: If not found, returns char number of beginning or end of buffer.\n\ ! 183: Note that this does -not- take take case-fold-search into consideration.") ! 184: (from, count, c) ! 185: Lisp_Object from, count, c; ! 186: { ! 187: CHECK_NUMBER_COERCE_MARKER (from, 0); ! 188: CHECK_NUMBER (count, 1); ! 189: CHECK_NUMBER (c, 2); ! 190: ! 191: return make_number (ScanBf (XINT (c), XINT (from), XINT (count))); ! 192: } ! 193: ! 194: ScanBf (target, pos, cnt) ! 195: register int target, pos, cnt; ! 196: { ! 197: register int end; ! 198: if (cnt > 0) ! 199: { ! 200: end = NumCharacters + 1; ! 201: while (pos < end) ! 202: { ! 203: if (CharAt (pos) == target && !--cnt) ! 204: return pos + 1; ! 205: pos++; ! 206: } ! 207: return pos; ! 208: } ! 209: if (cnt < 0) ! 210: { ! 211: end = FirstCharacter; ! 212: do pos--; ! 213: while (pos >= end && ! 214: (CharAt (pos) != target || ++cnt)); ! 215: } ! 216: return pos + 1; ! 217: } ! 218: ! 219: DEFUN ("skip-chars-forward", Fskip_chars_forward, Sskip_chars_forward, 1, 2, 0, ! 220: "Move point forward, stopping before a char not in CHARS, or at position LIM.\n\ ! 221: CHARS is like the inside of a [...] in a regular expression\n\ ! 222: except that ] is never special and \\ quotes ^, - or \\.\n\ ! 223: Thus, with arg \"a-zA-Z\", this skips letters stopping before first nonletter.\n\ ! 224: With arg \"^a-zA-Z\", skips nonletters stopping before first letter.") ! 225: (string, lim) ! 226: Lisp_Object string, lim; ! 227: { ! 228: skip_chars (1, string, lim); ! 229: return Qnil; ! 230: } ! 231: ! 232: DEFUN ("skip-chars-backward", Fskip_chars_backward, Sskip_chars_backward, 1, 2, 0, ! 233: "Move point backward, stopping after a char not in CHARS, or at position LIM.\n\ ! 234: See skip-chars-forward for details.") ! 235: (string, lim) ! 236: Lisp_Object string, lim; ! 237: { ! 238: skip_chars (0, string, lim); ! 239: return Qnil; ! 240: } ! 241: ! 242: skip_chars (forwardp, string, lim) ! 243: int forwardp; ! 244: Lisp_Object string, lim; ! 245: { ! 246: register unsigned char *p, *pend; ! 247: register unsigned char c; ! 248: unsigned char fastmap[0400]; ! 249: int negate = 0; ! 250: register int i; ! 251: ! 252: CHECK_STRING (string, 0); ! 253: ! 254: if (NULL (lim)) ! 255: XSETINT (lim, forwardp ? NumCharacters + 1 : FirstCharacter); ! 256: else ! 257: CHECK_NUMBER_COERCE_MARKER (lim, 1); ! 258: ! 259: p = XSTRING (string)->data; ! 260: pend = p + XSTRING (string)->size; ! 261: bzero (fastmap, sizeof fastmap); ! 262: ! 263: if (p != pend && *p == '^') ! 264: { ! 265: negate = 1; p++; ! 266: } ! 267: ! 268: /* Find the characters specified and set their elements of fastmap. */ ! 269: ! 270: while (p != pend) ! 271: { ! 272: c = *p++; ! 273: if (*p == '\\') ! 274: { ! 275: if (p == pend) break; ! 276: c = *p++; ! 277: } ! 278: if (p != pend && *p == '-') ! 279: { ! 280: p++; ! 281: if (p == pend) break; ! 282: while (c <= *p) ! 283: { ! 284: fastmap[c] = 1; ! 285: c++; ! 286: } ! 287: p++; ! 288: } ! 289: else ! 290: fastmap[c] = 1; ! 291: } ! 292: ! 293: /* If ^ was the first character, complement the fastmap. */ ! 294: ! 295: if (negate) ! 296: for (i = 0; i < sizeof fastmap; i++) ! 297: fastmap[i] ^= 1; ! 298: ! 299: if (forwardp) ! 300: { ! 301: while (point < XINT (lim) && fastmap[CharAt (point)]) ! 302: PointRight (1); ! 303: } ! 304: else ! 305: { ! 306: while (point > XINT (lim) && fastmap[CharAt (point - 1)]) ! 307: PointLeft (1); ! 308: } ! 309: } ! 310: ! 311: /* Subroutines of Lisp buffer search functions. */ ! 312: ! 313: static Lisp_Object ! 314: search_command (string, bound, noerror, count, direction, RE) ! 315: Lisp_Object string, bound, noerror, count; ! 316: int direction; ! 317: int RE; ! 318: { ! 319: register int np; ! 320: int lim; ! 321: int n = direction; ! 322: ! 323: if (!NULL (count)) ! 324: { ! 325: CHECK_NUMBER (count, 3); ! 326: n *= XINT (count); ! 327: } ! 328: ! 329: CHECK_STRING (string, 0); ! 330: if (NULL (bound)) ! 331: lim = n > 0 ? NumCharacters + 1 : FirstCharacter; ! 332: else ! 333: { ! 334: CHECK_NUMBER_COERCE_MARKER (bound, 1); ! 335: lim = XINT (bound); ! 336: if (n > 0 ? lim < point : lim > point) ! 337: error ("Invalid search bound (wrong side of point)"); ! 338: if (lim > NumCharacters + 1) ! 339: lim = NumCharacters + 1; ! 340: if (lim < FirstCharacter) ! 341: lim = FirstCharacter; ! 342: } ! 343: ! 344: np = search_buffer (string, point, lim, n, RE, ! 345: !NULL (bf_cur->case_fold_search) ? downcase_table : 0); ! 346: if (np == 0) ! 347: { ! 348: if (NULL (noerror)) ! 349: return signal_failure (string); ! 350: if (!EQ (noerror, Qt)) ! 351: SetPoint (lim); ! 352: return Qnil; ! 353: } ! 354: else ! 355: SetPoint (np); ! 356: ! 357: return Qt; ! 358: } ! 359: ! 360: /* search for the n'th occurrence of `string' in the current buffer, ! 361: starting at position `from' and stopping at position `lim', ! 362: treating `pat' as a literal string if `RE' is false or as ! 363: a regular expression if `RE' is true. ! 364: ! 365: If `n' is positive, searching is forward and `lim' must be greater than `from'. ! 366: If `n' is negative, searching is backward and `lim' must be less than `from'. ! 367: ! 368: Returns 0 if `n' occurrences are not found, ! 369: or else the position at the beginning of the `n'th occurrence (if searching backward) ! 370: or the end (if searching forward). */ ! 371: ! 372: search_buffer (string, from, lim, n, RE, trt) ! 373: Lisp_Object string; ! 374: int from; ! 375: register int lim; ! 376: int n; ! 377: int RE; ! 378: unsigned char *trt; ! 379: { ! 380: register int pos = from; ! 381: unsigned char *pat = XSTRING (string)->data; ! 382: register int len = XSTRING (string)->size; ! 383: register int i, j; ! 384: unsigned char *p1, *p2; ! 385: int s1, s2; ! 386: ! 387: immediate_quit = 1; /* Quit immediately if user types ^G, ! 388: because letting this function finish can take too long. */ ! 389: QUIT; /* Do a pending quit right away, to avoid paradoxical behavior */ ! 390: ! 391: if (RE) ! 392: { ! 393: compile_pattern (string, &searchbuf, (char *) trt); ! 394: ! 395: /* Get pointers and sizes of the two strings ! 396: that make up the visible portion of the buffer. */ ! 397: ! 398: p1 = bf_p1 + bf_head_clip; ! 399: s1 = bf_s1 - (bf_head_clip - 1); ! 400: p2 = bf_p2 + bf_s1 + 1; ! 401: s2 = bf_s2 - bf_tail_clip; ! 402: if (s1 < 0) ! 403: { ! 404: p2 -= s1; ! 405: s2 += s1; ! 406: s1 = 0; ! 407: } ! 408: if (s2 < 0) ! 409: { ! 410: s1 += s2; ! 411: s2 = 0; ! 412: } ! 413: } ! 414: ! 415: while (n < 0) ! 416: { ! 417: if (!RE) ! 418: { ! 419: pos -= len; ! 420: if (trt && !(trt == downcase_table && !isalpha (pat[0]))) ! 421: { ! 422: j = trt[pat[0]]; ! 423: while (pos >= lim ! 424: && (j != trt[CharAt (pos)] ! 425: || bcmp_buffer_translated (pat, len, pos, trt))) ! 426: pos--; ! 427: } ! 428: else ! 429: { ! 430: j = pat[0]; ! 431: while (pos >= lim ! 432: && (j != CharAt (pos) ! 433: || bcmp_buffer_translated (pat, len, pos, trt))) ! 434: pos--; ! 435: } ! 436: ! 437: if (pos < lim) ! 438: { ! 439: immediate_quit = 0; ! 440: return 0; ! 441: } ! 442: search_regs.start[0] = pos - 1; ! 443: search_regs.end[0] = pos - 1 + len; ! 444: } ! 445: else ! 446: { ! 447: if (re_search_2 (&searchbuf, p1, s1, p2, s2, ! 448: pos - FirstCharacter, lim - pos, &search_regs, ! 449: /* Don't allow match past current point */ ! 450: pos - FirstCharacter) ! 451: >= 0) ! 452: { ! 453: j = FirstCharacter - 1; ! 454: for (i = 0; i < RE_NREGS; i++) ! 455: { ! 456: search_regs.start[i] += j; ! 457: search_regs.end[i] += j; ! 458: } ! 459: /* Set pos to the new position. */ ! 460: pos = search_regs.start[0] + 1; ! 461: } ! 462: else ! 463: { ! 464: immediate_quit = 0; ! 465: return 0; ! 466: } ! 467: } ! 468: n++; ! 469: } ! 470: ! 471: while (n > 0) ! 472: { ! 473: if (!RE) ! 474: { ! 475: lim -= len; ! 476: if (trt && !(trt == downcase_table && !isalpha (pat[0]))) ! 477: { ! 478: j = trt[pat[0]]; ! 479: while (pos <= lim ! 480: && (j != trt[CharAt (pos)] ! 481: || bcmp_buffer_translated (pat, len, pos, trt))) ! 482: pos++; ! 483: } ! 484: else ! 485: { ! 486: j = pat[0]; ! 487: while (pos <= lim ! 488: && (j != CharAt (pos) ! 489: || bcmp_buffer_translated (pat, len, pos, trt))) ! 490: pos++; ! 491: } ! 492: ! 493: if (pos > lim) ! 494: { ! 495: immediate_quit = 0; ! 496: return 0; ! 497: } ! 498: ! 499: lim += len; ! 500: ! 501: search_regs.start[0] = pos - 1; ! 502: pos += len; ! 503: search_regs.end[0] = pos - 1; ! 504: } ! 505: else ! 506: { ! 507: if (re_search_2 (&searchbuf, p1, s1, p2, s2, ! 508: pos - FirstCharacter, lim - pos, &search_regs, ! 509: lim - FirstCharacter) ! 510: >= 0) ! 511: { ! 512: j = FirstCharacter - 1; ! 513: for (i = 0; i < RE_NREGS; i++) ! 514: { ! 515: search_regs.start[i] += j; ! 516: search_regs.end[i] += j; ! 517: } ! 518: pos = 1 + search_regs.end[0]; ! 519: } ! 520: else ! 521: { ! 522: immediate_quit = 0; ! 523: return 0; ! 524: } ! 525: } ! 526: n--; ! 527: } ! 528: immediate_quit = 0; ! 529: return pos; ! 530: } ! 531: ! 532: /* Return nonzero unless the `len' characters in the buffer starting at position `pos' ! 533: match the `len' characters at `pat', with all characters going through the ! 534: translate table `trt' if `trt' is nonzero. */ ! 535: ! 536: static int ! 537: bcmp_buffer_translated (pat, len, pos, trt) ! 538: unsigned char *pat; ! 539: int len; ! 540: int pos; ! 541: register char *trt; ! 542: { ! 543: int dist1 = 0; ! 544: register int i; ! 545: register unsigned char *p1, *p2; ! 546: ! 547: if (pos - 1 < bf_s1) ! 548: { ! 549: p1 = pat, p2 = &CharAt (pos); ! 550: dist1 = bf_s1 - (pos - 1); ! 551: if (dist1 > len) dist1 = len; ! 552: i = dist1; ! 553: ! 554: if (trt) ! 555: { ! 556: for (; i; i--) ! 557: if (trt[*p1++] != trt [*p2++]) return 1; ! 558: } ! 559: else ! 560: { ! 561: for (; i; i--) ! 562: if (*p1++ != *p2++) return 1; ! 563: } ! 564: } ! 565: ! 566: if (dist1 < len) ! 567: { ! 568: p1 = pat + dist1, p2 = &CharAt (pos + dist1); ! 569: i = len - dist1; ! 570: ! 571: if (trt) ! 572: { ! 573: for (; i; i--) ! 574: if (trt[*p1++] != trt [*p2++]) return 1; ! 575: } ! 576: else ! 577: { ! 578: for (; i; i--) ! 579: if (*p1++ != *p2++) return 1; ! 580: } ! 581: } ! 582: return 0; ! 583: } ! 584: ! 585: /* Given a string of words separated by word delimiters, ! 586: compute a regexp that matches those exact words ! 587: separated by arbitrary punctuation. */ ! 588: ! 589: static Lisp_Object ! 590: wordify (string) ! 591: Lisp_Object string; ! 592: { ! 593: register unsigned char *p, *o; ! 594: register int i, len, punct_count = 0, word_count = 0; ! 595: Lisp_Object val; ! 596: ! 597: CHECK_STRING (string, 0); ! 598: p = XSTRING (string)->data; ! 599: len = XSTRING (string)->size; ! 600: ! 601: for (i = 0; i < len; i++) ! 602: if (SYNTAX (p[i]) != Sword) ! 603: { ! 604: punct_count++; ! 605: if (i > 0 && SYNTAX (p[i-1]) == Sword) word_count++; ! 606: } ! 607: if (SYNTAX (p[len-1]) == Sword) word_count++; ! 608: if (!word_count) return build_string (""); ! 609: ! 610: val = make_string (p, len - punct_count + 5 * (word_count - 1) + 4); ! 611: ! 612: o = XSTRING (val)->data; ! 613: *o++ = '\\'; ! 614: *o++ = 'b'; ! 615: ! 616: for (i = 0; i < len; i++) ! 617: if (SYNTAX (p[i]) == Sword) ! 618: *o++ = p[i]; ! 619: else if (i > 0 && SYNTAX (p[i-1]) == Sword && --word_count) ! 620: { ! 621: *o++ = '\\'; ! 622: *o++ = 'W'; ! 623: *o++ = '\\'; ! 624: *o++ = 'W'; ! 625: *o++ = '*'; ! 626: } ! 627: ! 628: *o++ = '\\'; ! 629: *o++ = 'b'; ! 630: ! 631: return val; ! 632: } ! 633: ! 634: DEFUN ("search-backward", Fsearch_backward, Ssearch_backward, 1, 4, ! 635: "sSearch backward: ", ! 636: "Search backward from point for STRING.\n\ ! 637: Set point to the beginning of the occurrence found, and return t.\n\ ! 638: An optional second argument bounds the search; it is a buffer position.\n\ ! 639: The match found must not extend before that position.\n\ ! 640: Optional third argument, if t, means if fail just return nil (no error).\n\ ! 641: If not nil and not t, position at limit of search and return nil.\n\ ! 642: Optional fourth argument is repeat count--search for successive occurrences.") ! 643: (string, bound, noerror, count) ! 644: Lisp_Object string, bound, noerror, count; ! 645: { ! 646: return search_command (string, bound, noerror, count, -1, 0); ! 647: } ! 648: ! 649: DEFUN ("search-forward", Fsearch_forward, Ssearch_forward, 1, 4, "sSearch: ", ! 650: "Search forward from point for STRING.\n\ ! 651: Set point to the end of the occurrence found, and return t.\n\ ! 652: An optional second argument bounds the search; it is a buffer position.\n\ ! 653: The match found must not extend after that position.\n\ ! 654: Optional third argument, if t, means if fail just return nil (no error).\n\ ! 655: If not nil and not t, move to limit of search and return nil.\n\ ! 656: Optional fourth argument is repeat count--search for successive occurrences.") ! 657: (string, bound, noerror, count) ! 658: Lisp_Object string, bound, noerror, count; ! 659: { ! 660: return search_command (string, bound, noerror, count, 1, 0); ! 661: } ! 662: ! 663: DEFUN ("word-search-backward", Fword_search_backward, Sword_search_backward, 1, 4, ! 664: "sWord search backward: ", ! 665: "Search backward from point for STRING, ignoring differences in punctuation.\n\ ! 666: Set point to the beginning of the occurrence found, and return t.\n\ ! 667: An optional second argument bounds the search; it is a buffer position.\n\ ! 668: The match found must not extend before that position.\n\ ! 669: Optional third argument, if t, means if fail just return nil (no error).\n\ ! 670: If not nil and not t, move to limit of search and return nil.\n\ ! 671: Optional fourth argument is repeat count--search for successive occurrences.") ! 672: (string, bound, noerror, count) ! 673: Lisp_Object string, bound, noerror, count; ! 674: { ! 675: return search_command (wordify (string), bound, noerror, count, -1, 1); ! 676: } ! 677: ! 678: DEFUN ("word-search-forward", Fword_search_forward, Sword_search_forward, 1, 4, ! 679: "sWord search: ", ! 680: "Search forward from point for STRING, ignoring differences in punctuation.\n\ ! 681: Set point to the end of the occurrence found, and return t.\n\ ! 682: An optional second argument bounds the search; it is a buffer position.\n\ ! 683: The match found must not extend after that position.\n\ ! 684: Optional third argument, if t, means if fail just return nil (no error).\n\ ! 685: If not nil and not t, move to limit of search and return nil.\n\ ! 686: Optional fourth argument is repeat count--search for successive occurrences.") ! 687: (string, bound, noerror, count) ! 688: Lisp_Object string, bound, noerror, count; ! 689: { ! 690: return search_command (wordify (string), bound, noerror, count, 1, 1); ! 691: } ! 692: ! 693: DEFUN ("re-search-backward", Fre_search_backward, Sre_search_backward, 1, 4, ! 694: "sRE search backward: ", ! 695: "Search backward from point for regular expression REGEXP.\n\ ! 696: Set point to the beginning of the occurrence found, and return t.\n\ ! 697: An optional second argument bounds the search; it is a buffer position.\n\ ! 698: The match found must not extend before that position.\n\ ! 699: Optional third argument, if t, means if fail just return nil (no error).\n\ ! 700: If not nil and not t, move to limit of search and return nil.\n\ ! 701: Optional fourth argument is repeat count--search for successive occurrences.\n\ ! 702: See also the functions match-beginning and match-end and replace-match.") ! 703: (string, bound, noerror, count) ! 704: Lisp_Object string, bound, noerror, count; ! 705: { ! 706: return search_command (string, bound, noerror, count, -1, 1); ! 707: } ! 708: ! 709: DEFUN ("re-search-forward", Fre_search_forward, Sre_search_forward, 1, 4, ! 710: "sRE search: ", ! 711: "Search forward from point for regular expression REGEXP.\n\ ! 712: Set point to the end of the occurrence found, and return t.\n\ ! 713: An optional second argument bounds the search; it is a buffer position.\n\ ! 714: The match found must not extend after that position.\n\ ! 715: Optional third argument, if t, means if fail just return nil (no error).\n\ ! 716: If not nil and not t, move to limit of search and return nil.\n\ ! 717: Optional fourth argument is repeat count--search for successive occurrences.\n\ ! 718: See also the functions match-beginning and match-end and replace-match.") ! 719: (string, bound, noerror, count) ! 720: Lisp_Object string, bound, noerror, count; ! 721: { ! 722: return search_command (string, bound, noerror, count, 1, 1); ! 723: } ! 724: ! 725: DEFUN ("replace-match", Freplace_match, Sreplace_match, 1, 3, 0, ! 726: "Replace text matched by last search with NEWTEXT.\n\ ! 727: If second arg FIXEDCASE is non-nil, do not alter case of replacement text.\n\ ! 728: Otherwise convert to all caps or cap initials, like replaced text.\n\ ! 729: If third arg LITERAL is non-nil, insert NEWTEXT literally.\n\ ! 730: Otherwise treat \\ as special:\n\ ! 731: \\& in NEWTEXT means substitute original matched text,\n\ ! 732: \\<n> means substitute match for \\(...\\) number <n>,\n\ ! 733: \\\\ means insert one \\.\n\ ! 734: Leaves point at end of replacement text.") ! 735: (string, fixedcase, literal) ! 736: Lisp_Object string, fixedcase, literal; ! 737: { ! 738: enum { nochange, all_caps, cap_initial } case_action = nochange; ! 739: register int pos, last; ! 740: int some_multiletter_word = 0; ! 741: int some_letter = 0; ! 742: register char c, prevc; ! 743: int inslen; ! 744: ! 745: if (search_regs.start[0] + 1 < FirstCharacter ! 746: || search_regs.start[0] > search_regs.end[0] ! 747: || search_regs.end[0] > NumCharacters) ! 748: args_out_of_range(make_number (search_regs.start[0]), ! 749: make_number (search_regs.end[0])); ! 750: ! 751: if (NULL (fixedcase)) ! 752: { ! 753: /* Decide how to casify by examining the matched text. */ ! 754: ! 755: last = search_regs.end[0]; ! 756: prevc = '\n'; ! 757: case_action = all_caps; ! 758: ! 759: /* some_multiletter_word is set nonzero if any original word ! 760: is more than one letter long. */ ! 761: some_multiletter_word = 0; ! 762: ! 763: for (pos = search_regs.start[0] + 1; pos <= last; pos++) ! 764: { ! 765: c = CharAt (pos); ! 766: if (c >= 'a' && c <= 'z') ! 767: { ! 768: /* Cannot be all caps if any original char is lower case */ ! 769: ! 770: case_action = cap_initial; ! 771: if (SYNTAX (prevc) != Sword) ! 772: { ! 773: /* Cannot even be cap initials ! 774: if some original initial is lower case */ ! 775: case_action = nochange; ! 776: break; ! 777: } ! 778: else ! 779: some_multiletter_word = 1; ! 780: } ! 781: else if (c >= 'A' && c <= 'Z') ! 782: { ! 783: some_letter = 1; ! 784: if (!some_multiletter_word && SYNTAX (prevc) == Sword) ! 785: some_multiletter_word = 1; ! 786: } ! 787: ! 788: prevc = c; ! 789: } ! 790: ! 791: /* Do not make new text all caps ! 792: if the original text contained only single letter words. */ ! 793: if (case_action == all_caps && !some_multiletter_word) ! 794: case_action = cap_initial; ! 795: ! 796: if (!some_letter) case_action = nochange; ! 797: } ! 798: ! 799: SetPoint (search_regs.end[0] + 1); ! 800: if (!NULL (literal)) ! 801: Finsert (1, &string); ! 802: else ! 803: { ! 804: for (pos = 0; pos < XSTRING (string)->size; pos++) ! 805: { ! 806: c = XSTRING (string)->data[pos]; ! 807: if (c == '\\') ! 808: { ! 809: c = XSTRING (string)->data[++pos]; ! 810: if (c == '&') ! 811: place (search_regs.start[0] + 1, ! 812: search_regs.end[0] + 1); ! 813: else if (c >= '1' && c <= RE_NREGS + '0') ! 814: place (search_regs.start[c - '0'] + 1, ! 815: search_regs.end[c - '0'] + 1); ! 816: else ! 817: insert_char (c); ! 818: } ! 819: else ! 820: insert_char (c); ! 821: } ! 822: } ! 823: ! 824: inslen = point - (search_regs.end[0] + 1); ! 825: del_range (search_regs.start[0] + 1, search_regs.end[0] + 1); ! 826: ! 827: if (case_action == all_caps) ! 828: Fupcase_region (make_number (point - inslen), make_number (point)); ! 829: else if (case_action == cap_initial) ! 830: { /* Fcapitalize_region won't do; must not downcase anything. */ ! 831: last = 0; ! 832: for (pos = point - inslen; pos < point; pos++) ! 833: { ! 834: c = CharAt (pos); ! 835: if (!last && (c >= 'a' && c <= 'z')) ! 836: CharAt (pos) = c ^ ('a' - 'A'); ! 837: last = SYNTAX (c) == Sword; ! 838: } ! 839: } ! 840: return Qnil; ! 841: } ! 842: ! 843: static ! 844: place (l1, l2) ! 845: int l1, l2; ! 846: { ! 847: if (l1 < FirstCharacter) ! 848: l1 = FirstCharacter; ! 849: if (l1 >= NumCharacters) ! 850: l1 = NumCharacters; ! 851: if (l2 < l1) l2 = l1; ! 852: if (l2 >= NumCharacters) ! 853: l2 = NumCharacters; ! 854: GapTo (point); ! 855: InsCStr (&CharAt (l1), l2 - l1); ! 856: } ! 857: ! 858: DEFUN ("match-beginning", Fmatch_beginning, Smatch_beginning, 1, 1, 0, ! 859: "Return the character number of start of text matched by last regexp searched for.\n\ ! 860: ARG, a number, specifies which parenthesized expression in the last regexp.\n\ ! 861: Zero means the entire text matched by the whole regexp.") ! 862: (num) ! 863: Lisp_Object num; ! 864: { ! 865: register n; ! 866: CHECK_NUMBER (num, 0); ! 867: n = XINT (num); ! 868: if (n < 0 || n >= RE_NREGS) ! 869: error ("Out-of-bounds argument"); ! 870: return make_number (search_regs.start[n] + 1); ! 871: } ! 872: ! 873: DEFUN ("match-end", Fmatch_end, Smatch_end, 1, 1, 0, ! 874: "Return the character number of end of text matched by last regexp searched for.\n\ ! 875: ARG, a number, specifies which parenthesized expression in the last regexp.\n\ ! 876: Zero means the entire text matched by the whole regexp.") ! 877: (num) ! 878: Lisp_Object num; ! 879: { ! 880: register n; ! 881: CHECK_NUMBER (num, 0); ! 882: n = XINT (num); ! 883: if (n < 0 || n >= RE_NREGS) ! 884: error ("Out-of-bounds argument"); ! 885: return make_number (search_regs.end[n] + 1); ! 886: } ! 887: ! 888: DEFUN ("match-data", Fmatch_data, Smatch_data, 0, 0, 0, ! 889: "Return list containing all info on what the last search matched.\n\ ! 890: Element 2N is (match-beginning N); element 2N + 1 is (match-end N).\n\ ! 891: All are represented as markers.") ! 892: () ! 893: { ! 894: Lisp_Object data[2 * RE_NREGS]; ! 895: int i; ! 896: ! 897: for (i = 0; i < RE_NREGS; i++) ! 898: { ! 899: data[2 * i] = Fmake_marker (); ! 900: Fset_marker (data[2*i], make_number (search_regs.start[i] + 1), Qnil); ! 901: data[2 * i + 1] = Fmake_marker (); ! 902: Fset_marker (data[2*i + 1], make_number (search_regs.end[i] + 1), Qnil); ! 903: } ! 904: ! 905: return Flist (2 * RE_NREGS, data); ! 906: } ! 907: ! 908: ! 909: DEFUN ("store-match-data", Fstore_match_data, Sstore_match_data, 1, 1, 0, ! 910: "Set internal data on last search match from elements of LIST.\n\ ! 911: LIST should have been created by calling match-data previously.") ! 912: (list) ! 913: register Lisp_Object list; ! 914: { ! 915: register int i; ! 916: register Lisp_Object marker; ! 917: ! 918: if (!LISTP (list)) ! 919: list = wrong_type_argument (Qlistp, list, 0); ! 920: ! 921: for (i = 0; i < RE_NREGS; i++) ! 922: { ! 923: marker = Fcar (list); ! 924: CHECK_MARKER (marker, 0); ! 925: search_regs.start[i] = marker_position (marker) - 1; ! 926: list = Fcdr (list); ! 927: ! 928: marker = Fcar (list); ! 929: CHECK_MARKER (marker, 0); ! 930: search_regs.end[i] = marker_position (marker) - 1; ! 931: list = Fcdr (list); ! 932: } ! 933: ! 934: return Qnil; ! 935: } ! 936: ! 937: /* Quote a string to inactivate reg-expr chars */ ! 938: ! 939: DEFUN ("regexp-quote", Fregexp_quote, Sregexp_quote, 1, 1, 0, ! 940: "Return a regexp string which matches exactly STRING and nothing else.") ! 941: (str) ! 942: Lisp_Object str; ! 943: { ! 944: register unsigned char *p, *cp, *end; ! 945: register int size; ! 946: Lisp_Object ostr; ! 947: ! 948: CHECK_STRING (str, 0); ! 949: size = XSTRING (str)->size; ! 950: ! 951: /* Increment `size' for the escapes we will need to insert */ ! 952: ! 953: for (cp = XSTRING (str)->data, end = cp + size; cp != end; cp++) ! 954: if (*cp == '[' || *cp == ']' ! 955: || *cp == '*' || *cp == '.' || *cp == '\\' ! 956: || *cp == '?' || *cp == '+' ! 957: || *cp == '^' || *cp == '$') ! 958: size++; ! 959: ! 960: ostr = Fmake_string (make_number (size), make_number (0)); ! 961: ! 962: /* Now copy the data into the new string, inserting escapes. */ ! 963: ! 964: p = XSTRING (ostr)->data; ! 965: for (cp = XSTRING (str)->data; cp != end; cp++) ! 966: { ! 967: if (*cp == '[' || *cp == ']' ! 968: || *cp == '*' || *cp == '.' || *cp == '\\' ! 969: || *cp == '?' || *cp == '+' ! 970: || *cp == '^' || *cp == '$') ! 971: *p++ = '\\'; ! 972: *p++ = *cp; ! 973: } ! 974: return ostr; ! 975: } ! 976: ! 977: syms_of_search () ! 978: { ! 979: register int i; ! 980: ! 981: for (i = 0; i < 0400; i++) ! 982: downcase_table[i] = (i >= 'A' && i <= 'Z') ? i + 040 : i; ! 983: ! 984: searchbuf.allocated = 100; ! 985: searchbuf.buffer = (char *) malloc (searchbuf.allocated); ! 986: searchbuf.fastmap = search_fastmap; ! 987: ! 988: Qsearch_failed = intern ("search-failed"); ! 989: staticpro (&Qsearch_failed); ! 990: Qinvalid_regexp = intern ("invalid-regexp"); ! 991: staticpro (&Qinvalid_regexp); ! 992: ! 993: Fput (Qsearch_failed, Qerror_conditions, ! 994: Fcons (Qsearch_failed, Fcons (Qerror, Qnil))); ! 995: Fput (Qsearch_failed, Qerror_message, ! 996: build_string ("Search failed")); ! 997: ! 998: Fput (Qinvalid_regexp, Qerror_conditions, ! 999: Fcons (Qinvalid_regexp, Fcons (Qerror, Qnil))); ! 1000: Fput (Qinvalid_regexp, Qerror_message, ! 1001: build_string ("Invalid regexp")); ! 1002: ! 1003: last_regexp = Qnil; ! 1004: staticpro (&last_regexp); ! 1005: ! 1006: defsubr (&Sstring_match); ! 1007: defsubr (&Slooking_at); ! 1008: defsubr (&Sscan_buffer); ! 1009: defsubr (&Sskip_chars_forward); ! 1010: defsubr (&Sskip_chars_backward); ! 1011: defsubr (&Ssearch_forward); ! 1012: defsubr (&Ssearch_backward); ! 1013: defsubr (&Sword_search_forward); ! 1014: defsubr (&Sword_search_backward); ! 1015: defsubr (&Sre_search_forward); ! 1016: defsubr (&Sre_search_backward); ! 1017: defsubr (&Sreplace_match); ! 1018: defsubr (&Smatch_beginning); ! 1019: defsubr (&Smatch_end); ! 1020: defsubr (&Smatch_data); ! 1021: defsubr (&Sstore_match_data); ! 1022: defsubr (&Sregexp_quote); ! 1023: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.