GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/search.c Lines: 0 391 0.0 %
Date: 2017-11-07 Branches: 0 232 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: search.c,v 1.46 2017/08/06 04:39:45 bcallah Exp $	*/
2
3
/* This file is in the public domain. */
4
5
/*
6
 *		Search commands.
7
 * The functions in this file implement the search commands (both plain and
8
 * incremental searches are supported) and the query-replace command.
9
 *
10
 * The plain old search code is part of the original MicroEMACS "distribution".
11
 * The incremental search code and the query-replace code is by Rich Ellison.
12
 */
13
14
#include <sys/queue.h>
15
#include <ctype.h>
16
#include <signal.h>
17
#include <stdio.h>
18
#include <string.h>
19
20
#include "def.h"
21
#include "macro.h"
22
23
#define SRCH_BEGIN	(0)	/* Search sub-codes.	 */
24
#define SRCH_FORW	(-1)
25
#define SRCH_BACK	(-2)
26
#define SRCH_NOPR	(-3)
27
#define SRCH_ACCM	(-4)
28
#define SRCH_MARK	(-5)
29
30
struct srchcom {
31
	int		 s_code;
32
	struct line	*s_dotp;
33
	int		 s_doto;
34
	int		 s_dotline;
35
};
36
37
static int	isearch(int);
38
static void	is_cpush(int);
39
static void	is_lpush(void);
40
static void	is_pop(void);
41
static int	is_peek(void);
42
static void	is_undo(int *, int *);
43
static int	is_find(int);
44
static void	is_prompt(int, int, int);
45
static void	is_dspl(char *, int);
46
static int	eq(int, int, int);
47
48
static struct srchcom	cmds[NSRCH];
49
static int	cip;
50
51
int		srch_lastdir = SRCH_NOPR;	/* Last search flags.	 */
52
53
/*
54
 * Search forward.  Get a search string from the user, and search for it
55
 * starting at ".".  If found, "." gets moved to just after the matched
56
 * characters, and display does all the hard stuff.  If not found, it just
57
 * prints a message.
58
 */
59
/* ARGSUSED */
60
int
61
forwsearch(int f, int n)
62
{
63
	int	s;
64
65
	if ((s = readpattern("Search")) != TRUE)
66
		return (s);
67
	if (forwsrch() == FALSE) {
68
		dobeep();
69
		ewprintf("Search failed: \"%s\"", pat);
70
		return (FALSE);
71
	}
72
	srch_lastdir = SRCH_FORW;
73
	return (TRUE);
74
}
75
76
/*
77
 * Reverse search.  Get a search string from the user, and search, starting
78
 * at "." and proceeding toward the front of the buffer.  If found "." is
79
 * left pointing at the first character of the pattern [the last character
80
 * that was matched].
81
 */
82
/* ARGSUSED */
83
int
84
backsearch(int f, int n)
85
{
86
	int	s;
87
88
	if ((s = readpattern("Search backward")) != TRUE)
89
		return (s);
90
	if (backsrch() == FALSE) {
91
		dobeep();
92
		ewprintf("Search failed: \"%s\"", pat);
93
		return (FALSE);
94
	}
95
	srch_lastdir = SRCH_BACK;
96
	return (TRUE);
97
}
98
99
/*
100
 * Search again, using the same search string and direction as the last
101
 * search command. The direction has been saved in "srch_lastdir", so you
102
 * know which way to go.
103
 */
104
/* ARGSUSED */
105
int
106
searchagain(int f, int n)
107
{
108
	if (srch_lastdir == SRCH_FORW) {
109
		if (forwsrch() == FALSE) {
110
			dobeep();
111
			ewprintf("Search failed: \"%s\"", pat);
112
			return (FALSE);
113
		}
114
		return (TRUE);
115
	}
116
	if (srch_lastdir == SRCH_BACK) {
117
		if (backsrch() == FALSE) {
118
			dobeep();
119
			ewprintf("Search failed: \"%s\"", pat);
120
			return (FALSE);
121
		}
122
		return (TRUE);
123
	}
124
	dobeep();
125
	ewprintf("No last search");
126
	return (FALSE);
127
}
128
129
/*
130
 * Use incremental searching, initially in the forward direction.
131
 * isearch ignores any explicit arguments.
132
 */
