Annotation of 43BSD/contrib/emacs/src/search.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.