look.c revision 1.2 1 1.2 christos /* $NetBSD: look.c,v 1.2 2005/06/30 16:25:05 christos Exp $ */
2 1.1 perry
3 1.1 perry /* derived from: OpenBSD: look.c,v 1.3 2003/06/03 02:56:16 millert Exp */
4 1.1 perry
5 1.1 perry /*-
6 1.1 perry * Copyright (c) 1991, 1993
7 1.1 perry * The Regents of the University of California. All rights reserved.
8 1.1 perry *
9 1.1 perry * This code is derived from software contributed to Berkeley by
10 1.1 perry * David Hitz of Auspex Systems, Inc.
11 1.1 perry *
12 1.1 perry * Redistribution and use in source and binary forms, with or without
13 1.1 perry * modification, are permitted provided that the following conditions
14 1.1 perry * are met:
15 1.1 perry * 1. Redistributions of source code must retain the above copyright
16 1.1 perry * notice, this list of conditions and the following disclaimer.
17 1.1 perry * 2. Redistributions in binary form must reproduce the above copyright
18 1.1 perry * notice, this list of conditions and the following disclaimer in the
19 1.1 perry * documentation and/or other materials provided with the distribution.
20 1.1 perry * 3. Neither the name of the University nor the names of its contributors
21 1.1 perry * may be used to endorse or promote products derived from this software
22 1.1 perry * without specific prior written permission.
23 1.1 perry *
24 1.1 perry * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 1.1 perry * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 1.1 perry * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 1.1 perry * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 1.1 perry * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 1.1 perry * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 1.1 perry * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 1.1 perry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 1.1 perry * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 1.1 perry * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 1.1 perry * SUCH DAMAGE.
35 1.1 perry */
36 1.1 perry
37 1.1 perry #ifndef lint
38 1.1 perry #if 0
39 1.1 perry static const char sccsid[] = "@(#)look.c 8.2 (Berkeley) 5/4/95";
40 1.1 perry #endif
41 1.2 christos static const char rcsid[] = "$NetBSD: look.c,v 1.2 2005/06/30 16:25:05 christos Exp $";
42 1.1 perry #endif /* not lint */
43 1.1 perry
44 1.1 perry #include <sys/types.h>
45 1.1 perry #include <ctype.h>
46 1.1 perry #include <stdio.h>
47 1.1 perry #include <stdlib.h>
48 1.1 perry #include <string.h>
49 1.1 perry #include <err.h>
50 1.1 perry
51 1.2 christos #include "extern.h"
52 1.2 christos
53 1.2 christos static u_char *binary_search(u_char *, u_char *, u_char *);
54 1.2 christos static u_char *linear_search(u_char *, u_char *, u_char *);
55 1.2 christos static int compare(u_char *, u_char *, u_char *);
56 1.1 perry
57 1.1 perry int
58 1.1 perry look(u_char *string, u_char *front, u_char *back)
59 1.1 perry {
60 1.1 perry u_char *s;
61 1.1 perry
62 1.1 perry /* Convert string to lower case before searching. */
63 1.1 perry for (s = string; *s; s++) {
64 1.1 perry if (isupper(*s))
65 1.1 perry *s = _tolower(*s);
66 1.1 perry }
67 1.1 perry
68 1.1 perry front = binary_search(string, front, back);
69 1.1 perry front = linear_search(string, front, back);
70 1.1 perry
71 1.1 perry return (front != NULL);
72 1.1 perry }
73 1.1 perry
74 1.1 perry /*
75 1.1 perry * Binary search for "string" in memory between "front" and "back".
76 1.1 perry *
77 1.1 perry * This routine is expected to return a pointer to the start of a line at
78 1.1 perry * *or before* the first word matching "string". Relaxing the constraint
79 1.1 perry * this way simplifies the algorithm.
80 1.1 perry *
81 1.1 perry * Invariants:
82 1.1 perry * front points to the beginning of a line at or before the first
83 1.1 perry * matching string.
84 1.1 perry *
85 1.1 perry * back points to the beginning of a line at or after the first
86 1.1 perry * matching line.
87 1.1 perry *
88 1.1 perry * Base of the Invariants.
89 1.1 perry * front = NULL;
90 1.1 perry * back = EOF;
91 1.1 perry *
92 1.1 perry * Advancing the Invariants:
93 1.1 perry *
94 1.1 perry * p = first newline after halfway point from front to back.
95 1.1 perry *
96 1.1 perry * If the string at "p" is not greater than the string to match,
97 1.1 perry * p is the new front. Otherwise it is the new back.
98 1.1 perry *
99 1.1 perry * Termination:
100 1.1 perry *
101 1.1 perry * The definition of the routine allows it return at any point,
102 1.1 perry * since front is always at or before the line to print.
103 1.1 perry *
104 1.1 perry * In fact, it returns when the chosen "p" equals "back". This
105 1.1 perry * implies that there exists a string is least half as long as
106 1.1 perry * (back - front), which in turn implies that a linear search will
107 1.1 perry * be no more expensive than the cost of simply printing a string or two.
108 1.1 perry *
109 1.1 perry * Trying to continue with binary search at this point would be
110 1.1 perry * more trouble than it's worth.
111 1.1 perry */
112 1.1 perry #define SKIP_PAST_NEWLINE(p, back) \
113 1.2 christos while (p < back && *p++ != '\n') \
114 1.2 christos continue;
115 1.1 perry
116 1.2 christos static u_char *
117 1.1 perry binary_search(u_char *string, u_char *front, u_char *back)
118 1.1 perry {
119 1.1 perry u_char *p;
120 1.1 perry
121 1.1 perry p = front + (back - front) / 2;
122 1.1 perry SKIP_PAST_NEWLINE(p, back);
123 1.1 perry
124 1.1 perry /*
125 1.1 perry * If the file changes underneath us, make sure we don't
126 1.1 perry * infinitely loop.
127 1.1 perry */
128 1.1 perry while (p < back && back > front) {
129 1.1 perry if (compare(string, p, back) > 0)
130 1.1 perry front = p;
131 1.1 perry else
132 1.1 perry back = p;
133 1.1 perry p = front + (back - front) / 2;
134 1.1 perry SKIP_PAST_NEWLINE(p, back);
135 1.1 perry }
136 1.1 perry return (front);
137 1.1 perry }
138 1.1 perry
139 1.1 perry /*
140 1.1 perry * Find the first line that matches string, linearly searching from front
141 1.1 perry * to back.
142 1.1 perry *
143 1.1 perry * Return NULL for no such line.
144 1.1 perry *
145 1.1 perry * This routine assumes:
146 1.1 perry *
147 1.1 perry * o front points at the first character in a line.
148 1.1 perry * o front is before or at the first line to be printed.
149 1.1 perry */
150 1.2 christos static u_char *
151 1.1 perry linear_search(u_char *string, u_char *front, u_char *back)
152 1.1 perry {
153 1.1 perry int result;
154 1.1 perry
155 1.1 perry while (front < back) {
156 1.1 perry result = compare(string, front, back);
157 1.1 perry if (result == 0)
158 1.1 perry return (front); /* found it */
159 1.1 perry if (result < 0)
160 1.1 perry return (NULL); /* not there */
161 1.1 perry
162 1.1 perry SKIP_PAST_NEWLINE(front, back);
163 1.1 perry }
164 1.1 perry return (NULL);
165 1.1 perry }
166 1.1 perry
167 1.2 christos static int
168 1.1 perry compare(u_char *s1, u_char *s2, u_char *back)
169 1.1 perry {
170 1.1 perry int ch;
171 1.1 perry
172 1.1 perry /* Note that s1 is already upper case. */
173 1.1 perry for (;; ++s1, ++s2) {
174 1.1 perry if (*s2 == '\n' || s2 == back)
175 1.1 perry ch = '\0';
176 1.1 perry else if (isupper(*s2))
177 1.1 perry ch = _tolower(*s2);
178 1.1 perry else
179 1.1 perry ch = *s2;
180 1.1 perry if (*s1 != ch)
181 1.1 perry return (*s1 - ch);
182 1.1 perry if (ch == '\0')
183 1.1 perry return (0);
184 1.1 perry }
185 1.1 perry }
186