133
/* ARGSUSED */
134
int
135
forwisearch(int f, int n)
136
{
137
	if (macrodef || inmacro)
138
		/* We can't isearch in macro. Use search instead */
139
		return (forwsearch(f,n));
140
	else
141
		return (isearch(SRCH_FORW));
142
}
143
144
/*
145
 * Use incremental searching, initially in the reverse direction.
146
 * isearch ignores any explicit arguments.
147
 */
148
/* ARGSUSED */
149
int
150
backisearch(int f, int n)
151
{
152
	if (macrodef || inmacro)
153
		/* We can't isearch in macro. Use search instead */
154
		return (backsearch(f,n));
155
	else
156
		return (isearch(SRCH_BACK));
157
}
158
159
/*
160
 * Incremental Search.
161
 *	dir is used as the initial direction to search.
162
 *	^S	switch direction to forward
163
 *	^R	switch direction to reverse
164
 *	^Q	quote next character (allows searching for ^N etc.)
165
 *	<ESC>	exit from Isearch
166
 *	<DEL>	undoes last character typed. (tricky job to do this correctly).
167
 *	other ^ exit search, don't set mark
168
 *	else	accumulate into search string
169
 */
170
static int
171
isearch(int dir)
172
{
173
	struct line	*clp;		/* Saved line pointer */
174
	int		 c;
175
	int		 cbo;		/* Saved offset */
176
	int		 success;
177
	int		 pptr;
178
	int		 firstc;
179
	int		 xcase;
180
	int		 i;
181
	char		 opat[NPAT];
182
	int		 cdotline;	/* Saved line number */
183
184
	if (macrodef) {
185
		dobeep();
186
		ewprintf("Can't isearch in macro");
187
		return (FALSE);
188
	}
189
	for (cip = 0; cip < NSRCH; cip++)
190
		cmds[cip].s_code = SRCH_NOPR;
191
192
	(void)strlcpy(opat, pat, sizeof(opat));
193
	cip = 0;
194
	pptr = -1;
195
	clp = curwp->w_dotp;
196
	cbo = curwp->w_doto;
197
	cdotline = curwp->w_dotline;
198
	is_lpush();
199
	is_cpush(SRCH_BEGIN);
200
	success = TRUE;
201
	is_prompt(dir, TRUE, success);
202
203
	for (;;) {
204
		update(CMODE);
205
206
		switch (c = getkey(FALSE)) {
207
		case CCHR('['):
208
			/*
209
			 * If new characters come in the next 300 msec,
210
			 * we can assume that they belong to a longer
211
			 * escaped sequence so we should ungetkey the
212
			 * ESC to avoid writing out garbage.
213
			 */
214
			if (ttwait(300) == FALSE)
215
				ungetkey(c);
216
			srch_lastdir = dir;
217
			curwp->w_markp = clp;
218
			curwp->w_marko = cbo;
219
			curwp->w_markline = cdotline;
220
			ewprintf("Mark set");
221
			return (TRUE);
222
		case CCHR('G'):
223
			if (success != TRUE) {
224
				while (is_peek() == SRCH_ACCM)
225
					is_undo(&pptr, &dir);
226
				success = TRUE;
227
				is_prompt(dir, pptr < 0, success);
228
				break;
229
			}
230
			curwp->w_dotp = clp;
231
			curwp->w_doto = cbo;
232
			curwp->w_dotline = cdotline;
233
			curwp->w_rflag |= WFMOVE;
234
			srch_lastdir = dir;
235
			(void)ctrlg(FFRAND, 0);
236
			(void)strlcpy(pat, opat, sizeof(pat));
237
			return (ABORT);
238
		case CCHR('S'):
239
			if (dir == SRCH_BACK) {
240
				dir = SRCH_FORW;
241
				is_lpush();
242
				is_cpush(SRCH_FORW);
243
				success = TRUE;
244
			}
245
			if (success == FALSE && dir == SRCH_FORW) {
246
				/* wrap the search to beginning */
247
				curwp->w_dotp = bfirstlp(curbp);
248
				curwp->w_doto = 0;
249
				curwp->w_dotline = 1;
250
				if (is_find(dir) != FALSE) {
251
					is_cpush(SRCH_MARK);
252
					success = TRUE;
253
				}
254
				ewprintf("Overwrapped I-search: %s", pat);
255
				break;
256
			}
257
			is_lpush();
258
			pptr = strlen(pat);
259
			if (forwchar(FFRAND, 1) == FALSE) {
260
                                dobeep();
261
                                success = FALSE;
262
                                ewprintf("Failed I-search: %s", pat);
263
			} else {
264
				if (is_find(SRCH_FORW) != FALSE)
265
					is_cpush(SRCH_MARK);
266
				else {
267
					(void)backchar(FFRAND, 1);
268
					dobeep();
269
					success = FALSE;
270
					ewprintf("Failed I-search: %s", pat);
271
				}
272
			}
273
			is_prompt(dir, pptr < 0, success);
274
			break;
275
		case CCHR('R'):
276
			if (dir == SRCH_FORW) {
277
				dir = SRCH_BACK;
278
				is_lpush();
279
				is_cpush(SRCH_BACK);
280
				success = TRUE;
281
			}
282
			if (success == FALSE && dir == SRCH_BACK) {
283
				/* wrap the search to end */
284
				curwp->w_dotp = blastlp(curbp);
285
				curwp->w_doto = llength(curwp->w_dotp);
286
				curwp->w_dotline = curwp->w_bufp->b_lines;
287
				if (is_find(dir) != FALSE) {
288
					is_cpush(SRCH_MARK);
289
					success = TRUE;
290
				}
291
				ewprintf("Overwrapped I-search: %s", pat);
292
				break;
293
			}
294
			is_lpush();
295
			pptr = strlen(pat);
296
                        if (backchar(FFRAND, 1) == FALSE) {
297
                                dobeep();
298
                                success = FALSE;
299
                        } else {
300
				if (is_find(SRCH_BACK) != FALSE)
301
					is_cpush(SRCH_MARK);
302
				else {
303
					(void)forwchar(FFRAND, 1);
304
					dobeep();
305
					success = FALSE;
306
				}
307
			}
308
			is_prompt(dir, pptr < 0, success);
309
			break;
310
		case CCHR('W'):
311
			/* add the rest of the current word to the pattern */
312
			clp = curwp->w_dotp;
313
			cbo = curwp->w_doto;
314
			firstc = 1;
315
			if (pptr == -1)
316
				pptr = 0;
317
			if (dir == SRCH_BACK) {
318
				/* when isearching backwards, cbo is the start of the pattern */
319
				cbo += pptr;
320
			}
321
322
			/* if the search is case insensitive, add to pattern using lowercase */
323
			xcase = 0;
324
			for (i = 0; pat[i]; i++)
325
				if (ISUPPER(CHARMASK(pat[i])))
326
					xcase = 1;
327
328
			while (cbo < llength(clp)) {
329
				c = lgetc(clp, cbo++);
330
				if ((!firstc && !isalnum(c)))
331
					break;
332
333
				if (pptr == NPAT - 1) {
334
					dobeep();
335
					break;
336
				}
337
				firstc = 0;
338
				if (!xcase && ISUPPER(c))
339
					c = TOLOWER(c);
340
341
				pat[pptr++] = c;
342
				pat[pptr] = '\0';
343
				/* cursor only moves when isearching forwards */
344
				if (dir == SRCH_FORW) {
345
					curwp->w_doto = cbo;
346
					curwp->w_rflag |= WFMOVE;
347
					update(CMODE);
348
				}
349
			}
350
			is_prompt(dir, pptr < 0, success);
351
			break;
352
		case CCHR('H'):
353
		case CCHR('?'):
354
			is_undo(&pptr, &dir);
355
			if (is_peek() != SRCH_ACCM)
356
				success = TRUE;
357
			is_prompt(dir, pptr < 0, success);
358
			break;
359
		case CCHR('\\'):
360
		case CCHR('Q'):
361
			c = (char)getkey(FALSE);
362
			goto addchar;
363
		case CCHR('M'):
364
			c = CCHR('J');
365
			goto addchar;
366
		default:
367
			if (ISCTRL(c)) {
368
				ungetkey(c);
369
				curwp->w_markp = clp;
370
				curwp->w_marko = cbo;
371
				curwp->w_markline = cdotline;
372
				ewprintf("Mark set");
373
				curwp->w_rflag |= WFMOVE;
374
				return (TRUE);
375
			}
376
			/* FALLTHRU */
377
		case CCHR('I'):
378
		case CCHR('J'):
379
	addchar:
380
			if (pptr == -1)
381
				pptr = 0;
382
			if (pptr == 0)
383
				success = TRUE;
384
			if (pptr == NPAT - 1)
385
				dobeep();
386
			else {
387
				pat[pptr++] = c;
388
				pat[pptr] = '\0';
389
			}
390
			is_lpush();
391
			if (success != FALSE) {
392
				if (is_find(dir) != FALSE)
393
					is_cpush(c);
394
				else {
395
					success = FALSE;
396
					dobeep();
397
					is_cpush(SRCH_ACCM);
398
				}
399
			} else
400
				is_cpush(SRCH_ACCM);
401
			is_prompt(dir, FALSE, success);
402
		}
403
	}
404
	/* NOTREACHED */
405
}
406
407
static void
408
is_cpush(int cmd)
409
{
410
	if (++cip >= NSRCH)
411
		cip = 0;
412
	cmds[cip].s_code = cmd;
413
}
414
415
static void
416
is_lpush(void)
417
{
418
	int	ctp;
419
420
	ctp = cip + 1;
421
	if (ctp >= NSRCH)
422
		ctp = 0;
423
	cmds[ctp].s_code = SRCH_NOPR;
424
	cmds[ctp].s_doto = curwp->w_doto;
425
	cmds[ctp].s_dotp = curwp->w_dotp;
426
	cmds[ctp].s_dotline = curwp->w_dotline;
427
}
428
429
static void
430
is_pop(void)
431
{
432
	if (cmds[cip].s_code != SRCH_NOPR) {
433
		curwp->w_doto = cmds[cip].s_doto;
434
		curwp->w_dotp = cmds[cip].s_dotp;
435
		curwp->w_dotline = cmds[cip].s_dotline;
436
		curwp->w_rflag |= WFMOVE;
437
		cmds[cip].s_code = SRCH_NOPR;
438
	}
439
	if (--cip <= 0)
440
		cip = NSRCH - 1;
441
}
442
443
static int
444
is_peek(void)
445
{
446
	return (cmds[cip].s_code);
447
}
448
449
/* this used to always return TRUE (the return value was checked) */
450
static void
451
is_undo(int *pptr, int *dir)
452
{
453
	int	redo = FALSE;
454
455
	switch (cmds[cip].s_code) {
456
	case SRCH_BEGIN:
457
	case SRCH_NOPR:
458
		*pptr = -1;
459
		break;
460
	case SRCH_MARK:
461
		break;
462
	case SRCH_FORW:
463
		*dir = SRCH_BACK;
464
		redo = TRUE;
465
		break;
466
	case SRCH_BACK:
467
		*dir = SRCH_FORW;
468
		redo = TRUE;
469
		break;
470
	case SRCH_ACCM:
471
	default:
472
		*pptr -= 1;
473
		if (*pptr < 0)
474
			*pptr = 0;
475
		pat[*pptr] = '\0';
476
		break;
477
	}
478
	is_pop();
479
	if (redo)
480
		is_undo(pptr, dir);
481
}
482
483
static int
484
is_find(int dir)
485
{
486
	int	 plen, odoto, odotline;
487
	struct line	*odotp;
488
489
	odoto = curwp->w_doto;
490
	odotp = curwp->w_dotp;
491
	odotline = curwp->w_dotline;
492
	plen = strlen(pat);
493
	if (plen != 0) {
494
		if (dir == SRCH_FORW) {
495
			(void)backchar(FFARG | FFRAND, plen);
496
			if (forwsrch() == FALSE) {
497
				curwp->w_doto = odoto;
498
				curwp->w_dotp = odotp;
499
				curwp->w_dotline = odotline;
500
				return (FALSE);
501
			}
502
			return (TRUE);
503
		}
504
		if (dir == SRCH_BACK) {
505
			(void)forwchar(FFARG | FFRAND, plen);
506
			if (backsrch() == FALSE) {
507
				curwp->w_doto = odoto;
508
				curwp->w_dotp = odotp;
509
				curwp->w_dotline = odotline;
510
				return (FALSE);
511
			}
512
			return (TRUE);
513
		}
514
		dobeep();
515
		ewprintf("bad call to is_find");
516
		return (FALSE);
517
	}
518
	return (FALSE);
519
}
520
521
/*
522
 * If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used
523
 * to print an error message.  It also used to return TRUE or FALSE, depending
524
 * on if it liked the "dir".  However, none of the callers looked at the
525
 * status, so I just made the checking vanish.
526
 */
