1 /* $NetBSD: look.c,v 1.13 2009/04/12 14:01:20 lukem Exp $ */
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * David Hitz of Auspex Systems, Inc.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/cdefs.h>
37 __COPYRIGHT("@(#) Copyright (c) 1991, 1993\
38 The Regents of the University of California. All rights reserved.");
43 static char sccsid
[] = "@(#)look.c 8.2 (Berkeley) 5/4/95";
45 __RCSID("$NetBSD: look.c,v 1.13 2009/04/12 14:01:20 lukem Exp $");
49 * look -- find lines in a sorted list.
51 * The man page said that TABs and SPACEs participate in -d comparisons.
52 * In fact, they were ignored. This implements historic practice, not
56 #include <sys/types.h>
70 #include "pathnames.h"
73 * FOLD and DICT convert characters to a normal form for comparison,
74 * according to the user specified flags.
76 * DICT expects integers because it uses a non-character value to
77 * indicate a character which should not participate in comparisons.
82 #define NO_COMPARE (-2)
84 #define FOLD(c) (isascii(c) && isupper(c) ? tolower(c) : (c))
85 #define DICT(c) (isascii(c) && isalnum(c) ? (c) : NO_COMPARE)
89 char *binary_search
__P((char *, char *, char *));
90 int compare
__P((char *, char *, char *));
91 char *linear_search
__P((char *, char *, char *));
92 int look
__P((char *, char *, char *));
93 int main
__P((int, char **));
94 void print_from
__P((char *, char *, char *));
95 void usage
__P((void));
103 int ch
, fd
, termchar
;
104 char *back
, *front
, *string
, *p
;
111 while ((ch
= getopt(argc
, argv
, "dft:")) != -1)
130 case 2: /* Don't set -df for user. */
134 case 1: /* But set -df by default. */
142 if (termchar
!= '\0' && (p
= strchr(string
, termchar
)) != NULL
)
145 if ((fd
= open(file
, O_RDONLY
, 0)) < 0 || fstat(fd
, &sb
))
147 len
= (size_t)sb
.st_size
;
148 if ((off_t
)len
!= sb
.st_size
) {
152 if ((front
= mmap(NULL
, len
,
153 PROT_READ
, MAP_FILE
|MAP_SHARED
, fd
, (off_t
)0)) == NULL
)
156 exit(look(string
, front
, back
));
160 look(string
, front
, back
)
161 char *string
, *front
, *back
;
164 char *readp
, *writep
;
166 /* Reformat string string to avoid doing it multiple times later. */
167 for (readp
= writep
= string
; (ch
= *readp
++) != 0; ) {
172 if (ch
!= NO_COMPARE
)
177 front
= binary_search(string
, front
, back
);
178 front
= linear_search(string
, front
, back
);
181 print_from(string
, front
, back
);
182 return (front
? 0 : 1);
187 * Binary search for "string" in memory between "front" and "back".
189 * This routine is expected to return a pointer to the start of a line at
190 * *or before* the first word matching "string". Relaxing the constraint
191 * this way simplifies the algorithm.
194 * front points to the beginning of a line at or before the first
197 * back points to the beginning of a line at or after the first
200 * Base of the Invariants.
204 * Advancing the Invariants:
206 * p = first newline after halfway point from front to back.
208 * If the string at "p" is not greater than the string to match,
209 * p is the new front. Otherwise it is the new back.
213 * The definition of the routine allows it return at any point,
214 * since front is always at or before the line to print.
216 * In fact, it returns when the chosen "p" equals "back". This
217 * implies that there exists a string is least half as long as
218 * (back - front), which in turn implies that a linear search will
219 * be no more expensive than the cost of simply printing a string or two.
221 * Trying to continue with binary search at this point would be
222 * more trouble than it's worth.
224 #define SKIP_PAST_NEWLINE(p, back) \
225 while (p < back && *p++ != '\n');
228 binary_search(string
, front
, back
)
229 char *string
, *front
, *back
;
233 p
= front
+ (back
- front
) / 2;
234 SKIP_PAST_NEWLINE(p
, back
);
237 * If the file changes underneath us, make sure we don't
240 while (p
< back
&& back
> front
) {
241 if (compare(string
, p
, back
) == GREATER
)
245 p
= front
+ (back
- front
) / 2;
246 SKIP_PAST_NEWLINE(p
, back
);
252 * Find the first line that starts with string, linearly searching from front
255 * Return NULL for no such line.
257 * This routine assumes:
259 * o front points at the first character in a line.
260 * o front is before or at the first line to be printed.
263 linear_search(string
, front
, back
)
264 char *string
, *front
, *back
;
266 while (front
< back
) {
267 switch (compare(string
, front
, back
)) {
268 case EQUAL
: /* Found it. */
271 case LESS
: /* No such string. */
274 case GREATER
: /* Keep going. */
277 SKIP_PAST_NEWLINE(front
, back
);
283 * Print as many lines as match string, starting at front.
286 print_from(string
, front
, back
)
287 char *string
, *front
, *back
;
289 for (; front
< back
&& compare(string
, front
, back
) == EQUAL
; ++front
) {
290 for (; front
< back
&& *front
!= '\n'; ++front
)
291 if (putchar(*front
) == EOF
)
293 if (putchar('\n') == EOF
)
299 * Return LESS, GREATER, or EQUAL depending on how the string1 compares with
300 * string2 (s1 ??? s2).
302 * o Matches up to len(s1) are EQUAL.
303 * o Matches up to len(s2) are GREATER.
305 * Compare understands about the -f and -d flags, and treats comparisons
308 * The string "s1" is null terminated. The string s2 is '\n' terminated (or
309 * "back" terminated).
312 compare(s1
, s2
, back
)
313 char *s1
, *s2
, *back
;
317 for (; *s1
&& s2
< back
&& *s2
!= '\n'; ++s1
, ++s2
) {
324 if (ch
== NO_COMPARE
) {
325 ++s2
; /* Ignore character in comparison. */
329 return (*s1
< ch
? LESS
: GREATER
);
331 return (*s1
? GREATER
: EQUAL
);
337 (void)fprintf(stderr
, "usage: look [-df] [-t char] string [file]\n");