Upload Tizen:Base source
[framework/base/util-linux-ng.git] / misc-utils / look.c
1 /*-
2  * Copyright (c) 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * David Hitz of Auspex Systems, Inc.
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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37  /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
38   * - added Native Language Support
39   */
40
41 /*
42  * look -- find lines in a sorted list.
43  *
44  * The man page said that TABs and SPACEs participate in -d comparisons.
45  * In fact, they were ignored.  This implements historic practice, not
46  * the manual page.
47  */
48
49 #include <sys/types.h>
50 #include <sys/mman.h>
51 #include <sys/stat.h>
52
53 #include <limits.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <strings.h>
60 #include <ctype.h>
61 #include <getopt.h>
62 #include "pathnames.h"
63 #include "nls.h"
64
65 #define EQUAL           0
66 #define GREATER         1
67 #define LESS            (-1)
68
69 int dflag, fflag;
70 /* uglified the source a bit with globals, so that we only need
71    to allocate comparbuf once */
72 int stringlen;
73 char *string;
74 char *comparbuf;
75
76 static char *binary_search (char *, char *);
77 static int compare (char *, char *);
78 static void err (const char *fmt, ...);
79 static char *linear_search (char *, char *);
80 static int look (char *, char *);
81 static void print_from (char *, char *);
82 static void usage (void);
83
84 int
85 main(int argc, char *argv[])
86 {
87         struct stat sb;
88         int ch, fd, termchar;
89         char *back, *file, *front, *p;
90
91         setlocale(LC_ALL, "");
92         bindtextdomain(PACKAGE, LOCALEDIR);
93         textdomain(PACKAGE);
94
95         setlocale(LC_ALL, "");
96
97         file = _PATH_WORDS;
98         termchar = '\0';
99         string = NULL;          /* just for gcc */
100
101         while ((ch = getopt(argc, argv, "adft:")) != -1)
102                 switch(ch) {
103                 case 'a':
104                         file = _PATH_WORDS_ALT;
105                         break;
106                 case 'd':
107                         dflag = 1;
108                         break;
109                 case 'f':
110                         fflag = 1;
111                         break;
112                 case 't':
113                         termchar = *optarg;
114                         break;
115                 case '?':
116                 default:
117                         usage();
118                 }
119         argc -= optind;
120         argv += optind;
121
122         switch (argc) {
123         case 2:                         /* Don't set -df for user. */
124                 string = *argv++;
125                 file = *argv;
126                 break;
127         case 1:                         /* But set -df by default. */
128                 dflag = fflag = 1;
129                 string = *argv;
130                 break;
131         default:
132                 usage();
133         }
134
135         if (termchar != '\0' && (p = strchr(string, termchar)) != NULL)
136                 *++p = '\0';
137
138         if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
139                 err("%s: %s", file, strerror(errno));
140         front = mmap(NULL, (size_t) sb.st_size, PROT_READ,
141 #ifdef MAP_FILE
142                      MAP_FILE |
143 #endif
144                      MAP_SHARED, fd, (off_t) 0);
145         if
146 #ifdef MAP_FAILED
147            (front == MAP_FAILED)
148 #else
149            ((void *)(front) <= (void *)0)
150 #endif
151                 err("%s: %s", file, strerror(errno));
152
153 #if 0
154         /* workaround for mmap problem (rmiller@duskglow.com) */
155         if (front == (void *)0)
156                 return 1;
157 #endif
158
159         back = front + sb.st_size;
160         return look(front, back);
161 }
162
163 int
164 look(char *front, char *back)
165 {
166         int ch;
167         char *readp, *writep;
168
169         /* Reformat string string to avoid doing it multiple times later. */
170         if (dflag) {
171                 for (readp = writep = string; (ch = *readp++) != 0;) {
172                         if (isalnum(ch))
173                                 *(writep++) = ch;
174                 }
175                 *writep = '\0';
176                 stringlen = writep - string;
177         } else
178                 stringlen = strlen(string);
179
180         comparbuf = malloc(stringlen+1);
181         if (comparbuf == NULL)
182                 err(_("Out of memory"));
183
184         front = binary_search(front, back);
185         front = linear_search(front, back);
186
187         if (front)
188                 print_from(front, back);
189         return (front ? 0 : 1);
190 }
191
192
193 /*
194  * Binary search for "string" in memory between "front" and "back".
195  *
196  * This routine is expected to return a pointer to the start of a line at
197  * *or before* the first word matching "string".  Relaxing the constraint
198  * this way simplifies the algorithm.
199  *
200  * Invariants:
201  *      front points to the beginning of a line at or before the first
202  *      matching string.
203  *
204  *      back points to the beginning of a line at or after the first
205  *      matching line.
206  *
207  * Advancing the Invariants:
208  *
209  *      p = first newline after halfway point from front to back.
210  *
211  *      If the string at "p" is not greater than the string to match,
212  *      p is the new front.  Otherwise it is the new back.
213  *
214  * Termination:
215  *
216  *      The definition of the routine allows it return at any point,
217  *      since front is always at or before the line to print.
218  *
219  *      In fact, it returns when the chosen "p" equals "back".  This
220  *      implies that there exists a string is least half as long as
221  *      (back - front), which in turn implies that a linear search will
222  *      be no more expensive than the cost of simply printing a string or two.
223  *
224  *      Trying to continue with binary search at this point would be
225  *      more trouble than it's worth.
226  */
227 #define SKIP_PAST_NEWLINE(p, back) \
228         while (p < back && *p++ != '\n');
229
230 char *
231 binary_search(char *front, char *back)
232 {
233         char *p;
234
235         p = front + (back - front) / 2;
236         SKIP_PAST_NEWLINE(p, back);
237
238         /*
239          * If the file changes underneath us, make sure we don't
240          * infinitely loop.
241          */
242         while (p < back && back > front) {
243                 if (compare(p, back) == GREATER)
244                         front = p;
245                 else
246                         back = p;
247                 p = front + (back - front) / 2;
248                 SKIP_PAST_NEWLINE(p, back);
249         }
250         return (front);
251 }
252
253 /*
254  * Find the first line that starts with string, linearly searching from front
255  * to back.
256  *
257  * Return NULL for no such line.
258  *
259  * This routine assumes:
260  *
261  *      o front points at the first character in a line.
262  *      o front is before or at the first line to be printed.
263  */
264 char *
265 linear_search(char *front, char *back)
266 {
267         while (front < back) {
268                 switch (compare(front, back)) {
269                 case EQUAL:             /* Found it. */
270                         return (front);
271                         break;
272                 case LESS:              /* No such string. */
273                         return (NULL);
274                         break;
275                 case GREATER:           /* Keep going. */
276                         break;
277                 }
278                 SKIP_PAST_NEWLINE(front, back);
279         }
280         return (NULL);
281 }
282
283 /*
284  * Print as many lines as match string, starting at front.
285  */
286 void
287 print_from(char *front, char *back)
288 {
289         int eol;
290
291         while (front < back && compare(front, back) == EQUAL) {
292                 if (compare(front, back) == EQUAL) {
293                         eol = 0;
294                         while (front < back && !eol) {
295                                 if (putchar(*front) == EOF)
296                                         err("stdout: %s", strerror(errno));
297                                 if (*front++ == '\n')
298                                         eol = 1;
299                         }
300                 } else
301                         SKIP_PAST_NEWLINE(front, back);
302         }
303 }
304
305 /*
306  * Return LESS, GREATER, or EQUAL depending on how  string  compares with
307  * string2 (s1 ??? s2).
308  *
309  *      o Matches up to len(s1) are EQUAL.
310  *      o Matches up to len(s2) are GREATER.
311  *
312  * Compare understands about the -f and -d flags, and treats comparisons
313  * appropriately.
314  *
315  * The string "string" is null terminated.  The string "s2" is '\n' terminated
316  * (or "s2end" terminated).
317  *
318  * We use strcasecmp etc, since it knows how to ignore case also
319  * in other locales.
320  */
321 int
322 compare(char *s2, char *s2end) {
323         int i;
324         char *p;
325
326         /* copy, ignoring things that should be ignored */
327         p = comparbuf;
328         i = stringlen;
329         while(s2 < s2end && *s2 != '\n' && i) {
330                 if (!dflag || isalnum(*s2))
331                 {
332                         *p++ = *s2;
333                         i--;
334                 }
335                 s2++;
336         }
337         *p = 0;
338
339         /* and compare */
340         if (fflag)
341                 i = strncasecmp(comparbuf, string, stringlen);
342         else
343                 i = strncmp(comparbuf, string, stringlen);
344
345         return ((i > 0) ? LESS : (i < 0) ? GREATER : EQUAL);
346 }
347
348 static void
349 usage()
350 {
351         (void)fprintf(stderr, _("usage: look [-dfa] [-t char] string [file]\n"));
352         exit(2);
353 }
354
355 #if __STDC__
356 #include <stdarg.h>
357 #else
358 #include <varargs.h>
359 #endif
360
361 void
362 #if __STDC__
363 err(const char *fmt, ...)
364 #else
365 err(fmt, va_alist)
366         char *fmt;
367         va_dcl
368 #endif
369 {
370         va_list ap;
371 #if __STDC__
372         va_start(ap, fmt);
373 #else
374         va_start(ap);
375 #endif
376         (void)fprintf(stderr, "look: ");
377         (void)vfprintf(stderr, fmt, ap);
378         va_end(ap);
379         (void)fprintf(stderr, "\n");
380         exit(2);
381         /* NOTREACHED */
382 }