123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- /* Find the lines in a sorted file that start with a given string.
- Copyright (C) 1986 Richard Stallman
- NO WARRANTY
- BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
- NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT
- WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
- RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
- WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
- BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
- AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
- DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
- CORRECTION.
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
- STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
- WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
- LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
- OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
- USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
- DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
- A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
- PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
- DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
- GENERAL PUBLIC LICENSE TO COPY
- 1. You may copy and distribute verbatim copies of this source file
- as you receive it, in any medium, provided that you conspicuously
- and appropriately publish on each copy a valid copyright notice
- "Copyright (C) 1986 Richard M. Stallman"; and include following the
- copyright notice a verbatim copy of the above disclaimer of warranty
- and of this License.
- 2. You may modify your copy or copies of this source file or
- any portion of it, and copy and distribute such modifications under
- the terms of Paragraph 1 above, provided that you also do the following:
- a) cause the modified files to carry prominent notices stating
- that you changed the files and the date of any change; and
- b) cause the whole of any work that you distribute or publish,
- that in whole or in part contains or is a derivative of this
- program or any part thereof, to be freely distributed
- and licensed to all third parties on terms identical to those
- contained in this License Agreement (except that you may choose
- to grant more extensive warranty protection to third parties,
- at your option).
- 3. You may copy and distribute this program or any portion of it in
- compiled, executable or object code form under the terms of Paragraphs
- 1 and 2 above provided that you do the following:
- a) cause each such copy to be accompanied by the
- corresponding machine-readable source code, which must
- be distributed under the terms of Paragraphs 1 and 2 above; or,
- b) cause each such copy to be accompanied by a
- written offer, with no time limit, to give any third party
- free (except for a nominal shipping charge) a machine readable
- copy of the corresponding source code, to be distributed
- under the terms of Paragraphs 1 and 2 above; or,
- c) in the case of a recipient of this program in compiled, executable
- or object code form (without the corresponding source code) you
- shall cause copies you distribute to be accompanied by a copy
- of the written offer of source code which you received along
- with the copy you received.
- 4. You may not copy, sublicense, distribute or transfer this program
- except as expressly provided under this License Agreement. Any attempt
- otherwise to copy, sublicense, distribute or transfer this program is void and
- your rights to use the program under this License agreement shall be
- automatically terminated. However, parties who have received computer
- software programs from you with this License Agreement will not have
- their licenses terminated so long as such parties remain in full compliance.
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding! */
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- /* Nonzero means ignore characters other than letters, digits,
- tabs and spaces in both comparison and sorting */
- int dictionary_order;
- /* Nonzero means ignore case in comparing against `key'
- and also assume the file is sorted ignoring case. */
- int ignore_case;
- /* The string to be searched for (an argument) */
- char *key;
- /* Name of file to search, or 0 to use standard input. */
- char *default_file = "/usr/dict/words"; /* File used if none specd */
- char *filename;
- #define BUFSIZE 512
- char buffer[BUFSIZE];
- /* File address of the data now in buffer */
- long bufpos;
- /* Number of characters, starting at bufpos, now in buffer */
- int bufchars;
- /* Forward declarations */
- char *concat ();
- void fillbuf ();
- void read_before ();
- long trypos ();
- long findline ();
- void copylines ();
- main (argc, argv)
- int argc;
- char **argv;
- {
- FILE *stream;
- int desc;
- long pos;
- if (argc < 2)
- {
- fatal ("usage is look [-df] string [file]", 0);
- }
- if (argv[1][0] == '-')
- {
- char *p = argv[1] + 1;
- char c;
- if (argc < 3)
- fatal ("usage is look [-df] string [file]", 0);
- while (c = *p++)
- switch (c)
- {
- case 'd':
- dictionary_order = 1;
- break;
- case 'f':
- ignore_case = 1;
- break;
- default:
- fatal ("unknown switch %c", c);
- }
- key = argv[2];
- filename = argv[3]; /* Which is zero if there is no file */
- }
- else
- {
- key = argv[1];
- filename = argv[2]; /* which is zero if there is no file */
- }
- if (!filename)
- {
- filename = default_file;
- ignore_case = 1;
- dictionary_order = 1;
- }
- desc = open (filename, 0, 0);
- bufpos = -1; /* Indicate nothing buffered in core currently */
- /* Find position in file of first line that matches. */
- pos = findline (desc, key);
- if (pos < 0) exit (0); /* Give up if didn't find key */
- /* Now switch to ordinary stream input to read the matching lines */
- stream = fdopen (desc, "r");
- fseek (stream, pos, 0);
- /* Now copy out lines as long as they continue to match */
- copylines (stream, key);
- fclose (stream);
- }
- /* Binary search in the file for a line matching `key'. */
- long
- findline (desc, key)
- int desc;
- char *key;
- {
- long start;
- long end;
- long searchpoint;
- struct stat status;
- fstat (desc, &status); /* Read file status; in particular, the length */
- start = 0; /* Initialize binary search endpoints */
- end = status.st_size;
- searchpoint = 0; /* The meaning of searchpoint is this: */
- /* We already know no newline exists */
- /* between start and searchpoint. */
- do
- {
- long middle_line_start;
- long middle_line_searchpoint;
- int flag;
- int order;
- /* try to find a line starting between start and end.
- Since we know no line starts between start and searchpoint,
- it is ok to look for one between searchpoint and end. */
- flag = trybetween (desc, searchpoint, end,
- &middle_line_start, &middle_line_searchpoint);
- /* If region remaining is only one line, that's the one. */
- if (flag < 0) break;
- /* Compare this line with the key and decide following action */
- order = compare (desc, middle_line_start, key);
- if (order >= 0)
- {
- /* middle_line is beyond where we want */
- end = middle_line_start;
- }
- else
- {
- /* Where we want is beyond middle_line */
- start = middle_line_start;
- searchpoint = middle_line_searchpoint;
- }
- }
- while (searchpoint != end);
- /* It can happen that the line at `start' matches the key.
- This happens if the file's first line matches. */
- if (!compare (desc, start, key))
- return start;
- /* More often, the line at `end' is the one that we wanted .
- This is because, when we hit an exact match above,
- we set `end' to point at the matching line. */
- if (!compare (desc, end, key))
- return end;
- /* File has no match for `key'. */
- return -1;
- }
- /* Refill the buffer with data starting at `pos'. */
- void
- fillbuf (desc, pos)
- int desc;
- long pos;
- {
- bufpos = pos;
- lseek (desc, pos, 0);
- bufchars = read (desc, buffer, BUFSIZE);
- }
- /* Fill buffer with data up to position `pos'. */
- void
- read_before (desc, pos)
- int desc;
- long pos;
- {
- if (pos >= BUFSIZE)
- fillbuf (desc, pos - BUFSIZE);
- else
- fillbuf (desc, 0L);
- }
- /* Take a stab in the file at position `pos', and find
- the beginning of the line containing that position,
- or else return -1 if no start-of-line is found later than `start'.
- `start' should be less than `pos'. */
- long
- trypos (desc, start, pos)
- long pos;
- long start;
- int desc;
- {
- long try = pos;
- while (try > start)
- {
- char *p, *pstart;
- if (try <= bufpos || try > bufpos + bufchars)
- if (start > try - BUFSIZE)
- fillbuf (desc, start);
- else
- read_before (desc, try);
- if (start > bufpos)
- pstart = buffer + start - bufpos;
- else
- pstart = buffer;
- p = buffer + try - bufpos;
- while (p != pstart)
- if (*--p == '\n')
- return bufpos + (p - buffer) + 1;
- if (start >= bufpos)
- return -1;
- }
- return -1;
- }
- /* Find a line in the file starting between start and end, if possible.
- Try to find one midway between, but any line whose start is between is ok.
- If one is found, the value returned is positive
- and *lineposptr is set to the line's starting index in the file
- and *searchposptr is set to an index in the file at which we
- started searching back for the line's start.
- If no line is found, -1 is returned.
- This implies that there is no newline from start to end.
- The utility of *searchposptr is that the caller knows that
- no newlines exist between *lineposptr and there.
- This can sometimes be used to prevent searching that region
- over again on the next iteration of the binary search. */
- int
- trybetween (desc, start, end, lineposptr, searchposptr)
- int desc;
- long start;
- long end;
- long *lineposptr;
- long *searchposptr;
- {
- long guess = start;
- while (1)
- {
- long searchpoint = guess;
- long tem;
- guess = (guess + end + 1) / 2;
- if (guess == end) return -1;
- tem = trypos (desc, searchpoint, guess);
- if (tem >= 0)
- {
- *lineposptr = tem;
- *searchposptr = guess;
- return 0;
- }
- }
- }
- /* Compare the line starting at `pos' in the file with the string `key'.
- Assumes that the beginning of the line is in the buffer.
- Return 0 if they match, 1 if line is later, -1 if key is later. */
- int
- compare (desc, pos, key)
- int desc;
- long pos;
- char *key;
- {
- char *p = key;
- char *p1 = buffer + pos - bufpos;
- char *pe = buffer + bufchars;
- char ck = 1; /* Next char of key to compare */
- char cf; /* Next char of file to compare */
- while (ck)
- {
- int c;
- ck = *p++;
- if (dictionary_order)
- {
- while (ck && ck != ' ' && ck != '\t'
- && (ck < '0' || ck > '9')
- && (ck < 'a' || ck > 'z')
- && (ck < 'A' || ck > 'Z'))
- ck = *p++;
- }
- /* If beginning of line exactly matches the key, return "match".
- The binary search does not need to distinguish this case from an
- exact match, since it treats an exact match just like
- a line that is greater than the key.
- The final test for having found the key regards a zero value
- as meaning it was found. */
- if (!ck) return 0;
- if (p1 == pe)
- {
- fillbuf (desc, bufpos + bufchars);
- p1 = buffer;
- pe = buffer + bufchars;
- }
- if (p1 == pe) cf = 0;
- else cf = *p1++;
- if (dictionary_order)
- {
- while (cf && cf != '\n' && cf != ' ' && cf != '\t'
- && (cf < '0' || cf > '9')
- && (cf < 'a' || cf > 'z')
- && (cf < 'A' || cf > 'Z'))
- {
- if (p1 == pe)
- {
- fillbuf (desc, bufpos + bufchars);
- p1 = buffer;
- pe = buffer + bufchars;
- }
- if (p1 == pe) cf = 0;
- else cf = *p1++;
- }
- }
- if (ignore_case)
- {
- if (ck >= 'A' && ck <= 'Z')
- ck += 'a' - 'A';
- if (cf >= 'A' && cf <= 'Z')
- cf += 'a' - 'A';
- }
- if (cf == '\n') cf = 0; /* Treat newline as 0 for comparison */
- if (c = cf - ck)
- {
- if (c > 0) return 1;
- return -1;
- }
- }
- return 0;
- }
- /* Copy lines out of the stdio stream `stream'
- as long as they initially match `key'. */
- char *xbuffer; /* As a line is being tested, it is saved here */
- long xbufsize; /* xbuffer is enlarged as needed. Current size here. */
- void
- copylines (stream, key)
- FILE *stream;
- char *key;
- {
- int keylen = strlen (key);
- xbufsize = keylen;
- xbuffer = (char *) malloc (keylen);
- while (1)
- {
- long len;
- int c;
- /* Read and test one line of the file. */
- /* Read beginning of line into `xbuffer', comparing with `key'. */
- len = compare1 (key, stream);
- if (!len) break;
- fwrite (xbuffer, 1, len, stdout);
- do
- {
- c = getc (stream);
- putchar (c);
- }
- while (c != '\n');
- }
- }
- #define GROWBUF \
- { char *nbuf = (char *) realloc (xbuffer, xbufsize *= 2); \
- pb += (nbuf - xbuffer); \
- pbe += (nbuf - xbuffer); \
- xbuffer = nbuf; }
- /* Compare the string `key' against a line being read from `stream'
- and stored into `buffer' as it is read.
- The command switches control the kind of comparison used.
- If they are unequal, return 0.
- If they are equal, return the number of characters read. */
- int
- compare1 (key, stream)
- char *key;
- FILE *stream;
- {
- char *pk = key;
- char *pb = xbuffer;
- char *pbe = xbuffer + xbufsize;
- char c1 = 1, c2;
- while (c1)
- {
- /* Fetch one char from key and one from stream. */
- c1 = *pk++;
- if (!c1) return pb - xbuffer;
- c2 = getc (stream);
- *pb++ = c2;
- if (pb == pbe)
- GROWBUF;
- /* If desired, skip chars in each one until a letter, digit, space or tab */
- if (dictionary_order)
- {
- while (c1 != ' ' && c1 != '\t'
- && (c1 < '0' || c1 > '9')
- && (c1 < 'A' || c1 > 'Z')
- && (c1 < 'a' || c1 > 'z'))
- {
- c1 = *pk++;
- if (!c1) return pb - xbuffer;
- }
- while (c2 >= 0 && c2 != '\n' && c2 != ' ' && c2 != '\t'
- && (c2 < '0' || c2 > '9')
- && (c2 < 'A' || c2 > 'Z')
- && (c2 < 'a' || c2 > 'z'))
- {
- c2 = getc (stream);
- *pb++ = c2;
- if (pb == pbe)
- GROWBUF;
- }
- }
- if (ignore_case)
- {
- if (c1 >= 'A' && c1 <= 'Z')
- c1 += 'a' - 'A';
- if (c2 >= 'A' && c2 <= 'Z')
- c2 += 'a' - 'A';
- }
- if (c1 != c2) return 0;
- }
- return pb - xbuffer;
- }
- /* Print error message and exit. */
- fatal (s1, s2)
- char *s1, *s2;
- {
- error (s1, s2);
- exit (1);
- }
- /* Print error message. `s1' is printf control string, `s2' is arg for it. */
- error (s1, s2)
- char *s1, *s2;
- {
- printf ("look: ");
- printf (s1, s2);
- printf ("\n");
- }
- perror_with_name (name)
- char *name;
- {
- extern int errno, sys_nerr;
- extern char *sys_errlist[];
- char *s;
- if (errno < sys_nerr)
- s = concat ("", sys_errlist[errno], " for %s");
- else
- s = "cannot open %s";
- error (s, name);
- }
- /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
- char *
- concat (s1, s2, s3)
- char *s1, *s2, *s3;
- {
- int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
- char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
- strcpy (result, s1);
- strcpy (result + len1, s2);
- strcpy (result + len1 + len2, s3);
- *(result + len1 + len2 + len3) = 0;
- return result;
- }
- /* Like malloc but get fatal error if memory is exhausted. */
- int
- xmalloc (size)
- int size;
- {
- int result = malloc (size);
- if (!result)
- fatal ("virtual memory exhausted", 0);
- return result;
- }
- int
- xrealloc (ptr, size)
- char *ptr;
- int size;
- {
- int result = realloc (ptr, size);
- if (!result)
- fatal ("virtual memory exhausted");
- return result;
- }
|