set epp for empty patterns, too, "://" is a legal command
[nvi.git] / common / search.c
blob6e542d50aaae532bda416598e003b3854a5d10f1
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: search.c,v 8.31 1993/12/19 16:17:24 bostic Exp $ (Berkeley) $Date: 1993/12/19 16:17:24 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
20 #include "vi.h"
22 static int check_delta __P((SCR *, EXF *, long, recno_t));
23 static int ctag_conv __P((SCR *, char **, int *));
24 static int get_delta __P((SCR *, char **, long *, u_int *));
25 static int resetup __P((SCR *, regex_t **, enum direction,
26 char *, char **, long *, u_int *));
27 static void search_intr __P((int));
30 * resetup --
31 * Set up a search for a regular expression.
33 static int
34 resetup(sp, rep, dir, ptrn, epp, deltap, flagp)
35 SCR *sp;
36 regex_t **rep;
37 enum direction dir;
38 char *ptrn, **epp;
39 long *deltap;
40 u_int *flagp;
42 u_int flags;
43 int delim, eval, re_flags, replaced;
44 char *p, *t;
46 /* Set return information the default. */
47 *deltap = 0;
50 * Use saved pattern if no pattern supplied, or if only a delimiter
51 * character is supplied. Only the pattern was saved, historic vi
52 * did not reuse any delta supplied.
54 flags = *flagp;
55 if (ptrn == NULL)
56 goto prev;
57 if (ptrn[1] == '\0') {
58 if (epp != NULL)
59 *epp = ptrn + 1;
60 goto prev;
62 if (ptrn[0] == ptrn[1] && ptrn[2] == '\0') {
63 if (epp != NULL)
64 *epp = ptrn + 2;
65 prev: if (!F_ISSET(sp, S_SRE_SET)) {
66 msgq(sp, M_ERR, "No previous search pattern.");
67 return (1);
69 *rep = &sp->sre;
71 /* Empty patterns set the direction. */
72 if (LF_ISSET(SEARCH_SET)) {
73 F_SET(sp, S_SRE_SET);
74 sp->searchdir = dir;
75 sp->sre = **rep;
77 return (0);
80 re_flags = 0; /* Set RE flags. */
81 if (O_ISSET(sp, O_EXTENDED))
82 re_flags |= REG_EXTENDED;
83 if (O_ISSET(sp, O_IGNORECASE))
84 re_flags |= REG_ICASE;
86 if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
87 /* Set delimiter. */
88 delim = *ptrn++;
90 /* Find terminating delimiter, handling escaped delimiters. */
91 for (p = t = ptrn;;) {
92 if (p[0] == '\0' || p[0] == delim) {
93 if (p[0] == delim)
94 ++p;
95 *t = '\0';
96 break;
98 if (p[1] == delim && p[0] == '\\')
99 ++p;
100 *t++ = *p++;
104 * If characters after the terminating delimiter, it may
105 * be an error, or may be an offset. In either case, we
106 * return the end of the string, whatever it may be.
108 if (*p) {
109 if (get_delta(sp, &p, deltap, flagp))
110 return (1);
111 if (*p && LF_ISSET(SEARCH_TERM)) {
112 msgq(sp, M_ERR,
113 "Characters after search string and/or delta.");
114 return (1);
117 if (epp != NULL)
118 *epp = p;
120 /* Check for "/ " or other such silliness. */
121 if (*ptrn == '\0')
122 goto prev;
124 if (re_conv(sp, &ptrn, &replaced))
125 return (1);
126 } else if (LF_ISSET(SEARCH_TAG)) {
127 if (ctag_conv(sp, &ptrn, &replaced))
128 return (1);
129 re_flags &= ~(REG_EXTENDED | REG_ICASE);
132 /* Compile the RE. */
133 if (eval = regcomp(*rep, ptrn, re_flags))
134 re_error(sp, eval, *rep);
135 else if (LF_ISSET(SEARCH_SET)) {
136 F_SET(sp, S_SRE_SET);
137 sp->searchdir = dir;
138 sp->sre = **rep;
141 /* Free up any extra memory. */
142 if (replaced)
143 FREE_SPACE(sp, ptrn, 0);
144 return (eval);
148 * ctag_conv --
149 * Convert a tags search path into something that the POSIX
150 * 1003.2 RE functions can handle.
152 static int
153 ctag_conv(sp, ptrnp, replacedp)
154 SCR *sp;
155 char **ptrnp;
156 int *replacedp;
158 size_t blen, len;
159 int lastdollar;
160 char *bp, *p, *t;
162 *replacedp = 0;
164 len = strlen(p = *ptrnp);
166 /* Max memory usage is 2 times the length of the string. */
167 GET_SPACE_RET(sp, bp, blen, len * 2);
169 t = bp;
171 /* The last charcter is a '/' or '?', we just strip it. */
172 if (p[len - 1] == '/' || p[len - 1] == '?')
173 p[len - 1] = '\0';
175 /* The next-to-last character is a '$', and it's magic. */
176 if (p[len - 2] == '$') {
177 lastdollar = 1;
178 p[len - 2] = '\0';
179 } else
180 lastdollar = 0;
182 /* The first character is a '/' or '?', we just strip it. */
183 if (p[0] == '/' || p[0] == '?')
184 ++p;
186 /* The second character is a '^', and it's magic. */
187 if (p[0] == '^')
188 *t++ = *p++;
191 * Escape every other magic character we can find, stripping the
192 * backslashes ctags inserts to escape the search delimiter
193 * characters.
195 while (p[0]) {
196 /* Ctags escapes the search delimiter characters. */
197 if (p[0] == '\\' && (p[1] == '/' || p[1] == '?'))
198 ++p;
199 else if (strchr("^.[]$*", p[0]))
200 *t++ = '\\';
201 *t++ = *p++;
203 if (lastdollar)
204 *t++ = '$';
205 *t++ = '\0';
207 *ptrnp = bp;
208 *replacedp = 1;
209 return (0);
213 * Search interrupts.
215 * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt the
216 * search, so we install a handler. VQUIT is ignored by main() because
217 * nvi never wants to catch it. A handler for VSUSP should have been
218 * installed by the screen code.
220 #define SET_UP_INTERRUPTS { \
221 act.sa_handler = search_intr; \
222 sigemptyset(&act.sa_mask); \
223 act.sa_flags = 0; \
224 if (isig = !sigaction(SIGINT, &act, &oact)) { \
225 istate = F_ISSET(sp, S_INTERRUPTIBLE); \
226 F_CLR(sp, S_INTERRUPTED); \
227 F_SET(sp, S_INTERRUPTIBLE); \
228 if (tcgetattr(STDIN_FILENO, &term)) { \
229 msgq(sp, M_SYSERR, "tcgetattr"); \
230 return (1); \
232 nterm = term; \
233 nterm.c_lflag |= ISIG; \
234 if (tcsetattr(STDIN_FILENO, \
235 TCSANOW | TCSASOFT, &nterm)) { \
236 msgq(sp, M_SYSERR, "tcsetattr"); \
237 return (1); \
242 #define TEAR_DOWN_INTERRUPTS { \
243 if (isig) { \
244 if (sigaction(SIGINT, &oact, NULL)) \
245 msgq(sp, M_SYSERR, "signal"); \
246 if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, &term)) \
247 msgq(sp, M_SYSERR, "tcsetattr"); \
248 F_CLR(sp, S_INTERRUPTED); \
249 if (!istate) \
250 F_CLR(sp, S_INTERRUPTIBLE); \
255 * search_intr --
256 * Set the interrupt bit in any screen that is interruptible.
258 * XXX
259 * In the future this may be a problem. The user should be able to move to
260 * another screen and keep typing while this runs. If so, and the user has
261 * more than one search/global (see ex/ex_global.c) running, it will be hard
262 * to decide which one to stop.
264 static void
265 search_intr(signo)
266 int signo;
268 SCR *sp;
270 for (sp = __global_list->dq.cqh_first;
271 sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
272 if (F_ISSET(sp, S_INTERRUPTIBLE))
273 F_SET(sp, S_INTERRUPTED);
276 #define EMPTYMSG "File empty; nothing to search."
277 #define EOFMSG "Reached end-of-file without finding the pattern."
278 #define NOTFOUND "Pattern not found."
279 #define SOFMSG "Reached top-of-file without finding the pattern."
280 #define WRAPMSG "Search wrapped."
283 f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
284 SCR *sp;
285 EXF *ep;
286 MARK *fm, *rm;
287 char *ptrn, **eptrn;
288 u_int *flagp;
290 struct sigaction act, oact;
291 struct termios nterm, term;
292 regmatch_t match[1];
293 regex_t *re, lre;
294 recno_t lno;
295 size_t coff, len;
296 long delta;
297 u_int flags, istate;
298 int eval, isig, rval, wrapped;
299 char *l;
301 if (file_lline(sp, ep, &lno))
302 return (1);
303 flags = *flagp;
304 if (lno == 0) {
305 if (LF_ISSET(SEARCH_MSG))
306 msgq(sp, M_INFO, EMPTYMSG);
307 return (1);
310 re = &lre;
311 if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp))
312 return (1);
315 * Start searching immediately after the cursor. If at the end of the
316 * line, start searching on the next line. This is incompatible (read
317 * bug fix) with the historic vi -- searches for the '$' pattern never
318 * moved forward, and "-t foo" didn't work if "foo" was the first thing
319 * in the file.
321 if (LF_ISSET(SEARCH_FILE)) {
322 lno = 1;
323 coff = 0;
324 } else {
325 if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) {
326 GETLINE_ERR(sp, fm->lno);
327 return (1);
329 if (fm->cno + 1 >= len) {
330 if (fm->lno == lno) {
331 if (!O_ISSET(sp, O_WRAPSCAN)) {
332 if (LF_ISSET(SEARCH_MSG))
333 msgq(sp, M_INFO, EOFMSG);
334 return (1);
336 lno = 1;
337 } else
338 lno = fm->lno + 1;
339 coff = 0;
340 } else {
341 lno = fm->lno;
342 coff = fm->cno + 1;
347 * Set up busy message, interrupts.
349 * F_search is called from the ex_tagfirst() routine, which runs
350 * before the screen really exists. Make sure we don't step on
351 * anything.
353 if (sp->s_position != NULL)
354 busy_on(sp, 1, "Searching...");
356 if (F_ISSET(sp->gp, G_ISFROMTTY))
357 SET_UP_INTERRUPTS;
359 for (rval = 1, wrapped = 0;; ++lno, coff = 0) {
360 if (F_ISSET(sp, S_INTERRUPTED)) {
361 msgq(sp, M_INFO, "Interrupted.");
362 break;
364 if (wrapped && lno > fm->lno ||
365 (l = file_gline(sp, ep, lno, &len)) == NULL) {
366 if (wrapped) {
367 if (LF_ISSET(SEARCH_MSG))
368 msgq(sp, M_INFO, NOTFOUND);
369 break;
371 if (!O_ISSET(sp, O_WRAPSCAN)) {
372 if (LF_ISSET(SEARCH_MSG))
373 msgq(sp, M_INFO, EOFMSG);
374 break;
376 lno = 0;
377 wrapped = 1;
378 continue;
381 /* If already at EOL, just keep going. */
382 if (len && coff == len)
383 continue;
385 /* Set the termination. */
386 match[0].rm_so = coff;
387 match[0].rm_eo = len;
389 #if defined(DEBUG) && 0
390 TRACE(sp, "F search: %lu from %u to %u\n",
391 lno, coff, len ? len - 1 : len);
392 #endif
393 /* Search the line. */
394 eval = regexec(re, l, 1, match,
395 (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
396 if (eval == REG_NOMATCH)
397 continue;
398 if (eval != 0) {
399 re_error(sp, eval, re);
400 break;
403 /* Warn if wrapped. */
404 if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
405 msgq(sp, M_INFO, WRAPMSG);
408 * If an offset, see if it's legal. It's possible to match
409 * past the end of the line with $, so check for that case.
411 if (delta) {
412 if (check_delta(sp, ep, delta, lno))
413 break;
414 rm->lno = delta + lno;
415 rm->cno = 0;
416 } else {
417 #if defined(DEBUG) && 0
418 TRACE(sp, "found: %qu to %qu\n",
419 match[0].rm_so, match[0].rm_eo);
420 #endif
421 rm->lno = lno;
422 rm->cno = match[0].rm_so;
425 * If a change command, it's possible to move beyond
426 * the end of a line. Historic vi generally got this
427 * wrong (try "c?$<cr>"). Not all that sure this gets
428 * it right, there are lots of strange cases.
430 if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
431 rm->cno = len ? len - 1 : 0;
433 rval = 0;
434 break;
437 /* Turn off busy message, interrupts. */
438 if (sp->s_position != NULL)
439 busy_off(sp);
441 if (F_ISSET(sp->gp, G_ISFROMTTY))
442 TEAR_DOWN_INTERRUPTS;
444 return (rval);
448 b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
449 SCR *sp;
450 EXF *ep;
451 MARK *fm, *rm;
452 char *ptrn, **eptrn;
453 u_int *flagp;
455 struct sigaction act, oact;
456 struct termios nterm, term;
457 regmatch_t match[1];
458 regex_t *re, lre;
459 recno_t lno;
460 size_t coff, len, last;
461 long delta;
462 u_int flags, istate;
463 int eval, isig, rval, wrapped;
464 char *l;
466 if (file_lline(sp, ep, &lno))
467 return (1);
468 flags = *flagp;
469 if (lno == 0) {
470 if (LF_ISSET(SEARCH_MSG))
471 msgq(sp, M_INFO, EMPTYMSG);
472 return (1);
475 re = &lre;
476 if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp))
477 return (1);
479 /* If in the first column, start searching on the previous line. */
480 if (fm->cno == 0) {
481 if (fm->lno == 1) {
482 if (!O_ISSET(sp, O_WRAPSCAN)) {
483 if (LF_ISSET(SEARCH_MSG))
484 msgq(sp, M_INFO, SOFMSG);
485 return (1);
487 } else
488 lno = fm->lno - 1;
489 } else
490 lno = fm->lno;
492 /* Turn on busy message, interrupts. */
493 busy_on(sp, 1, "Searching...");
495 if (F_ISSET(sp->gp, G_ISFROMTTY))
496 SET_UP_INTERRUPTS;
498 for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) {
499 if (F_ISSET(sp, S_INTERRUPTED)) {
500 msgq(sp, M_INFO, "Interrupted.");
501 break;
503 if (wrapped && lno < fm->lno || lno == 0) {
504 if (wrapped) {
505 if (LF_ISSET(SEARCH_MSG))
506 msgq(sp, M_INFO, NOTFOUND);
507 break;
509 if (!O_ISSET(sp, O_WRAPSCAN)) {
510 if (LF_ISSET(SEARCH_MSG))
511 msgq(sp, M_INFO, SOFMSG);
512 break;
514 if (file_lline(sp, ep, &lno))
515 goto err;
516 if (lno == 0) {
517 if (LF_ISSET(SEARCH_MSG))
518 msgq(sp, M_INFO, EMPTYMSG);
519 break;
521 ++lno;
522 wrapped = 1;
523 continue;
526 if ((l = file_gline(sp, ep, lno, &len)) == NULL)
527 goto err;
529 /* Set the termination. */
530 match[0].rm_so = 0;
531 match[0].rm_eo = coff ? coff : len;
533 #if defined(DEBUG) && 0
534 TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
535 #endif
536 /* Search the line. */
537 eval = regexec(re, l, 1, match,
538 (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
539 if (eval == REG_NOMATCH)
540 continue;
541 if (eval != 0) {
542 re_error(sp, eval, re);
543 break;
546 /* Warn if wrapped. */
547 if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
548 msgq(sp, M_INFO, WRAPMSG);
550 if (delta) {
551 if (check_delta(sp, ep, delta, lno))
552 break;
553 rm->lno = delta + lno;
554 rm->cno = 0;
555 } else {
556 #if defined(DEBUG) && 0
557 TRACE(sp, "found: %qu to %qu\n",
558 match[0].rm_so, match[0].rm_eo);
559 #endif
561 * Find the last acceptable one in this line. This
562 * is really painful, we need a cleaner interface to
563 * regexec to make this possible.
565 for (;;) {
566 last = match[0].rm_so;
567 match[0].rm_so = match[0].rm_eo + 1;
568 if (match[0].rm_so >= len ||
569 coff && match[0].rm_so >= coff)
570 break;
571 match[0].rm_eo = coff ? coff : len;
572 eval = regexec(re, l, 1, match,
573 (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
574 REG_STARTEND);
575 if (eval == REG_NOMATCH)
576 break;
577 if (eval != 0) {
578 re_error(sp, eval, re);
579 goto err;
582 rm->lno = lno;
584 /* See comment in f_search(). */
585 if (!LF_ISSET(SEARCH_EOL) && last >= len)
586 rm->cno = len ? len - 1 : 0;
587 else
588 rm->cno = last;
590 rval = 0;
591 break;
594 /* Turn off busy message, interrupts. */
595 err: busy_off(sp);
597 if (F_ISSET(sp->gp, G_ISFROMTTY))
598 TEAR_DOWN_INTERRUPTS;
600 return (rval);
604 * re_conv --
605 * Convert vi's regular expressions into something that the
606 * the POSIX 1003.2 RE functions can handle.
608 * There are three conversions we make to make vi's RE's (specifically
609 * the global, search, and substitute patterns) work with POSIX RE's.
611 * 1: If O_MAGIC is not set, strip backslashes from the magic character
612 * set (.[]*~) that have them, and add them to the ones that don't.
613 * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
614 * from the last substitute command's replacement string. If O_MAGIC
615 * is set, it's the string "~".
616 * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
617 * new RE escapes.
620 re_conv(sp, ptrnp, replacedp)
621 SCR *sp;
622 char **ptrnp;
623 int *replacedp;
625 size_t blen, needlen;
626 int magic;
627 char *bp, *p, *t;
630 * First pass through, we figure out how much space we'll need.
631 * We do it in two passes, on the grounds that most of the time
632 * the user is doing a search and won't have magic characters.
633 * That way we can skip the malloc and memmove's.
635 for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p)
636 switch (*p) {
637 case '\\':
638 switch (*++p) {
639 case '<':
640 magic = 1;
641 needlen += sizeof(RE_WSTART);
642 break;
643 case '>':
644 magic = 1;
645 needlen += sizeof(RE_WSTOP);
646 break;
647 case '~':
648 if (!O_ISSET(sp, O_MAGIC)) {
649 magic = 1;
650 needlen += sp->repl_len;
652 break;
653 case '.':
654 case '[':
655 case ']':
656 case '*':
657 if (!O_ISSET(sp, O_MAGIC)) {
658 magic = 1;
659 needlen += 1;
661 break;
662 default:
663 needlen += 2;
665 break;
666 case '~':
667 if (O_ISSET(sp, O_MAGIC)) {
668 magic = 1;
669 needlen += sp->repl_len;
671 break;
672 case '.':
673 case '[':
674 case ']':
675 case '*':
676 if (!O_ISSET(sp, O_MAGIC)) {
677 magic = 1;
678 needlen += 2;
680 break;
681 default:
682 needlen += 1;
683 break;
686 if (!magic) {
687 *replacedp = 0;
688 return (0);
692 * Get enough memory to hold the final pattern.
694 * XXX
695 * It's nul-terminated, for now.
697 GET_SPACE_RET(sp, bp, blen, needlen + 1);
699 for (p = *ptrnp, t = bp; *p != '\0'; ++p)
700 switch (*p) {
701 case '\\':
702 switch (*++p) {
703 case '<':
704 memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1);
705 t += sizeof(RE_WSTART) - 1;
706 break;
707 case '>':
708 memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1);
709 t += sizeof(RE_WSTOP) - 1;
710 break;
711 case '~':
712 if (O_ISSET(sp, O_MAGIC))
713 *t++ = '~';
714 else {
715 memmove(t, sp->repl, sp->repl_len);
716 t += sp->repl_len;
718 break;
719 case '.':
720 case '[':
721 case ']':
722 case '*':
723 if (O_ISSET(sp, O_MAGIC))
724 *t++ = '\\';
725 *t++ = *p;
726 break;
727 default:
728 *t++ = '\\';
729 *t++ = *p;
731 break;
732 case '~':
733 if (O_ISSET(sp, O_MAGIC)) {
734 memmove(t, sp->repl, sp->repl_len);
735 t += sp->repl_len;
736 } else
737 *t++ = '~';
738 break;
739 case '.':
740 case '[':
741 case ']':
742 case '*':
743 if (!O_ISSET(sp, O_MAGIC))
744 *t++ = '\\';
745 *t++ = *p;
746 break;
747 default:
748 *t++ = *p;
749 break;
751 *t = '\0';
753 *ptrnp = bp;
754 *replacedp = 1;
755 return (0);
759 * get_delta --
760 * Get a line delta. The trickiness is that the delta can be pretty
761 * complicated, i.e. "+3-2+3++- ++" is allowed.
763 * !!!
764 * In historic vi, if you had a delta on a search pattern which was used as
765 * a motion command, the command became a line mode command regardless of the
766 * cursor positions. A fairly common trick is to use a delta of "+0" to make
767 * the command a line mode command. This is the only place that knows about
768 * delta's, so we set the return flag information here.
770 static int
771 get_delta(sp, dp, valp, flagp)
772 SCR *sp;
773 char **dp;
774 long *valp;
775 u_int *flagp;
777 char *p;
778 long val, tval;
780 for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) {
781 if (isblank(*p)) {
782 ++p;
783 continue;
785 if (*p == '+' || *p == '-') {
786 if (!isdigit(*(p + 1))) {
787 if (*p == '+') {
788 if (tval == LONG_MAX)
789 goto overflow;
790 ++tval;
791 } else {
792 if (tval == LONG_MIN)
793 goto underflow;
794 --tval;
796 ++p;
797 continue;
799 } else
800 if (!isdigit(*p))
801 break;
803 errno = 0;
804 val = strtol(p, &p, 10);
805 if (errno == ERANGE) {
806 if (val == LONG_MAX)
807 overflow: msgq(sp, M_ERR, "Delta value overflow.");
808 else if (val == LONG_MIN)
809 underflow: msgq(sp, M_ERR, "Delta value underflow.");
810 else
811 msgq(sp, M_SYSERR, NULL);
812 return (1);
814 if (val >= 0) {
815 if (LONG_MAX - val < tval)
816 goto overflow;
817 } else
818 if (-(LONG_MIN - tval) > val)
819 goto underflow;
820 tval += val;
822 *dp = p;
823 *valp = tval;
824 return (0);
828 * check_delta --
829 * Check a line delta to see if it's legal.
831 static int
832 check_delta(sp, ep, delta, lno)
833 SCR *sp;
834 EXF *ep;
835 long delta;
836 recno_t lno;
838 /* A delta can overflow a record number. */
839 if (delta < 0) {
840 if (lno < LONG_MAX && delta >= (long)lno) {
841 msgq(sp, M_ERR, "Search offset before line 1.");
842 return (1);
844 } else {
845 if (ULONG_MAX - lno < delta) {
846 msgq(sp, M_ERR, "Delta value overflow.");
847 return (1);
849 if (file_gline(sp, ep, lno + delta, NULL) == NULL) {
850 msgq(sp, M_ERR, "Search offset past end-of-file.");
851 return (1);
854 return (0);
858 * re_error --
859 * Report a regular expression error.
861 void
862 re_error(sp, errcode, preg)
863 SCR *sp;
864 int errcode;
865 regex_t *preg;
867 size_t s;
868 char *oe;
870 s = regerror(errcode, preg, "", 0);
871 if ((oe = malloc(s)) == NULL)
872 msgq(sp, M_SYSERR, NULL);
873 else {
874 (void)regerror(errcode, preg, oe, s);
875 msgq(sp, M_ERR, "RE error: %s", oe);
877 free(oe);