xref: /netbsd-src/distrib/utils/more/prim.c (revision 57920690e6a5f49ca814fd57c83389eaa084cf26)
1 /*	$NetBSD: prim.c,v 1.11 2020/09/29 02:58:51 msaitoh Exp $	*/
2 
3 /*
4  * Copyright (c) 1988 Mark Nudelman
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)prim.c	8.1 (Berkeley) 6/6/93";
37 #else
38 __RCSID("$NetBSD: prim.c,v 1.11 2020/09/29 02:58:51 msaitoh Exp $");
39 #endif
40 #endif /* not lint */
41 
42 /*
43  * Primitives for displaying the file on the screen.
44  */
45 
46 #include <sys/types.h>
47 #include <stdio.h>
48 #include <ctype.h>
49 #include <string.h>
50 
51 #include "less.h"
52 #include "extern.h"
53 
54 int back_scroll = -1;
55 int hit_eof;		/* keeps track of how many times we hit end of file */
56 int screen_trashed;
57 
58 static int squished;
59 
60 
61 static int match(char *, char *);
62 static int badmark __P((int));
63 
64 /*
65  * Check to see if the end of file is currently "displayed".
66  */
67 void
eof_check()68 eof_check()
69 /*###72 [cc] conflicting types for `eof_check'%%%*/
70 {
71 	off_t pos;
72 
73 	if (sigs)
74 		return;
75 	/*
76 	 * If the bottom line is empty, we are at EOF.
77 	 * If the bottom line ends at the file length,
78 	 * we must be just at EOF.
79 	 */
80 	pos = position(BOTTOM_PLUS_ONE);
81 	if (pos == NULL_POSITION || pos == ch_length())
82 		hit_eof++;
83 }
84 
85 /*
86  * If the screen is "squished", repaint it.
87  * "Squished" means the first displayed line is not at the top
88  * of the screen; this can happen when we display a short file
89  * for the first time.
90  */
91 void
squish_check()92 squish_check()
93 /*###95 [cc] conflicting types for `squish_check'%%%*/
94 {
95 	if (squished) {
96 		squished = 0;
97 		repaint();
98 	}
99 }
100 
101 /*
102  * Display n lines, scrolling forward, starting at position pos in the
103  * input file.  "only_last" means display only the last screenful if
104  * n > screen size.
105  */
106 void
forw(n,pos,only_last)107 forw(n, pos, only_last)
108 /*###109 [cc] conflicting types for `forw'%%%*/
109 	int n;
110 	off_t pos;
111 	int only_last;
112 {
113 	static int first_time = 1;
114 	int eof = 0, do_repaint;
115 
116 	squish_check();
117 
118 	/*
119 	 * do_repaint tells us not to display anything till the end,
120 	 * then just repaint the entire screen.
121 	 */
122 	do_repaint = (only_last && n > sc_height-1);
123 
124 	if (!do_repaint) {
125 		if (top_scroll && n >= sc_height - 1) {
126 			/*
127 			 * Start a new screen.
128 			 * {{ This is not really desirable if we happen
129 			 *    to hit eof in the middle of this screen,
130 			 *    but we don't yet know if that will happen. }}
131 			 */
132 			clear();
133 			home();
134 		} else {
135 			lower_left();
136 			clear_eol();
137 		}
138 
139 		/*
140 		 * This is not contiguous with what is currently displayed.
141 		 * Clear the screen image (position table) and start a new
142 		 * screen.
143 		 */
144 		if (pos != position(BOTTOM_PLUS_ONE)) {
145 			pos_clear();
146 			add_forw_pos(pos);
147 			if (top_scroll) {
148 				clear();
149 				home();
150 			} else if (!first_time)
151 				putstr("...skipping...\n");
152 		}
153 	}
154 
155 	for (short_file = 0; --n >= 0;) {
156 		/*
157 		 * Read the next line of input.
158 		 */
159 		pos = forw_line(pos);
160 		if (pos == NULL_POSITION) {
161 			/*
162 			 * end of file; copy the table if the file was
163 			 * too small for an entire screen.
164 			 */
165 			eof = 1;
166 			if (position(TOP) == NULL_POSITION) {
167 				copytable();
168 				if (!position(TOP))
169 					short_file = 1;
170 			}
171 			break;
172 		}
173 		/*
174 		 * Add the position of the next line to the position table.
175 		 * Display the current line on the screen.
176 		 */
177 		add_forw_pos(pos);
178 		if (do_repaint)
179 			continue;
180 		/*
181 		 * If this is the first screen displayed and we hit an early
182 		 * EOF (i.e. before the requested number of lines), we
183 		 * "squish" the display down at the bottom of the screen.
184 		 */
185 		if (first_time && line == NULL && !top_scroll) {
186 			squished = 1;
187 			continue;
188 		}
189 		put_line();
190 	}
191 
192 	if (eof && !sigs)
193 		hit_eof++;
194 	else
195 		eof_check();
196 	if (do_repaint)
197 		repaint();
198 	first_time = 0;
199 	(void) currline(BOTTOM);
200 }
201 
202 /*
203  * Display n lines, scrolling backward.
204  */
205 void
back(n,pos,only_last)206 back(n, pos, only_last)
207 /*###207 [cc] conflicting types for `back'%%%*/
208 	int n;
209 	off_t pos;
210 	int only_last;
211 {
212 	int do_repaint;
213 
214 	squish_check();
215 	do_repaint = (n > get_back_scroll() || (only_last && n > sc_height-1));
216 	hit_eof = 0;
217 	while (--n >= 0)
218 	{
219 		/*
220 		 * Get the previous line of input.
221 		 */
222 		pos = back_line(pos);
223 		if (pos == NULL_POSITION)
224 			break;
225 		/*
226 		 * Add the position of the previous line to the position table.
227 		 * Display the line on the screen.
228 		 */
229 		add_back_pos(pos);
230 		if (!do_repaint)
231 		{
232 			if (retain_below)
233 			{
234 				lower_left();
235 				clear_eol();
236 			}
237 			home();
238 			add_line();
239 			put_line();
240 		}
241 	}
242 
243 	eof_check();
244 	if (do_repaint)
245 		repaint();
246 	(void) currline(BOTTOM);
247 }
248 
249 /*
250  * Display n more lines, forward.
251  * Start just after the line currently displayed at the bottom of the screen.
252  */
253 void
forward(n,only_last)254 forward(n, only_last)
255 /*###254 [cc] conflicting types for `forward'%%%*/
256 	int n;
257 	int only_last;
258 {
259 	off_t pos;
260 
261 	if (hit_eof) {
262 		/*
263 		 * If we're trying to go forward from end-of-file,
264 		 * go on to the next file.
265 		 */
266 		next_file(1);
267 		return;
268 	}
269 
270 	pos = position(BOTTOM_PLUS_ONE);
271 	if (pos == NULL_POSITION)
272 	{
273 		hit_eof++;
274 		return;
275 	}
276 	forw(n, pos, only_last);
277 }
278 
279 /*
280  * Display n more lines, backward.
281  * Start just before the line currently displayed at the top of the screen.
282  */
283 void
backward(n,only_last)284 backward(n, only_last)
285 /*###283 [cc] conflicting types for `backward'%%%*/
286 	int n;
287 	int only_last;
288 {
289 	off_t pos;
290 
291 	pos = position(TOP);
292 	/*
293 	 * This will almost never happen, because the top line is almost
294 	 * never empty.
295 	 */
296 	if (pos == NULL_POSITION)
297 		return;
298 	back(n, pos, only_last);
299 }
300 
301 /*
302  * Repaint the screen, starting from a specified position.
303  */
304 void
prepaint(pos)305 prepaint(pos)
306 /*###303 [cc] conflicting types for `prepaint'%%%*/
307 	off_t pos;
308 {
309 	hit_eof = 0;
310 	forw(sc_height-1, pos, 0);
311 	screen_trashed = 0;
312 }
313 
314 /*
315  * Repaint the screen.
316  */
317 void
repaint()318 repaint()
319 /*###315 [cc] conflicting types for `repaint'%%%*/
320 {
321 	/*
322 	 * Start at the line currently at the top of the screen
323 	 * and redisplay the screen.
324 	 */
325 	prepaint(position(TOP));
326 }
327 
328 /*
329  * Jump to the end of the file.
330  * It is more convenient to paint the screen backward,
331  * from the end of the file toward the beginning.
332  */
333 void
jump_forw()334 jump_forw()
335 /*###330 [cc] conflicting types for `jump_forw'%%%*/
336 {
337 	off_t pos;
338 
339 	if (ch_end_seek())
340 	{
341 		error("Cannot seek to end of file");
342 		return;
343 	}
344 	lastmark();
345 	pos = ch_tell();
346 	clear();
347 	pos_clear();
348 	add_back_pos(pos);
349 	back(sc_height - 1, pos, 0);
350 }
351 
352 /*
353  * Jump to line n in the file.
354  */
355 void
jump_back(n)356 jump_back(n)
357 /*###351 [cc] conflicting types for `jump_back'%%%*/
358 	int n;
359 {
360 	int c, nlines;
361 
362 	/*
363 	 * This is done the slow way, by starting at the beginning
364 	 * of the file and counting newlines.
365 	 *
366 	 * {{ Now that we have line numbering (in linenum.c),
367 	 *    we could improve on this by starting at the
368 	 *    nearest known line rather than at the beginning. }}
369 	 */
370 	if (ch_seek((off_t)0)) {
371 		/*
372 		 * Probably a pipe with beginning of file no longer buffered.
373 		 * If he wants to go to line 1, we do the best we can,
374 		 * by going to the first line which is still buffered.
375 		 */
376 		if (n <= 1 && ch_beg_seek() == 0)
377 			jump_loc(ch_tell());
378 		error("Cannot get to beginning of file");
379 		return;
380 	}
381 
382 	/*
383 	 * Start counting lines.
384 	 */
385 	for (nlines = 1;  nlines < n;  nlines++)
386 		while ((c = ch_forw_get()) != '\n')
387 			if (c == EOI) {
388 				char message[40];
389 				(void)sprintf(message, "File has only %d lines",
390 				    nlines - 1);
391 				error(message);
392 				return;
393 			}
394 	jump_loc(ch_tell());
395 }
396 
397 /*
398  * Jump to a specified percentage into the file.
399  * This is a poor compensation for not being able to
400  * quickly jump to a specific line number.
401  */
402 void
jump_percent(percent)403 jump_percent(percent)
404 /*###397 [cc] conflicting types for `jump_percent'%%%*/
405 	int percent;
406 {
407 	off_t pos, len;
408 	int c;
409 
410 	/*
411 	 * Determine the position in the file
412 	 * (the specified percentage of the file's length).
413 	 */
414 	if ((len = ch_length()) == NULL_POSITION)
415 	{
416 		error("Don't know length of file");
417 		return;
418 	}
419 	pos = (percent * len) / 100;
420 
421 	/*
422 	 * Back up to the beginning of the line.
423 	 */
424 	if (ch_seek(pos) == 0)
425 	{
426 		while ((c = ch_back_get()) != '\n' && c != EOI)
427 			;
428 		if (c == '\n')
429 			(void) ch_forw_get();
430 		pos = ch_tell();
431 	}
432 	jump_loc(pos);
433 }
434 
435 /*
436  * Jump to a specified position in the file.
437  */
438 void
jump_loc(pos)439 jump_loc(pos)
440 /*###432 [cc] conflicting types for `jump_loc'%%%*/
441 	off_t pos;
442 {
443 	int nline;
444 	off_t tpos;
445 
446 	if ((nline = onscreen(pos)) >= 0) {
447 		/*
448 		 * The line is currently displayed.
449 		 * Just scroll there.
450 		 */
451 		forw(nline, position(BOTTOM_PLUS_ONE), 0);
452 		return;
453 	}
454 
455 	/*
456 	 * Line is not on screen.
457 	 * Seek to the desired location.
458 	 */
459 	if (ch_seek(pos)) {
460 		error("Cannot seek to that position");
461 		return;
462 	}
463 
464 	/*
465 	 * See if the desired line is BEFORE the currently displayed screen.
466 	 * If so, then move forward far enough so the line we're on will be
467 	 * at the bottom of the screen, in order to be able to call back()
468 	 * to make the screen scroll backwards & put the line at the top of
469 	 * the screen.
470 	 * {{ This seems inefficient, but it's not so bad,
471 	 *    since we can never move forward more than a
472 	 *    screenful before we stop to redraw the screen. }}
473 	 */
474 	tpos = position(TOP);
475 	if (tpos != NULL_POSITION && pos < tpos) {
476 		off_t npos = pos;
477 		/*
478 		 * Note that we can't forw_line() past tpos here,
479 		 * so there should be no EOI at this stage.
480 		 */
481 		for (nline = 0;  npos < tpos && nline < sc_height - 1;  nline++)
482 			npos = forw_line(npos);
483 
484 		if (npos < tpos) {
485 			/*
486 			 * More than a screenful back.
487 			 */
488 			lastmark();
489 			clear();
490 			pos_clear();
491 			add_back_pos(npos);
492 		}
493 
494 		/*
495 		 * Note that back() will repaint() if nline > back_scroll.
496 		 */
497 		back(nline, npos, 0);
498 		return;
499 	}
500 	/*
501 	 * Remember where we were; clear and paint the screen.
502 	 */
503 	lastmark();
504 	prepaint(pos);
505 }
506 
507 /*
508  * The table of marks.
509  * A mark is simply a position in the file.
510  */
511 #define	NMARKS		(27)		/* 26 for a-z plus one for quote */
512 #define	LASTMARK	(NMARKS-1)	/* For quote */
513 static off_t marks[NMARKS];
514 
515 /*
516  * Initialize the mark table to show no marks are set.
517  */
518 void
init_mark()519 init_mark()
520 /*###511 [cc] conflicting types for `init_mark'%%%*/
521 {
522 	int i;
523 
524 	for (i = 0;  i < NMARKS;  i++)
525 		marks[i] = NULL_POSITION;
526 }
527 
528 /*
529  * See if a mark letter is valid (between a and z).
530  */
531 static int
badmark(c)532 badmark(c)
533 	int c;
534 {
535 	if (c < 'a' || c > 'z')
536 	{
537 		error("Choose a letter between 'a' and 'z'");
538 		return (1);
539 	}
540 	return (0);
541 }
542 
543 /*
544  * Set a mark.
545  */
546 void
setmark(c)547 setmark(c)
548 /*###538 [cc] conflicting types for `setmark'%%%*/
549 	int c;
550 {
551 	if (badmark(c))
552 		return;
553 	marks[c-'a'] = position(TOP);
554 }
555 
556 void
lastmark()557 lastmark()
558 /*###547 [cc] conflicting types for `lastmark'%%%*/
559 {
560 	marks[LASTMARK] = position(TOP);
561 }
562 
563 /*
564  * Go to a previously set mark.
565  */
566 void
gomark(c)567 gomark(c)
568 /*###556 [cc] conflicting types for `gomark'%%%*/
569 	int c;
570 {
571 	off_t pos;
572 
573 	if (c == '\'') {
574 		pos = marks[LASTMARK];
575 		if (pos == NULL_POSITION)
576 			pos = 0;
577 	}
578 	else {
579 		if (badmark(c))
580 			return;
581 		pos = marks[c-'a'];
582 		if (pos == NULL_POSITION) {
583 			error("mark not set");
584 			return;
585 		}
586 	}
587 	jump_loc(pos);
588 }
589 
590 /*
591  * Get the backwards scroll limit.
592  * Must call this function instead of just using the value of
593  * back_scroll, because the default case depends on sc_height and
594  * top_scroll, as well as back_scroll.
595  */
596 int
get_back_scroll()597 get_back_scroll()
598 {
599 	if (back_scroll >= 0)
600 		return (back_scroll);
601 	if (top_scroll)
602 		return (sc_height - 2);
603 	return (sc_height - 1);
604 }
605 
606 /*
607  * Search for the n-th occurrence of a specified pattern,
608  * either forward or backward.
609  */
610 int
search(search_forward,pattern,n,wantmatch)611 search(search_forward, pattern, n, wantmatch)
612 	int search_forward;
613 	char *pattern;
614 	int n;
615 	int wantmatch;
616 {
617 	off_t pos, linepos;
618 	char *p;
619 	char *q;
620 	int linenum;
621 	int linematch;
622 #ifdef RECOMP
623 	char *re_comp();
624 	char *errmsg;
625 #else
626 #ifdef REGCMP
627 	char *regcmp();
628 	static char *cpattern = NULL;
629 #else
630 	static char lpbuf[100];
631 	static char *last_pattern = NULL;
632 #endif
633 #endif
634 
635 	/*
636 	 * For a caseless search, convert any uppercase in the pattern to
637 	 * lowercase.
638 	 */
639 	if (caseless && pattern != NULL)
640 		for (p = pattern;  *p;  p++)
641 			if (isupper((unsigned char)*p))
642 				*p = tolower((unsigned char)*p);
643 #ifdef RECOMP
644 
645 	/*
646 	 * (re_comp handles a null pattern internally,
647 	 *  so there is no need to check for a null pattern here.)
648 	 */
649 	if ((errmsg = re_comp(pattern)) != NULL)
650 	{
651 		error(errmsg);
652 		return(0);
653 	}
654 #else
655 #ifdef REGCMP
656 	if (pattern == NULL || *pattern == '\0')
657 	{
658 		/*
659 		 * A null pattern means use the previous pattern.
660 		 * The compiled previous pattern is in cpattern, so just use it.
661 		 */
662 		if (cpattern == NULL)
663 		{
664 			error("No previous regular expression");
665 			return(0);
666 		}
667 	} else
668 	{
669 		/*
670 		 * Otherwise compile the given pattern.
671 		 */
672 		char *s;
673 		if ((s = regcmp(pattern, 0)) == NULL)
674 		{
675 			error("Invalid pattern");
676 			return(0);
677 		}
678 		if (cpattern != NULL)
679 			free(cpattern);
680 		cpattern = s;
681 	}
682 #else
683 	if (pattern == NULL || *pattern == '\0')
684 	{
685 		/*
686 		 * Null pattern means use the previous pattern.
687 		 */
688 		if (last_pattern == NULL)
689 		{
690 			error("No previous regular expression");
691 			return(0);
692 		}
693 		pattern = last_pattern;
694 	} else
695 	{
696 		(void)strlcpy(lpbuf, pattern, sizeof(lpbuf));
697 		last_pattern = lpbuf;
698 	}
699 #endif
700 #endif
701 
702 	/*
703 	 * Figure out where to start the search.
704 	 */
705 
706 	if (position(TOP) == NULL_POSITION) {
707 		/*
708 		 * Nothing is currently displayed.  Start at the beginning
709 		 * of the file.  (This case is mainly for searches from the
710 		 * command line.
711 		 */
712 		pos = (off_t)0;
713 	} else if (!search_forward) {
714 		/*
715 		 * Backward search: start just before the top line
716 		 * displayed on the screen.
717 		 */
718 		pos = position(TOP);
719 	} else {
720 		/*
721 		 * Start at the second screen line displayed on the screen.
722 		 */
723 		pos = position(TOP_PLUS_ONE);
724 	}
725 
726 	if (pos == NULL_POSITION)
727 	{
728 		/*
729 		 * Can't find anyplace to start searching from.
730 		 */
731 		error("Nothing to search");
732 		return(0);
733 	}
734 
735 	linenum = find_linenum(pos);
736 	for (;;)
737 	{
738 		/*
739 		 * Get lines until we find a matching one or
740 		 * until we hit end-of-file (or beginning-of-file
741 		 * if we're going backwards).
742 		 */
743 		if (sigs)
744 			/*
745 			 * A signal aborts the search.
746 			 */
747 			return(0);
748 
749 		if (search_forward)
750 		{
751 			/*
752 			 * Read the next line, and save the
753 			 * starting position of that line in linepos.
754 			 */
755 			linepos = pos;
756 			pos = forw_raw_line(pos);
757 			if (linenum != 0)
758 				linenum++;
759 		} else
760 		{
761 			/*
762 			 * Read the previous line and save the
763 			 * starting position of that line in linepos.
764 			 */
765 			pos = back_raw_line(pos);
766 			linepos = pos;
767 			if (linenum != 0)
768 				linenum--;
769 		}
770 
771 		if (pos == NULL_POSITION)
772 		{
773 			/*
774 			 * We hit EOF/BOF without a match.
775 			 */
776 			error("Pattern not found");
777 			return(0);
778 		}
779 
780 		/*
781 		 * If we're using line numbers, we might as well
782 		 * remember the information we have now (the position
783 		 * and line number of the current line).
784 		 */
785 		if (linenums)
786 			add_lnum(linenum, pos);
787 
788 		/*
789 		 * If this is a caseless search, convert uppercase in the
790 		 * input line to lowercase.
791 		 */
792 		if (caseless)
793 			for (p = q = line;  *p;  p++, q++)
794 				*q = isupper((unsigned char)*p) ?
795 				    tolower((unsigned char)*p) : *p;
796 
797 		/*
798 		 * Remove any backspaces along with the preceding char.
799 		 * This allows us to match text which is underlined or
800 		 * overstruck.
801 		 */
802 		for (p = q = line;  *p;  p++, q++)
803 			if (q > line && *p == '\b')
804 				/* Delete BS and preceding char. */
805 				q -= 2;
806 			else
807 				/* Otherwise, just copy. */
808 				*q = *p;
809 
810 		/*
811 		 * Test the next line to see if we have a match.
812 		 * This is done in a variety of ways, depending
813 		 * on what pattern matching functions are available.
814 		 */
815 #ifdef REGCMP
816 		linematch = (regex(cpattern, line) != NULL);
817 #else
818 #ifdef RECOMP
819 		linematch = (re_exec(line) == 1);
820 #else
821 		linematch = match(pattern, line);
822 #endif
823 #endif
824 		/*
825 		 * We are successful if wantmatch and linematch are
826 		 * both true (want a match and got it),
827 		 * or both false (want a non-match and got it).
828 		 */
829 		if (((wantmatch && linematch) || (!wantmatch && !linematch)) &&
830 		      --n <= 0)
831 			/*
832 			 * Found the line.
833 			 */
834 			break;
835 	}
836 	jump_loc(linepos);
837 	return(1);
838 }
839 
840 #if !defined(REGCMP) && !defined(RECOMP)
841 /*
842  * We have neither regcmp() nor re_comp().
843  * We use this function to do simple pattern matching.
844  * It supports no metacharacters like *, etc.
845  */
846 static int
match(pattern,buf)847 match(pattern, buf)
848 	char *pattern, *buf;
849 {
850 	char *pp, *lp;
851 
852 	for ( ;  *buf != '\0';  buf++)
853 	{
854 		for (pp = pattern, lp = buf;  *pp == *lp;  pp++, lp++)
855 			if (*pp == '\0' || *lp == '\0')
856 				break;
857 		if (*pp == '\0')
858 			return (1);
859 	}
860 	return (0);
861 }
862 #endif
863