527
static void
528
is_prompt(int dir, int flag, int success)
529
{
530
	if (dir == SRCH_FORW) {
531
		if (success != FALSE)
532
			is_dspl("I-search", flag);
533
		else
534
			is_dspl("Failing I-search", flag);
535
	} else if (dir == SRCH_BACK) {
536
		if (success != FALSE)
537
			is_dspl("I-search backward", flag);
538
		else
539
			is_dspl("Failing I-search backward", flag);
540
	} else
541
		ewprintf("Broken call to is_prompt");
542
}
543
544
/*
545
 * Prompt writing routine for the incremental search.  The "i_prompt" is just
546
 * a string. The "flag" determines whether pat should be printed.
547
 */
548
static void
549
is_dspl(char *i_prompt, int flag)
550
{
551
	if (flag != FALSE)
552
		ewprintf("%s: ", i_prompt);
553
	else
554
		ewprintf("%s: %s", i_prompt, pat);
555
}
556
557
/*
558
 * Query Replace.
559
 *	Replace strings selectively.  Does a search and replace operation.
560
 */
561
/* ARGSUSED */
562
int
563
queryrepl(int f, int n)
564
{
565
	int	s;
566
	int	rcnt = 0;		/* replacements made so far	*/
567
	int	plen;			/* length of found string	*/
568
	char	news[NPAT], *rep;	/* replacement string		*/
569
570
	if (macrodef) {
571
		dobeep();
572
		ewprintf("Can't query replace in macro");
573
		return (FALSE);
574
	}
575
576
	if ((s = readpattern("Query replace")) != TRUE)
577
		return (s);
578
	if ((rep = eread("Query replace %s with: ", news, NPAT,
579
	    EFNUL | EFNEW | EFCR, pat)) == NULL)
580
		return (ABORT);
581
	else if (rep[0] == '\0')
582
		news[0] = '\0';
583
	ewprintf("Query replacing %s with %s:", pat, news);
584
	plen = strlen(pat);
585
586
	/*
587
	 * Search forward repeatedly, checking each time whether to insert
588
	 * or not.  The "!" case makes the check always true, so it gets put
589
	 * into a tighter loop for efficiency.
590
	 */
591
	while (forwsrch() == TRUE) {
592
retry:
593
		update(CMODE);
594
		switch (getkey(FALSE)) {
595
		case 'y':
596
		case ' ':
597
			if (lreplace((RSIZE)plen, news) == FALSE)
598
				return (FALSE);
599
			rcnt++;
600
			break;
601
		case '.':
602
			if (lreplace((RSIZE)plen, news) == FALSE)
603
				return (FALSE);
604
			rcnt++;
605
			goto stopsearch;
606
		/* ^G, CR or ESC */
607
		case CCHR('G'):
608
			(void)ctrlg(FFRAND, 0);
609
			goto stopsearch;
610
		case CCHR('['):
611
		case CCHR('M'):
612
			goto stopsearch;
613
		case '!':
614
			do {
615
				if (lreplace((RSIZE)plen, news) == FALSE)
616
					return (FALSE);
617
				rcnt++;
618
			} while (forwsrch() == TRUE);
619
			goto stopsearch;
620
		case 'n':
621
		case CCHR('H'):
622
		/* To not replace */
623
		case CCHR('?'):
624
			break;
625
		default:
626
			ewprintf("y/n or <SP>/<DEL>: replace/don't, [.] repl-end, [!] repl-rest, <CR>/<ESC> quit");
627
			goto retry;
628
		}
629
	}
630
stopsearch:
631
	curwp->w_rflag |= WFFULL;
632
	update(CMODE);
633
	if (rcnt == 1)
634
		ewprintf("Replaced 1 occurrence");
635
	else
636
		ewprintf("Replaced %d occurrences", rcnt);
637
	return (TRUE);
638
}
639
640
/*
641
 * Replace string globally without individual prompting.
642
 */
