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