rework ARGS structures as part of ex parser rework
[nvi.git] / common / search.c
blobd5317e457fe9fdf5d79d592275e9e400ff385e2a
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.28 1993/11/18 16:16:07 bostic Exp $ (Berkeley) $Date: 1993/11/18 16:16:07 $";
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 || ptrn[1] == '\0') {
56 if (!F_ISSET(sp, S_SRE_SET)) {
57 noprev: msgq(sp, M_INFO, "No previous search pattern.");
58 return (1);
60 *rep = &sp->sre;
62 /* Empty patterns set the direction. */
63 if (LF_ISSET(SEARCH_SET)) {
64 F_SET(sp, S_SRE_SET);
65 sp->searchdir = dir;
66 sp->sre = **rep;
68 return (0);
71 re_flags = 0; /* Set RE flags. */
72 if (O_ISSET(sp, O_EXTENDED))
73 re_flags |= REG_EXTENDED;
74 if (O_ISSET(sp, O_IGNORECASE))
75 re_flags |= REG_ICASE;
77 if (LF_ISSET(SEARCH_PARSE)) { /* Parse the string. */
78 /* Set delimiter. */
79 delim = *ptrn++;
81 /* Find terminating delimiter, handling escaped delimiters. */
82 for (p = t = ptrn;;) {
83 if (p[0] == '\0' || p[0] == delim) {
84 if (p[0] == delim)
85 ++p;
86 *t = '\0';
87 break;
89 if (p[1] == delim && p[0] == '\\')
90 ++p;
91 *t++ = *p++;
95 * If characters after the terminating delimiter, it may
96 * be an error, or may be an offset. In either case, we
97 * return the end of the string, whatever it may be, or
98 * change the end pointer to reference a NULL. Don't just
99 * whack the string, in case it's text space.
101 if (*p) {
102 if (get_delta(sp, &p, deltap, flagp))
103 return (1);
104 if (*p && LF_ISSET(SEARCH_TERM)) {
105 msgq(sp, M_ERR,
106 "Characters after search string and/or delta.");
107 return (1);
109 if (epp != NULL)
110 *epp = p;
111 } else {
113 * STATIC: NEVER WRITTEN.
114 * Can't be const, because the normal case isn't.
116 static char ebuf[1];
117 if (epp != NULL)
118 *epp = ebuf;
121 /* If the pattern was empty, use the previous pattern. */
122 if (ptrn == NULL || *ptrn == '\0') {
123 if (!F_ISSET(sp, S_SRE_SET))
124 goto noprev;
125 *rep = &sp->sre;
126 return (0);
129 if (re_conv(sp, &ptrn, &replaced))
130 return (1);
131 } else if (LF_ISSET(SEARCH_TAG)) {
132 if (ctag_conv(sp, &ptrn, &replaced))
133 return (1);
134 re_flags &= ~(REG_EXTENDED | REG_ICASE);
137 /* Compile the RE. */
138 if (eval = regcomp(*rep, ptrn, re_flags))
139 re_error(sp, eval, *rep);
140 else if (LF_ISSET(SEARCH_SET)) {
141 F_SET(sp, S_SRE_SET);
142 sp->searchdir = dir;
143 sp->sre = **rep;
146 /* Free up any extra memory. */
147 if (replaced)
148 FREE_SPACE(sp, ptrn, 0);
149 return (eval);
153 * ctag_conv --
154 * Convert a tags search path into something that the POSIX
155 * 1003.2 RE functions can handle.
157 static int
158 ctag_conv(sp, ptrnp, replacedp)
159 SCR *sp;
160 char **ptrnp;
161 int *replacedp;
163 size_t blen, len;
164 int lastdollar;
165 char *bp, *p, *t;
167 *replacedp = 0;
169 len = strlen(p = *ptrnp);
171 /* Max memory usage is 2 times the length of the string. */
172 GET_SPACE(sp, bp, blen, len * 2);
174 t = bp;
176 /* The last charcter is a '/' or '?', we just strip it. */
177 if (p[len - 1] == '/' || p[len - 1] == '?')
178 p[len - 1] = '\0';
180 /* The next-to-last character is a '$', and it's magic. */
181 if (p[len - 2] == '$') {
182 lastdollar = 1;
183 p[len - 2] = '\0';
184 } else
185 lastdollar = 0;
187 /* The first character is a '/' or '?', we just strip it. */
188 if (p[0] == '/' || p[0] == '?')
189 ++p;
191 /* The second character is a '^', and it's magic. */
192 if (p[0] == '^')
193 *t++ = *p++;
196 * Escape every other magic character we can find, stripping the
197 * backslashes ctags inserts to escape the search delimiter
198 * characters.
200 while (p[0]) {
201 /* Ctags escapes the search delimiter characters. */
202 if (p[0] == '\\' && (p[1] == '/' || p[1] == '?'))
203 ++p;
204 else if (strchr("^.[]$*", p[0]))
205 *t++ = '\\';
206 *t++ = *p++;
208 if (lastdollar)
209 *t++ = '$';
210 *t++ = '\0';
212 *ptrnp = bp;
213 *replacedp = 1;
214 return (0);
218 * Search interrupts.
220 * ISIG turns on VINTR, VQUIT and VSUSP. We want VINTR to interrupt the
221 * search, so we install a handler. VQUIT is ignored by main() because
222 * nvi never wants to catch it. A handler for VSUSP should have been
223 * installed by the screen code.
225 #define SET_UP_INTERRUPTS { \
226 act.sa_handler = search_intr; \
227 sigemptyset(&act.sa_mask); \
228 act.sa_flags = 0; \
229 if (isig = !sigaction(SIGINT, &act, &oact)) { \
230 istate = F_ISSET(sp, S_INTERRUPTIBLE); \
231 F_CLR(sp, S_INTERRUPTED); \
232 F_SET(sp, S_INTERRUPTIBLE); \
233 if (tcgetattr(STDIN_FILENO, &term)) { \
234 msgq(sp, M_SYSERR, "tcgetattr"); \
235 return (1); \
237 nterm = term; \
238 nterm.c_lflag |= ISIG; \
239 if (tcsetattr(STDIN_FILENO, \
240 TCSANOW | TCSASOFT, &nterm)) { \
241 msgq(sp, M_SYSERR, "tcsetattr"); \
242 return (1); \
247 #define TEAR_DOWN_INTERRUPTS { \
248 if (isig) { \
249 if (sigaction(SIGINT, &oact, NULL)) \
250 msgq(sp, M_SYSERR, "signal"); \
251 if (tcsetattr(STDIN_FILENO, TCSANOW | TCSASOFT, &term)) \
252 msgq(sp, M_SYSERR, "tcsetattr"); \
253 F_CLR(sp, S_INTERRUPTED); \
254 if (!istate) \
255 F_CLR(sp, S_INTERRUPTIBLE); \
260 * search_intr --
261 * Set the interrupt bit in any screen that is interruptible.
263 * XXX
264 * In the future this may be a problem. The user should be able to move to
265 * another screen and keep typing while this runs. If so, and the user has
266 * more than one search/global (see ex/ex_global.c) running, it will be hard
267 * to decide which one to stop.
269 static void
270 search_intr(signo)
271 int signo;
273 SCR *sp;
275 for (sp = __global_list->dq.cqh_first;
276 sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
277 if (F_ISSET(sp, S_INTERRUPTIBLE))
278 F_SET(sp, S_INTERRUPTED);
281 #define EMPTYMSG "File empty; nothing to search."
282 #define EOFMSG "Reached end-of-file without finding the pattern."
283 #define NOTFOUND "Pattern not found."
284 #define SOFMSG "Reached top-of-file without finding the pattern."
285 #define WRAPMSG "Search wrapped."
288 f_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
289 SCR *sp;
290 EXF *ep;
291 MARK *fm, *rm;
292 char *ptrn, **eptrn;
293 u_int *flagp;
295 struct sigaction act, oact;
296 struct termios nterm, term;
297 regmatch_t match[1];
298 regex_t *re, lre;
299 recno_t lno;
300 size_t coff, len;
301 long delta;
302 u_int flags, istate;
303 int eval, isig, rval, wrapped;
304 char *l;
306 if (file_lline(sp, ep, &lno))
307 return (1);
308 flags = *flagp;
309 if (lno == 0) {
310 if (LF_ISSET(SEARCH_MSG))
311 msgq(sp, M_INFO, EMPTYMSG);
312 return (1);
315 re = &lre;
316 if (resetup(sp, &re, FORWARD, ptrn, eptrn, &delta, flagp))
317 return (1);
320 * Start searching immediately after the cursor. If at the end of the
321 * line, start searching on the next line. This is incompatible (read
322 * bug fix) with the historic vi -- searches for the '$' pattern never
323 * moved forward, and "-t foo" didn't work if "foo" was the first thing
324 * in the file.
326 if (LF_ISSET(SEARCH_FILE)) {
327 lno = 1;
328 coff = 0;
329 } else {
330 if ((l = file_gline(sp, ep, fm->lno, &len)) == NULL) {
331 GETLINE_ERR(sp, fm->lno);
332 return (1);
334 if (fm->cno + 1 >= len) {
335 if (fm->lno == lno) {
336 if (!O_ISSET(sp, O_WRAPSCAN)) {
337 if (LF_ISSET(SEARCH_MSG))
338 msgq(sp, M_INFO, EOFMSG);
339 return (1);
341 lno = 1;
342 } else
343 lno = fm->lno + 1;
344 coff = 0;
345 } else {
346 lno = fm->lno;
347 coff = fm->cno + 1;
352 * Set up busy message, interrupts.
354 * F_search is called from the ex_tagfirst() routine, which runs
355 * before the screen really exists. Make sure we don't step on
356 * anything.
358 if (sp->s_position != NULL)
359 busy_on(sp, 1, "Searching...");
361 if (F_ISSET(sp->gp, G_ISFROMTTY))
362 SET_UP_INTERRUPTS;
364 for (rval = 1, wrapped = 0;; ++lno, coff = 0) {
365 if (F_ISSET(sp, S_INTERRUPTED)) {
366 msgq(sp, M_INFO, "Interrupted.");
367 break;
369 if (wrapped && lno > fm->lno ||
370 (l = file_gline(sp, ep, lno, &len)) == NULL) {
371 if (wrapped) {
372 if (LF_ISSET(SEARCH_MSG))
373 msgq(sp, M_INFO, NOTFOUND);
374 break;
376 if (!O_ISSET(sp, O_WRAPSCAN)) {
377 if (LF_ISSET(SEARCH_MSG))
378 msgq(sp, M_INFO, EOFMSG);
379 break;
381 lno = 0;
382 wrapped = 1;
383 continue;
386 /* If already at EOL, just keep going. */
387 if (len && coff == len)
388 continue;
390 /* Set the termination. */
391 match[0].rm_so = coff;
392 match[0].rm_eo = len;
394 #if defined(DEBUG) && 0
395 TRACE(sp, "F search: %lu from %u to %u\n",
396 lno, coff, len ? len - 1 : len);
397 #endif
398 /* Search the line. */
399 eval = regexec(re, l, 1, match,
400 (match[0].rm_so == 0 ? 0 : REG_NOTBOL) | REG_STARTEND);
401 if (eval == REG_NOMATCH)
402 continue;
403 if (eval != 0) {
404 re_error(sp, eval, re);
405 break;
408 /* Warn if wrapped. */
409 if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
410 msgq(sp, M_INFO, WRAPMSG);
413 * If an offset, see if it's legal. It's possible to match
414 * past the end of the line with $, so check for that case.
416 if (delta) {
417 if (check_delta(sp, ep, delta, lno))
418 break;
419 rm->lno = delta + lno;
420 rm->cno = 0;
421 } else {
422 #if defined(DEBUG) && 0
423 TRACE(sp, "found: %qu to %qu\n",
424 match[0].rm_so, match[0].rm_eo);
425 #endif
426 rm->lno = lno;
427 rm->cno = match[0].rm_so;
430 * If a change command, it's possible to move beyond
431 * the end of a line. Historic vi generally got this
432 * wrong (try "c?$<cr>"). Not all that sure this gets
433 * it right, there are lots of strange cases.
435 if (!LF_ISSET(SEARCH_EOL) && rm->cno >= len)
436 rm->cno = len ? len - 1 : 0;
438 rval = 0;
439 break;
442 /* Turn off busy message, interrupts. */
443 if (sp->s_position != NULL)
444 busy_off(sp);
446 if (F_ISSET(sp->gp, G_ISFROMTTY))
447 TEAR_DOWN_INTERRUPTS;
449 return (rval);
453 b_search(sp, ep, fm, rm, ptrn, eptrn, flagp)
454 SCR *sp;
455 EXF *ep;
456 MARK *fm, *rm;
457 char *ptrn, **eptrn;
458 u_int *flagp;
460 struct sigaction act, oact;
461 struct termios nterm, term;
462 regmatch_t match[1];
463 regex_t *re, lre;
464 recno_t lno;
465 size_t coff, len, last;
466 long delta;
467 u_int flags, istate;
468 int eval, isig, rval, wrapped;
469 char *l;
471 if (file_lline(sp, ep, &lno))
472 return (1);
473 flags = *flagp;
474 if (lno == 0) {
475 if (LF_ISSET(SEARCH_MSG))
476 msgq(sp, M_INFO, EMPTYMSG);
477 return (1);
480 re = &lre;
481 if (resetup(sp, &re, BACKWARD, ptrn, eptrn, &delta, flagp))
482 return (1);
484 /* If in the first column, start searching on the previous line. */
485 if (fm->cno == 0) {
486 if (fm->lno == 1) {
487 if (!O_ISSET(sp, O_WRAPSCAN)) {
488 if (LF_ISSET(SEARCH_MSG))
489 msgq(sp, M_INFO, SOFMSG);
490 return (1);
492 } else
493 lno = fm->lno - 1;
494 } else
495 lno = fm->lno;
497 /* Turn on busy message, interrupts. */
498 busy_on(sp, 1, "Searching...");
500 if (F_ISSET(sp->gp, G_ISFROMTTY))
501 SET_UP_INTERRUPTS;
503 for (rval = 1, wrapped = 0, coff = fm->cno;; --lno, coff = 0) {
504 if (F_ISSET(sp, S_INTERRUPTED)) {
505 msgq(sp, M_INFO, "Interrupted.");
506 break;
508 if (wrapped && lno < fm->lno || lno == 0) {
509 if (wrapped) {
510 if (LF_ISSET(SEARCH_MSG))
511 msgq(sp, M_INFO, NOTFOUND);
512 break;
514 if (!O_ISSET(sp, O_WRAPSCAN)) {
515 if (LF_ISSET(SEARCH_MSG))
516 msgq(sp, M_INFO, SOFMSG);
517 break;
519 if (file_lline(sp, ep, &lno))
520 goto err;
521 if (lno == 0) {
522 if (LF_ISSET(SEARCH_MSG))
523 msgq(sp, M_INFO, EMPTYMSG);
524 break;
526 ++lno;
527 wrapped = 1;
528 continue;
531 if ((l = file_gline(sp, ep, lno, &len)) == NULL)
532 goto err;
534 /* Set the termination. */
535 match[0].rm_so = 0;
536 match[0].rm_eo = coff ? coff : len;
538 #if defined(DEBUG) && 0
539 TRACE(sp, "B search: %lu from 0 to %qu\n", lno, match[0].rm_eo);
540 #endif
541 /* Search the line. */
542 eval = regexec(re, l, 1, match,
543 (match[0].rm_eo == len ? 0 : REG_NOTEOL) | REG_STARTEND);
544 if (eval == REG_NOMATCH)
545 continue;
546 if (eval != 0) {
547 re_error(sp, eval, re);
548 break;
551 /* Warn if wrapped. */
552 if (wrapped && O_ISSET(sp, O_WARN) && LF_ISSET(SEARCH_MSG))
553 msgq(sp, M_INFO, WRAPMSG);
555 if (delta) {
556 if (check_delta(sp, ep, delta, lno))
557 break;
558 rm->lno = delta + lno;
559 rm->cno = 0;
560 } else {
561 #if defined(DEBUG) && 0
562 TRACE(sp, "found: %qu to %qu\n",
563 match[0].rm_so, match[0].rm_eo);
564 #endif
566 * Find the last acceptable one in this line. This
567 * is really painful, we need a cleaner interface to
568 * regexec to make this possible.
570 for (;;) {
571 last = match[0].rm_so;
572 match[0].rm_so = match[0].rm_eo + 1;
573 if (match[0].rm_so >= len ||
574 coff && match[0].rm_so >= coff)
575 break;
576 match[0].rm_eo = coff ? coff : len;
577 eval = regexec(re, l, 1, match,
578 (match[0].rm_so == 0 ? 0 : REG_NOTBOL) |
579 REG_STARTEND);
580 if (eval == REG_NOMATCH)
581 break;
582 if (eval != 0) {
583 re_error(sp, eval, re);
584 goto err;
587 rm->lno = lno;
589 /* See comment in f_search(). */
590 if (!LF_ISSET(SEARCH_EOL) && last >= len)
591 rm->cno = len ? len - 1 : 0;
592 else
593 rm->cno = last;
595 rval = 0;
596 break;
599 /* Turn off busy message, interrupts. */
600 err: busy_off(sp);
602 if (F_ISSET(sp->gp, G_ISFROMTTY))
603 TEAR_DOWN_INTERRUPTS;
605 return (rval);
609 * re_conv --
610 * Convert vi's regular expressions into something that the
611 * the POSIX 1003.2 RE functions can handle.
613 * There are three conversions we make to make vi's RE's (specifically
614 * the global, search, and substitute patterns) work with POSIX RE's.
616 * 1: If O_MAGIC is not set, strip backslashes from the magic character
617 * set (.[]*~) that have them, and add them to the ones that don't.
618 * 2: If O_MAGIC is not set, the string "\~" is replaced with the text
619 * from the last substitute command's replacement string. If O_MAGIC
620 * is set, it's the string "~".
621 * 3: The pattern \<ptrn\> does "word" searches, convert it to use the
622 * new RE escapes.
625 re_conv(sp, ptrnp, replacedp)
626 SCR *sp;
627 char **ptrnp;
628 int *replacedp;
630 size_t blen, needlen;
631 int magic;
632 char *bp, *p, *t;
635 * First pass through, we figure out how much space we'll need.
636 * We do it in two passes, on the grounds that most of the time
637 * the user is doing a search and won't have magic characters.
638 * That way we can skip the malloc and memmove's.
640 for (p = *ptrnp, magic = 0, needlen = 0; *p != '\0'; ++p)
641 switch (*p) {
642 case '\\':
643 switch (*++p) {
644 case '<':
645 magic = 1;
646 needlen += sizeof(RE_WSTART);
647 break;
648 case '>':
649 magic = 1;
650 needlen += sizeof(RE_WSTOP);
651 break;
652 case '~':
653 if (!O_ISSET(sp, O_MAGIC)) {
654 magic = 1;
655 needlen += sp->repl_len;
657 break;
658 case '.':
659 case '[':
660 case ']':
661 case '*':
662 if (!O_ISSET(sp, O_MAGIC)) {
663 magic = 1;
664 needlen += 1;
666 break;
667 default:
668 needlen += 2;
670 break;
671 case '~':
672 if (O_ISSET(sp, O_MAGIC)) {
673 magic = 1;
674 needlen += sp->repl_len;
676 break;
677 case '.':
678 case '[':
679 case ']':
680 case '*':
681 if (!O_ISSET(sp, O_MAGIC)) {
682 magic = 1;
683 needlen += 2;
685 break;
686 default:
687 needlen += 1;
688 break;
691 if (!magic) {
692 *replacedp = 0;
693 return (0);
697 * Get enough memory to hold the final pattern.
699 * XXX
700 * It's nul-terminated, for now.
702 GET_SPACE(sp, bp, blen, needlen + 1);
704 for (p = *ptrnp, t = bp; *p != '\0'; ++p)
705 switch (*p) {
706 case '\\':
707 switch (*++p) {
708 case '<':
709 memmove(t, RE_WSTART, sizeof(RE_WSTART) - 1);
710 t += sizeof(RE_WSTART) - 1;
711 break;
712 case '>':
713 memmove(t, RE_WSTOP, sizeof(RE_WSTOP) - 1);
714 t += sizeof(RE_WSTOP) - 1;
715 break;
716 case '~':
717 if (O_ISSET(sp, O_MAGIC))
718 *t++ = '~';
719 else {
720 memmove(t, sp->repl, sp->repl_len);
721 t += sp->repl_len;
723 break;
724 case '.':
725 case '[':
726 case ']':
727 case '*':
728 if (O_ISSET(sp, O_MAGIC))
729 *t++ = '\\';
730 *t++ = *p;
731 break;
732 default:
733 *t++ = '\\';
734 *t++ = *p;
736 break;
737 case '~':
738 if (O_ISSET(sp, O_MAGIC)) {
739 memmove(t, sp->repl, sp->repl_len);
740 t += sp->repl_len;
741 } else
742 *t++ = '~';
743 break;
744 case '.':
745 case '[':
746 case ']':
747 case '*':
748 if (!O_ISSET(sp, O_MAGIC))
749 *t++ = '\\';
750 *t++ = *p;
751 break;
752 default:
753 *t++ = *p;
754 break;
756 *t = '\0';
758 *ptrnp = bp;
759 *replacedp = 1;
760 return (0);
764 * get_delta --
765 * Get a line delta. The trickiness is that the delta can be pretty
766 * complicated, i.e. "+3-2+3++- ++" is allowed.
768 * !!!
769 * In historic vi, if you had a delta on a search pattern which was used as
770 * a motion command, the command became a line mode command regardless of the
771 * cursor positions. A fairly common trick is to use a delta of "+0" to make
772 * the command a line mode command. This is the only place that knows about
773 * delta's, so we set the return flag information here.
775 static int
776 get_delta(sp, dp, valp, flagp)
777 SCR *sp;
778 char **dp;
779 long *valp;
780 u_int *flagp;
782 char *p;
783 long val, tval;
785 for (tval = 0, p = *dp; *p != '\0'; *flagp |= SEARCH_DELTA) {
786 if (isblank(*p)) {
787 ++p;
788 continue;
790 if (*p == '+' || *p == '-') {
791 if (!isdigit(*(p + 1))) {
792 if (*p == '+') {
793 if (tval == LONG_MAX)
794 goto overflow;
795 ++tval;
796 } else {
797 if (tval == LONG_MIN)
798 goto underflow;
799 --tval;
801 ++p;
802 continue;
804 } else
805 if (!isdigit(*p))
806 break;
808 errno = 0;
809 val = strtol(p, &p, 10);
810 if (errno == ERANGE) {
811 if (val == LONG_MAX)
812 overflow: msgq(sp, M_ERR, "Delta value overflow.");
813 else if (val == LONG_MIN)
814 underflow: msgq(sp, M_ERR, "Delta value underflow.");
815 else
816 msgq(sp, M_SYSERR, NULL);
817 return (1);
819 if (val >= 0) {
820 if (LONG_MAX - val < tval)
821 goto overflow;
822 } else
823 if (-(LONG_MIN - tval) > val)
824 goto underflow;
825 tval += val;
827 *dp = p;
828 *valp = tval;
829 return (0);
833 * check_delta --
834 * Check a line delta to see if it's legal.
836 static int
837 check_delta(sp, ep, delta, lno)
838 SCR *sp;
839 EXF *ep;
840 long delta;
841 recno_t lno;
843 /* A delta can overflow a record number. */
844 if (delta < 0) {
845 if (lno < LONG_MAX && delta >= (long)lno) {
846 msgq(sp, M_ERR, "Search offset before line 1.");
847 return (1);
849 } else {
850 if (ULONG_MAX - lno < delta) {
851 msgq(sp, M_ERR, "Delta value overflow.");
852 return (1);
854 if (file_gline(sp, ep, lno + delta, NULL) == NULL) {
855 msgq(sp, M_ERR, "Search offset past end-of-file.");
856 return (1);
859 return (0);
863 * re_error --
864 * Report a regular expression error.
866 void
867 re_error(sp, errcode, preg)
868 SCR *sp;
869 int errcode;
870 regex_t *preg;
872 size_t s;
873 char *oe;
875 s = regerror(errcode, preg, "", 0);
876 if ((oe = malloc(s)) == NULL)
877 msgq(sp, M_SYSERR, NULL);
878 else {
879 (void)regerror(errcode, preg, oe, s);
880 msgq(sp, M_ERR, "RE error: %s", oe);
882 free(oe);