643
/* ARGSUSED */
644
int
645
replstr(int f, int n)
646
{
647
	char	news[NPAT];
648
	int	s, plen, rcnt = 0;
649
	char	*r;
650
651
	if ((s = readpattern("Replace string")) != TRUE)
652
		return s;
653
654
	r = eread("Replace string %s with: ", news, NPAT,
655
	    EFNUL | EFNEW | EFCR,  pat);
656
	if (r == NULL)
657
		 return (ABORT);
658
659
	plen = strlen(pat);
660
	while (forwsrch() == TRUE) {
661
		update(CMODE);
662
		if (lreplace((RSIZE)plen, news) == FALSE)
663
			return (FALSE);
664
665
		rcnt++;
666
	}
667
668
	curwp->w_rflag |= WFFULL;
669
	update(CMODE);
670
671
	if (rcnt == 1)
672
		ewprintf("Replaced 1 occurrence");
673
	else
674
		ewprintf("Replaced %d occurrences", rcnt);
675
676
	return (TRUE);
677
}
678
679
/*
680
 * This routine does the real work of a forward search.  The pattern is sitting
681
 * in the external variable "pat".  If found, dot is updated, the window system
682
 * is notified of the change, and TRUE is returned.  If the string isn't found,
683
 * FALSE is returned.
684
 */
685
int
686
forwsrch(void)
687
{
688
	struct line	*clp, *tlp;
689
	int	 cbo, tbo, c, i, xcase = 0;
690
	char	*pp;
691
	int	 nline;
692
693
	clp = curwp->w_dotp;
694
	cbo = curwp->w_doto;
695
	nline = curwp->w_dotline;
696
	for (i = 0; pat[i]; i++)
697
		if (ISUPPER(CHARMASK(pat[i])))
698
			xcase = 1;
699
	for (;;) {
700
		if (cbo == llength(clp)) {
701
			if ((clp = lforw(clp)) == curbp->b_headp)
702
				break;
703
			nline++;
704
			cbo = 0;
705
			c = CCHR('J');
706
		} else
707
			c = lgetc(clp, cbo++);
708
		if (eq(c, pat[0], xcase) != FALSE) {
709
			tlp = clp;
710
			tbo = cbo;
711
			pp = &pat[1];
712
			while (*pp != 0) {
713
				if (tbo == llength(tlp)) {
714
					tlp = lforw(tlp);
715
					if (tlp == curbp->b_headp)
716
						goto fail;
717
					tbo = 0;
718
					c = CCHR('J');
719
					if (eq(c, *pp++, xcase) == FALSE)
720
						goto fail;
721
					nline++;
722
				} else {
723
					c = lgetc(tlp, tbo++);
724
					if (eq(c, *pp++, xcase) == FALSE)
725
						goto fail;
726
				}
727
			}
728
			curwp->w_dotp = tlp;
729
			curwp->w_doto = tbo;
730
			curwp->w_dotline = nline;
731
			curwp->w_rflag |= WFMOVE;
732
			return (TRUE);
733
		}
734
fail:		;
735
	}
736
	return (FALSE);
737
}
738
739
/*
740
 * This routine does the real work of a backward search.  The pattern is
741
 * sitting in the external variable "pat".  If found, dot is updated, the
742
 * window system is notified of the change, and TRUE is returned.  If the
743
 * string isn't found, FALSE is returned.
744
 */
745
int
746
backsrch(void)
747
{
748
	struct line	*clp, *tlp;
749
	int	 cbo, tbo, c, i, xcase = 0;
750
	char	*epp, *pp;
751
	int	 nline, pline;
752
753
	for (epp = &pat[0]; epp[1] != 0; ++epp);
754
	clp = curwp->w_dotp;
755
	cbo = curwp->w_doto;
756
	nline = curwp->w_dotline;
757
	for (i = 0; pat[i]; i++)
758
		if (ISUPPER(CHARMASK(pat[i])))
759
			xcase = 1;
760
	for (;;) {
761
		if (cbo == 0) {
762
			clp = lback(clp);
763
			if (clp == curbp->b_headp)
764
				return (FALSE);
765
			nline--;
766
			cbo = llength(clp) + 1;
767
		}
768
		if (--cbo == llength(clp))
769
			c = CCHR('J');
770
		else
771
			c = lgetc(clp, cbo);
772
		if (eq(c, *epp, xcase) != FALSE) {
773
			tlp = clp;
774
			tbo = cbo;
775
			pp = epp;
776
			pline = nline;
777
			while (pp != &pat[0]) {
778
				if (tbo == 0) {
779
					tlp = lback(tlp);
780
					if (tlp == curbp->b_headp)
781
						goto fail;
782
					nline--;
783
					tbo = llength(tlp) + 1;
784
				}
785
				if (--tbo == llength(tlp))
786
					c = CCHR('J');
787
				else
788
					c = lgetc(tlp, tbo);
789
				if (eq(c, *--pp, xcase) == FALSE) {
790
					nline = pline;
791
					goto fail;
792
				}
793
			}
794
			curwp->w_dotp = tlp;
795
			curwp->w_doto = tbo;
796
			curwp->w_dotline = nline;
797
			curwp->w_rflag |= WFMOVE;
798
			return (TRUE);
799
		}
800
fail:		;
801
	}
802
	/* NOTREACHED */
803
}
804
805
/*
806
 * Compare two characters.  The "bc" comes from the buffer.  It has its case
807
 * folded out. The "pc" is from the pattern.
808
 */
809
static int
810
eq(int bc, int pc, int xcase)
811
{
812
	bc = CHARMASK(bc);
813
	pc = CHARMASK(pc);
814
	if (bc == pc)
815
		return (TRUE);
816
	if (xcase)
817
		return (FALSE);
818
	if (ISUPPER(bc))
819
		return (TOLOWER(bc) == pc);
820
	if (ISUPPER(pc))
821
		return (bc == TOLOWER(pc));
822
	return (FALSE);
823
}
824
825
/*
826
 * Read a pattern.  Stash it in the external variable "pat".  The "pat" is not
827
 * updated if the user types in an empty line.  If the user typed an empty
828
 * line, and there is no old pattern, it is an error.  Display the old pattern,
829
 * in the style of Jeff Lomicka.  There is some do-it-yourself control
830
 * expansion.
831
 */
832
int
833
readpattern(char *r_prompt)
834
{
835
	char	tpat[NPAT], *rep;
836
	int	retval;
837
838
	if (pat[0] == '\0')
839
		rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, r_prompt);
840
	else
841
		rep = eread("%s (default %s): ", tpat, NPAT,
842
		    EFNUL | EFNEW | EFCR, r_prompt, pat);
843
844
	/* specified */
845
	if (rep == NULL) {
846
		retval = ABORT;
847
	} else if (rep[0] != '\0') {
848
		(void)strlcpy(pat, tpat, sizeof(pat));
849
		retval = TRUE;
850
	} else if (pat[0] != '\0') {
851
		retval = TRUE;
852
	} else
853
		retval = FALSE;
854
	return (retval);
855
}