home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / unix / volume26 / agrep201 / part02 < prev    next >
Encoding:
Text File  |  1992-05-08  |  44.5 KB  |  1,434 lines

  1. Newsgroups: comp.sources.unix
  2. From: sw@cs.arizona.edu (Sun Wu)
  3. Subject: v26i022: agrep - very fast grep with approximate pattern matching, Part02/03
  4. Sender: unix-sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: sw@cs.arizona.edu (Sun Wu)
  8. Posting-Number: Volume 26, Issue 22
  9. Archive-Name: agrep-2.01/part02
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 2 (of 3)."
  18. # Contents:  agrep.1 asearch.c mgrep.c parse.c
  19. # Wrapped by sw@optima on Sun Feb 23 13:44:16 1992
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'agrep.1' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'agrep.1'\"
  23. else
  24. echo shar: Extracting \"'agrep.1'\" \(12740 characters\)
  25. sed "s/^X//" >'agrep.1' <<'END_OF_FILE'
  26. X.TH AGREP l "Jan 17, 1992"
  27. X.SH NAME
  28. agrep \- search a file for a string or regular expression, with approximate matching capabilities
  29. X.SH SYNOPSIS
  30. X.B agrep
  31. X[
  32. X.B \-#cdehiklnpstvwxBDGIS
  33. X]
  34. X.I pattern 
  35. X[ -f
  36. X.I patternfile 
  37. X]
  38. X[
  39. X.IR filename ".\|.\|. ]" 
  40. X.SH DESCRIPTION
  41. X.B agrep
  42. searches the input
  43. X.IR filenames
  44. X(standard input is the default, but see a warning under LIMITATIONS) 
  45. for records containing strings which either 
  46. X\fIexactly\fP or \fIapproximately\fP match a pattern.  
  47. A record is by default a line, but it can be defined differently using
  48. the -d option (see below).
  49. Normally, each record found is copied to the standard output.
  50. Approximate matching allows finding records that contain the pattern 
  51. with several errors including substitutions, insertions, and
  52. deletions.
  53. XFor example, Massechusets matches Massachusetts with two errors
  54. X(one substitution and one insertion).  Running 
  55. X.B agrep
  56. X-2 Massechusets foo outputs all lines in foo containing any string with
  57. at most 2 errors from Massechusets.
  58. X.LP
  59. X.B agrep 
  60. supports many kinds of queries including 
  61. arbitrary wild cards, sets of patterns, and in general,
  62. regular expressions.
  63. See PATTERNS below.
  64. It supports most of the options supported by the 
  65. X.B grep
  66. family plus several more (but it is not 100% compatible with grep).
  67. XFor more information on the algorithms used by agrep see
  68. Wu and Manber, 
  69. X"Fast Text Searching With Errors,"
  70. Technical report #91-11, Department of Computer Science, University
  71. of Arizona, June 1991 (available by anonymous ftp from cs.arizona.edu
  72. in agrep/agrep.ps.1), and
  73. Wu and Manber,
  74. X"Agrep -- A Fast Approximate Pattern Searching Tool",
  75. To appear in USENIX Conference 1992 January (available by anonymous ftp
  76. from cs.arizona.edu in agrep/agrep.ps.2).
  77. X.LP
  78. As with the rest of the \fBgrep\fP family, the characters
  79. X.RB ` $ ',
  80. X.RB `^ ',
  81. X.RB ` \(** ',
  82. X.RB ` [ ' ,
  83. X.RB ` ] ' ,
  84. X.RB ` \s+2^\s0 ',
  85. X.RB ` | ',
  86. X.RB ` ( ',
  87. X.RB ` ) ',
  88. X.RB ` ! ',
  89. and
  90. X.RB ` \e '
  91. can cause unexpected results when included in the
  92. X.IR pattern ,
  93. as these characters are also meaningful
  94. to the shell.  To avoid these problems, one should always enclose the entire
  95. pattern argument in single quotes, i.e., 'pattern'.
  96. Do not use double quotes (").
  97. X.LP
  98. When
  99. X.B agrep
  100. is applied to more than one input
  101. file, the name of the file is displayed
  102. preceding each line which matches
  103. the pattern.  The filename is not displayed
  104. when processing a single
  105. file, so if you actually want the filename
  106. to appear, use
  107. X.B /dev/null
  108. as a second file in the list.
  109. X.SH OPTIONS
  110. X.TP
  111. X.B \-\fI#\fP
  112. X\fI#\fP is a non-negative integer (at most 8)
  113. specifying the maximum number of errors
  114. permitted in finding the approximate matches (defaults to zero).
  115. Generally, each insertion, deletion, or substitution counts as one error.
  116. It is possible to adjust the relative cost of insertions,
  117. deletions and substitutions (see -I -D and -S options).
  118. X.TP
  119. X.B \-c
  120. Display only the count of matching records.
  121. X.TP
  122. X.B \-d "'\fIdelim\fP'"
  123. Define \fIdelim\fP to be the separator between two records.
  124. The default value is '$', namely a record is by default
  125. a line.
  126. X\fIdelim\fP can be a string of size at most 8
  127. X(with possible use of ^ and $), but not
  128. a regular expression.
  129. Text between two \fIdelim\fP's, before the first \fIdelim\fP,
  130. and after the last \fIdelim\fP is considered as one record.
  131. XFor example, -d '$$' defines paragraphs as records and -d '^From\ '
  132. defines mail messages as records.
  133. X.B agrep
  134. matches each record separately.
  135. This option does not currently work with regular expressions.
  136. X.TP
  137. X.BI \-e " pattern"
  138. Same as a simple
  139. X.I pattern
  140. argument, but useful when the
  141. X.I pattern
  142. begins with a
  143. X.RB ` \- '.
  144. X.TP
  145. X.BI \-f " patternfile"
  146. X.I patternfile 
  147. contains a set of (simple) patterns.
  148. The output is all lines that match at least one of the patterns in 
  149. X.I patternfile.
  150. Currently, the \-f option works only for exact match and for simple
  151. patterns (any meta symbol is interpreted as a regular character);
  152. it is compatible only with \-c, \-h, \-i, \-l, \-s, \-v, \-w, and \-x options.
  153. see LIMITATIONS for size bounds.
  154. X.TP
  155. X.B \-h
  156. Do not display filenames.
  157. X.TP
  158. X.B \-i
  159. Case-insensitive search \(em e.g., "A" and "a" are considered equivalent.
  160. X.TP
  161. X.B \-k
  162. No symbol in the pattern is treated as a meta character. 
  163. XFor example, agrep -k 'a(b|c)*d' foo will find  
  164. the occurrences of a(b|c)*d in foo whereas agrep 'a(b|c)*d' foo
  165. will find substrings in foo that match the regular expression 'a(b|c)*d'.
  166. X.TP
  167. X.B \-l
  168. List only the files that contain a match.
  169. This option is useful for looking for files containing a certain pattern.
  170. XFor example, " agrep -l 'wonderful'  * " will list the names of those
  171. files in current directory that contain the word 'wonderful'.
  172. X.TP
  173. X.B \-n
  174. XEach line that is printed is prefixed by its record number in the file.
  175. X.TP
  176. X.B \-p
  177. XFind records in the text that contain a supersequence of the pattern.
  178. XFor example,
  179. X\fB agrep \-p DCS foo
  180. will match "Department of Computer Science."
  181. X.TP
  182. X.B \-s   
  183. Work silently, that is, display nothing except error messages.
  184. This is useful for checking the error status.
  185. X.TP
  186. X.B \-t   
  187. Output the record starting from the end of 
  188. X.I delim
  189. to (and including) the next
  190. X.I delim.
  191. This is useful for cases where 
  192. X.I delim
  193. should come at the end of the record.
  194. X.TP
  195. X.B \-v
  196. Inverse mode \(em display only those records that
  197. X.I do not
  198. contain the pattern.
  199. X.TP
  200. X.B \-w
  201. Search for the pattern as a word \(em i.e., surrounded by non-alphanumeric
  202. characters.  The non-alphanumeric 
  203. X.B must
  204. surround the match;  they cannot be counted as errors.
  205. XFor example, 
  206. X.B agrep
  207. X-w -1 car will match cars, but not characters.
  208. X.TP
  209. X.B \-x
  210. The pattern must match the whole line.
  211. X.TP
  212. X.B \-B
  213. Best match mode.
  214. When \-B is specified and no exact matches are found, agrep
  215. will continue to search until the closest matches (i.e., the ones
  216. with minimum number of errors)
  217. are found, at which point the following message will be shown:
  218. X"the best match contains x errors, there are y matches, output them? (y/n)"
  219. The best match mode is not supported for standard input, e.g.,
  220. pipeline input.
  221. When the \-#, \-c, or \-l options are specified, the \-B option is ignored.
  222. In general, \-B may be slower than \-#, but not by very much.
  223. X.TP
  224. X.B \-D\fIk\fP
  225. Set the cost of a deletion to \fIk\fP (\fIk\fP is a positive integer).
  226. This option does not currently work with regular expressions.
  227. X.TP
  228. X.B \-G
  229. Output the files that contain a match.
  230. X.TP
  231. X.B \-I\fIk\fP
  232. Set the cost of an insertion to \fIk\fP (\fIk\fP is a positive integer).
  233. This option does not currently work with regular expressions.
  234. X.TP
  235. X.B \-S\fIk\fP
  236. Set the cost of a substitution to \fIk\fP (\fIk\fP is a positive integer).
  237. This option does not currently work with regular expressions.
  238. X.ne 4
  239. X.SH PATTERNS
  240. X.LP
  241. X\fIagrep\fP 
  242. supports a large variety of patterns, including simple
  243. strings, strings with classes of characters, sets of strings, 
  244. wild cards, and regular expressions.
  245. X.TP
  246. X\fBStrings\fP
  247. any sequence of characters, including the special symbols
  248. X`^' for beginning of line and `$' for end of line.
  249. The special characters listed above (
  250. X.RB ` $ ',
  251. X.RB `^ ',
  252. X.RB ` \(** ',
  253. X.RB ` [ ' ,
  254. X.RB ` \s+2^\s0 ',
  255. X.RB ` | ',
  256. X.RB ` ( ',
  257. X.RB ` ) ',
  258. X.RB ` ! ',
  259. and
  260. X.RB ` \e '
  261. X) should be preceded by `\\' if they are to be matched as regular
  262. characters.  For example, \\^abc\\\\ corresponds to the string ^abc\\,
  263. whereas ^abc corresponds to the string abc at the beginning of a
  264. line.
  265. X.TP
  266. X\fBClasses of characters\fP
  267. a list of characters inside [] (in order) corresponds to any character
  268. from the list.  For example, [a-ho-z] is any character between a and h
  269. or between o and z.  The symbol `^' inside [] complements the list.
  270. XFor example, [^i-n] denote any character in the character set except
  271. character 'i' to 'n'.
  272. The symbol `^' thus has two meanings, but this is consistent with
  273. egrep.
  274. The symbol `.' (don't care) stands for any symbol (except for the
  275. newline symbol).
  276. X.TP
  277. X\fBBoolean operations\fP
  278. X.B agrep 
  279. supports an `and' operation `;' 
  280. and an `or' operation `,',
  281. but not a combination of both.  For example, 'fast;network' searches
  282. for all records containing both words.
  283. X.TP
  284. X\fBWild cards\fP
  285. The symbol '#' is used to denote a wild card.  # matches zero or any
  286. number of arbitrary characters.  For example, 
  287. ex#e matches example.  The symbol # is equivalent to .* in egrep.
  288. In fact, .* will work too, because it is a valid regular expression
  289. X(see below), but unless this is part of an actual regular expression,
  290. X# will work faster. 
  291. X.TP
  292. X\fBCombination of exact and approximate matching\fP
  293. any pattern inside angle brackets <> must match the text exactly even
  294. if the match is with errors.  For example, <mathemat>ics matches
  295. mathematical with one error (replacing the last s with an a), but
  296. mathe<matics> does not match mathematical no matter how many errors we
  297. allow.
  298. X.TP
  299. X\fBRegular expressions\fP
  300. The syntax of regular expressions in \fBagrep\fP is in general the same as
  301. that for \fBegrep\fP.  The union operation `|', Kleene closure `*',
  302. and parentheses () are all supported.
  303. Currently '+' is not supported.
  304. Regular expressions are currently limited to approximately 30
  305. characters (generally excluding meta characters).  Some options
  306. X(\-d, \-w, \-f, \-t, \-x, \-D, \-I, \-S) do not 
  307. currently work with regular expressions.
  308. The maximal number of errors for regular expressions that use '*'
  309. or '|' is 4.
  310. X.SH EXAMPLES
  311. X.LP
  312. X.TP
  313. agrep -2 -c ABCDEFG foo 
  314. gives the number of lines in file foo that contain ABCDEFG
  315. within two errors.
  316. X.TP
  317. agrep -1 -D2 -S2 'ABCD#YZ' foo
  318. outputs the lines containing ABCD followed, within arbitrary
  319. distance, by YZ, with up to one additional insertion
  320. X(-D2 and -S2 make deletions and substitutions too "expensive").
  321. X.TP
  322. agrep -5 -p abcdefghij /usr/dict/words
  323. outputs the list of all words containing at least 5 of the first 10
  324. letters of the alphabet \fIin order\fR.  (Try it:  any list starting
  325. with academia and ending with sacrilegious must mean something!)
  326. X.TP 
  327. agrep -1 'abc[0-9](de|fg)*[x-z]' foo
  328. outputs the lines containing, within up to one error, the string
  329. that starts with abc followed by one digit, followed by zero or more
  330. repetitions of either de or fg, followed by either x, y, or z.
  331. X.TP
  332. agrep -d '^From\ ' 'breakdown;internet' mbox
  333. outputs all mail messages (the pattern '^From\ ' separates mail messages
  334. in a mail file) that contain keywords 'breakdown' and 'internet'.
  335. X.TP
  336. agrep -d '$$' -1 '<word1> <word2>' foo
  337. finds all paragraphs that contain word1 followed by word2 with one
  338. error in place of the blank.  
  339. In particular, if word1 is the last word in a line and word2
  340. is the first word in the next line, then the space will be
  341. substituted by a newline symbol and it will match.
  342. Thus, this is a way to overcome separation by a newline.
  343. Note that -d '$$' (or another delim which spans more than one line)
  344. is necessary, because otherwise agrep searches
  345. only one line at a time.
  346. X.TP
  347. agrep '^agrep' <this manual>
  348. outputs all the examples of the use of agrep in this man pages.
  349. X.PD
  350. X.SH "SEE ALSO"
  351. X.BR ed (1),
  352. X.BR ex (1),
  353. X.BR grep (1V),
  354. X.BR sh (1),
  355. X.BR csh (1).
  356. X.SH BUGS/LIMITATIONS
  357. Any bug reports or comments will be appreciated! 
  358. Please mail them to sw@cs.arizona.edu or udi@cs.arizona.edu
  359. X.LP
  360. Regular expressions do not support the '+' operator (match 1 or more
  361. instances of the preceding token).  These can be searched for by using
  362. this syntax in the pattern:
  363. X.sp
  364. X.in 1.0i
  365. X\&'\fIpattern\fB(\fIpattern\fB)*\fR'
  366. X.in
  367. X.sp
  368. X(search for strings containing one instance of the pattern, followed by 0 or
  369. more instances of the pattern).
  370. X.LP
  371. The following can cause an infinite loop:
  372. X.B agrep
  373. pattern * > output_file.
  374. If the number of matches is high, they may be deposited in
  375. output_file before it is completely read leading to more matches of
  376. the pattern within output_file (the matches are against the whole
  377. directory).  It's not clear whether this is a "bug" (grep will do the
  378. same), but be warned.
  379. X.LP
  380. The maximum size of the 
  381. X.I patternfile
  382. is limited to be 250Kb, and the maximum number of patterns
  383. is limited to be 30,000.
  384. X.LP
  385. Standard input is the default if no input file is given.
  386. However, if standard input is keyed in directly (as opposed to through
  387. a pipe, for example) agrep may not work for some non-simple patterns.
  388. X.LP
  389. There is no size limit for simple patterns.
  390. More complicated patterns are currently limited to approximately 30 characters.
  391. Lines are limited to 1024 characters.
  392. Records are limited to 48K, and may be truncated if they are larger
  393. than that.
  394. The limit of record length can be 
  395. changed by modifying the parameter Max_record in agrep.h.
  396. X.SH DIAGNOSTICS
  397. XExit status is 0 if any matches are found,
  398. X1 if none, 2 for syntax errors or inaccessible files.
  399. X.SH AUTHORS
  400. Sun Wu and Udi Manber, Department of Computer Science, University of
  401. Arizona, Tucson, AZ 85721.  {sw|udi}@cs.arizona.edu.
  402. X
  403. X
  404. END_OF_FILE
  405. if test 12740 -ne `wc -c <'agrep.1'`; then
  406.     echo shar: \"'agrep.1'\" unpacked with wrong size!
  407. fi
  408. # end of 'agrep.1'
  409. fi
  410. if test -f 'asearch.c' -a "${1}" != "-c" ; then 
  411.   echo shar: Will not clobber existing file \"'asearch.c'\"
  412. else
  413. echo shar: Extracting \"'asearch.c'\" \(11214 characters\)
  414. sed "s/^X//" >'asearch.c' <<'END_OF_FILE'
  415. X/* Copyright (c) 1991 Sun Wu and Udi Manber.  All Rights Reserved. */
  416. X#include "agrep.h"
  417. X
  418. extern unsigned Init1, Init[], Mask[], endposition, D_endpos, AND, NO_ERR_MASK;
  419. extern int DELIMITER, FILENAMEONLY, INVERSE;
  420. extern CHAR CurrentFileName[];
  421. extern int I, num_of_matched, TRUNCATE;
  422. X
  423. asearch(old_D_pat, text, D)
  424. CHAR old_D_pat[]; int text; register unsigned D;
  425. X{
  426. X  register unsigned i, c, r1, r2, CMask, r_NO_ERR, r_Init1; 
  427. X  register unsigned A0, B0, A1, B1, endpos;
  428. X  unsigned A2, B2, A3, B3, A4, B4;
  429. X  unsigned A[MaxError+1], B[MaxError+1];
  430. X  unsigned D_Mask;
  431. X  unsigned end;
  432. X  int D_length, FIRSTROUND, ResidueSize, lasti, l, k, buffer_end, j=0;
  433. X  int printout_end;
  434. X  CHAR buffer[2*Max_record+1];
  435. X     
  436. X  if (I == 0) Init1 = 037777777777;
  437. X  if(D > 4) {
  438. X         asearch0(old_D_pat, text, D); 
  439. X         return;  }
  440. X  D_length = strlen(old_D_pat);
  441. X  buffer[Max_record-1] = '\n';
  442. X  D_Mask = D_endpos;
  443. X  for ( i=1; i<D_length; i++) D_Mask = (D_Mask<<1) | D_Mask;
  444. X  D_Mask = ~D_Mask;
  445. X
  446. X  r_Init1 = Init1; /* put Init1 in register */
  447. X  r_NO_ERR = NO_ERR_MASK; /* put NO_ERR_MASK in register */
  448. X  endpos = D_endpos;    
  449. X  FIRSTROUND = ON;
  450. X  A0 = B0 = A1 = B1 = A2 = B2 = A3 = B3 = A4 = B4 = Init[0];
  451. X  for(k=0; k<=D; k++) A[k] = B[k] = Init[0];
  452. X  lasti = Max_record;
  453. X
  454. X  while ((l = fill_buf(text, buffer + Max_record, Max_record)) > 0)
  455. X  { i = Max_record; end = Max_record + l ;
  456. X    if (FIRSTROUND) { 
  457. X        i = Max_record - 1;
  458. X    if(DELIMITER) {
  459. X        for(k=0; k<D_length; k++) {
  460. X                    if(old_D_pat[k] != buffer[Max_record+k])                         break;
  461. X        }
  462. X        if(k>=D_length) j--;
  463. X    }
  464. X        FIRSTROUND = OFF; }
  465. X    if (l < BlockSize) {
  466. X        strncpy(buffer+end, old_D_pat, D_length);
  467. X        buffer[end+D_length] = '\0';
  468. X        end = end + D_length; }
  469. X    while (i < end )
  470. X    {
  471. X        c = buffer[i];
  472. X        CMask = Mask[c];
  473. X              r1 = r_Init1 & B0;
  474. X              A0 = ((B0 >>1 ) & CMask) | r1;
  475. X              r1 = r_Init1 & B1;
  476. X              r2 =  B0 | (((A0 | B0) >> 1) & r_NO_ERR); 
  477. X              A1 = ((B1 >>1 ) & CMask) | r2 | r1 ;  
  478. X                     if(D == 1) goto Nextchar;
  479. X              r1 = r_Init1 & B2;
  480. X              r2 =  B1 | (((A1 | B1) >> 1) & r_NO_ERR); 
  481. X              A2 = ((B2 >>1 ) & CMask) | r2 | r1 ;  
  482. X                     if(D == 2) goto Nextchar;
  483. X              r1 = r_Init1 & B3;
  484. X              r2 =  B2 | (((A2 | B2) >> 1) & r_NO_ERR); 
  485. X              A3 = ((B3 >>1 ) & CMask) | r2 | r1 ;  
  486. X                     if(D == 3) goto Nextchar;
  487. X              r1 = r_Init1 & B4;
  488. X              r2 =  B3 | (((A3 | B3) >> 1) & r_NO_ERR); 
  489. X              A4 = ((B4 >>1 ) & CMask) | r2 | r1 ;  
  490. X                     if(D == 4) goto Nextchar;
  491. Nextchar: i=i+1;
  492. X        if(A0 & endpos) {
  493. X           j++;  r1 = A0;
  494. X           if ( D == 1) r1 = A1;
  495. X           if ( D == 2) r1 = A2;
  496. X           if ( D == 3) r1 = A3;
  497. X           if ( D == 4) r1 = A4;
  498. X           if(((AND == 1) && ((r1 & endposition) == endposition)) ||                           ((AND == 0) && (r1 & endposition)) ^ INVERSE )
  499. X                 {    
  500. X                      if(FILENAMEONLY) {
  501. X                         num_of_matched++;
  502. X                         printf("%s\n", CurrentFileName);
  503. X                         return;  }
  504. X                      printout_end = i - D_length - 1;           
  505. X                      if(!(lasti >= Max_record + l - 1))
  506. X                         output(buffer, lasti, printout_end, j);
  507. X                 }
  508. X           lasti = i - D_length; /* point to starting position of D_pat */
  509. X           TRUNCATE = OFF;
  510. X           for(k=0; k<= D; k++) {
  511. X              B[k] = Init[0];
  512. X           }
  513. X           r1 = B[0] & Init1;
  514. X           A[0] = (((B[0]>>1) & CMask) | r1) & D_Mask;
  515. X           for(k=1; k<= D; k++) {
  516. X              r1 = Init1 & B[k];
  517. X              r2 = B[k-1] | (((A[k-1] | B[k-1])>>1)&r_NO_ERR);
  518. X              A[k] = (((B[k]>>1)&CMask) | r1 | r2) ;
  519. X           }
  520. X           A0 = A[0]; B0 = B[0]; A1 = A[1]; B1 = B[1]; A2 = A[2]; B2 = B[2];
  521. X           A3 = A[3]; B3 = B[3]; A4 = A[4]; B4 = B[4];
  522. X        }
  523. X        c = buffer[i];
  524. X        CMask = Mask[c];
  525. X              r1 = r_Init1 & A0;
  526. X              B0 = ((A0 >> 1 ) & CMask) | r1;
  527. X#ifdef DEBUG
  528. X    printf("Mask = %o, B0 = %o\n", CMask, B0);
  529. X#endif
  530. X              r1 = r_Init1 & A1;
  531. X              r2 =  A0 | (((A0 | B0) >> 1) & r_NO_ERR); 
  532. X              B1 = ((A1 >>1 ) & CMask) | r2 | r1 ;  
  533. X                     if(D == 1) goto Nextchar1;
  534. X              r1 = r_Init1 & A2;
  535. X              r2 =  A1 | (((A1 | B1) >> 1) & r_NO_ERR); 
  536. X              B2 = ((A2 >>1 ) & CMask) | r2 | r1 ;  
  537. X                     if(D == 2) goto Nextchar1;
  538. X              r1 = r_Init1 & A3;
  539. X              r2 =  A2 | (((A2 | B2) >> 1) & r_NO_ERR); 
  540. X              B3 = ((A3 >>1 ) & CMask) | r2 | r1 ;  
  541. X                     if(D == 3) goto Nextchar1;
  542. X              r1 = r_Init1 & A4;
  543. X              r2 =  A3 | (((A3 | B3) >> 1) & r_NO_ERR); 
  544. X              B4 = ((A4 >>1 ) & CMask) | r2 | r1 ;  
  545. X                     if(D == 4) goto Nextchar1;
  546. Nextchar1: i=i+1;
  547. X        if(B0 & endpos) {
  548. X           j++;  r1 = B0;
  549. X           if ( D == 1) r1 = B1;
  550. X           if ( D == 2) r1 = B2;
  551. X           if ( D == 3) r1 = B3;
  552. X           if ( D == 4) r1 = B4;
  553. X           if(((AND == 1) && ((r1 & endposition) == endposition)) ||                           ((AND == 0) && (r1 & endposition)) ^ INVERSE )
  554. X                 { 
  555. X                    if(FILENAMEONLY) {
  556. X                       num_of_matched++;
  557. X                       printf("%s\n", CurrentFileName);
  558. X                       return; }
  559. X                    printout_end = i - D_length -1 ; 
  560. X                    if(!(lasti >= Max_record + l - 1))
  561. X                       output(buffer, lasti, printout_end, j);
  562. X                 }
  563. X           lasti = i - D_length ;
  564. X           TRUNCATE = OFF;
  565. X           for(k=0; k<= D; k++) {
  566. X              A[k] = Init[0];
  567. X           }
  568. X           r1 = A[0] & Init1; 
  569. X           B[0] = (((A[0]>>1)&CMask) | r1) & D_Mask;
  570. X           for(k=1; k<= D; k++) {
  571. X              r1 = Init1 & A[k];
  572. X              r2 = A[k-1] | (((A[k-1] | B[k-1])>>1)&r_NO_ERR);
  573. X              B[k] = (((A[k]>>1)&CMask) | r1 | r2) ;
  574. X           }
  575. X           A0 = A[0]; B0 = B[0]; A1 = A[1]; B1 = B[1]; A2 = A[2]; B2 = B[2];
  576. X           A3 = A[3]; B3 = B[3]; A4 = A[4]; B4 = B[4];
  577. X        }
  578. X    }
  579. X    if(l < BlockSize) {
  580. X           lasti = Max_record ;
  581. X    }
  582. X    else {
  583. X       ResidueSize = Max_record + l - lasti;
  584. X       if(ResidueSize > Max_record) {
  585. X          ResidueSize = Max_record;
  586. X          TRUNCATE = ON;         }
  587. X       strncpy(buffer+Max_record-ResidueSize, buffer+lasti, ResidueSize);
  588. X       lasti = Max_record - ResidueSize;
  589. X       if(lasti == 0)     lasti = 1; 
  590. X    }
  591. X  }
  592. X  return;
  593. X}
  594. X
  595. asearch0(old_D_pat, text, D)
  596. CHAR old_D_pat[]; int text; register unsigned D;
  597. X{
  598. X  register unsigned i, c, r1, r2, r3, CMask, r_NO_ERR, r_Init1,  end, endpos; 
  599. X  unsigned A[MaxError+2], B[MaxError+2];
  600. X  unsigned D_Mask;
  601. X  int D_length, FIRSTROUND, ResidueSize, lasti, l, k, buffer_end, j=0;
  602. X  int printout_end;
  603. X  CHAR buffer[BlockSize+Max_record+1];
  604. X  
  605. X  D_length = strlen(old_D_pat);
  606. X  buffer[Max_record-1] = '\n';
  607. X  D_Mask = D_endpos;
  608. X  for ( i=1; i<D_length; i++) D_Mask = (D_Mask<<1) | D_Mask;
  609. X  D_Mask = ~D_Mask;
  610. X
  611. X  r_Init1 = Init1; /* put Init1 in register */
  612. X  r_NO_ERR = NO_ERR_MASK; /* put NO_ERR_MASK in register */
  613. X  endpos = D_endpos;    
  614. X  FIRSTROUND = ON;
  615. X  for(k=0; k<=D; k++) A[k] = B[k] = Init[0];
  616. X  lasti = Max_record;
  617. X
  618. X  while ((l = fill_buf(text, buffer + Max_record, Max_record)) > 0)
  619. X  { i = Max_record; end = Max_record + l ;
  620. X    if (FIRSTROUND) { 
  621. X        i = Max_record - 1;
  622. X        FIRSTROUND = OFF; }
  623. X    if (l < BlockSize) {
  624. X        strncpy(buffer+end, old_D_pat, D_length);
  625. X        buffer[end+D_length] = '\0';
  626. X        end = end + D_length; }
  627. X    while (i < end )
  628. X    {
  629. X        c = buffer[i++];
  630. X        CMask = Mask[c];
  631. X              r1 = B[0] & r_Init1;
  632. X              A[0] = (((B[0] >> 1)) & CMask | r1 ) ;
  633. X              for(k=1; k<=D; k++) {
  634. X                     r1 = r_Init1 & B[k];
  635. X                     r2 = B[k-1] | (((A[k-1]|B[k-1])>>1) & r_NO_ERR);
  636. X                     A[k] = ((B[k] >> 1) & CMask) | r2 | r1;
  637. X              }
  638. X        if(A[0] & endpos) {
  639. X           j++;  
  640. X           r1 = A[D];
  641. X           if(((AND == 1) && ((r1 & endposition) == endposition)) ||                           ((AND == 0) && (r1 & endposition)) ^ INVERSE )
  642. X                 {    
  643. X                      if(FILENAMEONLY) {
  644. X                         num_of_matched++;
  645. X                         printf("%s\n", CurrentFileName);
  646. X                         return;  }
  647. X                      printout_end = i - D_length - 1;           
  648. X                      if(!(lasti >= Max_record + l - 1))
  649. X                         output(buffer, lasti, printout_end, j);
  650. X                 }
  651. X           lasti = i - D_length; /* point to starting position of D_pat */
  652. X           for(k=0; k<= D; k++) {
  653. X              B[k] = Init[0];
  654. X           }
  655. X           r1 = B[0] & r_Init1;
  656. X           A[0] = (((B[0]>>1) & CMask) | r1) & D_Mask;
  657. X           for(k=1; k<= D; k++) {
  658. X              r1 = Init1 & B[k];
  659. X              r2 = B[k-1] | (((A[k-1] | B[k-1])>>1)&r_NO_ERR);
  660. X              A[k] = (((B[k]>>1)&CMask) | r1 | r2) ;
  661. X           }
  662. X        }
  663. X        c = buffer[i++];
  664. X        CMask = Mask[c];
  665. X              r1   = r_Init1 & A[0];
  666. X              B[0] = ((A[0] >> 1 ) & CMask) | r1;
  667. X              for(k=1; k<=D; k++) {
  668. X                     r1 = r_Init1 & A[k];
  669. X                     r2 = A[k-1] | (((A[k-1]|B[k-1])>>1) & r_NO_ERR);
  670. X                     B[k] = ((A[k] >> 1) & CMask) | r2 | r1;
  671. X              }
  672. X        if(B[0] & endpos) {
  673. X           j++;  
  674. X           r1 = B[D];
  675. X           if(((AND == 1) && ((r1 & endposition) == endposition)) ||                           ((AND == 0) && (r1 & endposition)) ^ INVERSE )
  676. X                 { 
  677. X                    if(FILENAMEONLY) {
  678. X                       num_of_matched++;
  679. X                       printf("%s\n", CurrentFileName);
  680. X                       return; }
  681. X                    printout_end = i - D_length -1 ; 
  682. X                    if(!(lasti >= Max_record + l - 1))
  683. X                       output(buffer, lasti, printout_end, j);
  684. X                 }
  685. X           lasti = i - D_length ;
  686. X           for(k=0; k<= D; k++) {
  687. X              A[k] = Init[0];
  688. X           }
  689. X           r1 = A[0] & r_Init1; 
  690. X           B[0] = (((A[0]>>1)&CMask) | r1) & D_Mask;
  691. X           for(k=1; k<= D; k++) {
  692. X              r1 = r_Init1 & A[k];
  693. X              r2 = A[k-1] | (((A[k-1] | B[k-1])>>1)&r_NO_ERR);
  694. X              B[k] = (((A[k]>>1)&CMask) | r1 | r2) ;
  695. X           }
  696. X        }
  697. X    }
  698. X    if(l < BlockSize) {
  699. X           lasti = Max_record;
  700. X    }
  701. X    else {
  702. X       ResidueSize = Max_record + l - lasti;
  703. X       if(ResidueSize > Max_record) {
  704. X          ResidueSize = Max_record;
  705. X          TRUNCATE = ON;         }
  706. X       strncpy(buffer+Max_record-ResidueSize, buffer+lasti, ResidueSize);
  707. X       lasti = Max_record - ResidueSize;
  708. X       if(lasti == 0)     lasti = 1; 
  709. X    }
  710. X  }
  711. X  return;
  712. X}
  713. X
  714. X
  715. X/*
  716. fill_buf(fd, buf, record_size)
  717. int fd, record_size; unsigned char *buf;
  718. X{
  719. int num_read=1;
  720. int total_read=0;
  721. X    while(total_read < record_size && num_read > 0) {
  722. X        num_read = read(fd, buf+total_read, 4096);
  723. X        total_read = total_read + num_read;
  724. X    }
  725. X    return(total_read);
  726. X}
  727. X*/
  728. END_OF_FILE
  729. if test 11214 -ne `wc -c <'asearch.c'`; then
  730.     echo shar: \"'asearch.c'\" unpacked with wrong size!
  731. fi
  732. # end of 'asearch.c'
  733. fi
  734. if test -f 'mgrep.c' -a "${1}" != "-c" ; then 
  735.   echo shar: Will not clobber existing file \"'mgrep.c'\"
  736. else
  737. echo shar: Extracting \"'mgrep.c'\" \(8386 characters\)
  738. sed "s/^X//" >'mgrep.c' <<'END_OF_FILE'
  739. X/* Copyright (c) 1991 Sun Wu and Udi Manber.  All Rights Reserved. */
  740. X/* multipattern matcher */
  741. X#include <stdio.h>
  742. X#define MAXPAT  256
  743. X#define MAXLINE 1024
  744. X#define MAXSYM  256
  745. X#define MAXMEMBER1 4096
  746. X#define MAXPATFILE 260000
  747. X#define BLOCKSIZE  8192
  748. X#define MAXHASH    8192
  749. X#define mm        8191
  750. X#define max_num    30000  
  751. X#define W_DELIM       252
  752. X#define L_DELIM    10 
  753. X
  754. extern COUNT, FNAME, SILENT, FILENAMEONLY, num_of_matched;
  755. extern INVERSE;
  756. extern WORDBOUND, WHOLELINE, NOUPPER;
  757. extern unsigned char  CurrentFileName[], Progname[]; 
  758. extern total_line;
  759. X
  760. int LONG  = 0;
  761. int SHORT = 0;
  762. int p_size=0;
  763. unsigned char SHIFT1[MAXMEMBER1];
  764. unsigned char tr[MAXSYM];
  765. unsigned char tr1[MAXSYM];
  766. struct pat_list {
  767. X    int  index;
  768. X    struct pat_list *next;
  769. X} *HASH[MAXHASH];
  770. struct pat_list  *pt, *qt;
  771. unsigned char buf[MAXPATFILE+BLOCKSIZE];
  772. unsigned char pat_spool[MAXPATFILE+2*max_num+MAXPAT];
  773. unsigned char *patt[max_num];
  774. unsigned char pat_len[max_num];
  775. X
  776. X
  777. prepf(fp)
  778. int fp;
  779. X{
  780. X    int length=0, i, p=1, pdx=0, num_pat;
  781. X    unsigned char *pat_ptr=pat_spool, temp[10];
  782. X    unsigned Mask = 15;
  783. X    int num_read;
  784. X
  785. X    while((num_read = read(fp, buf+length, BLOCKSIZE)) > 0) {
  786. X    length = length + num_read;
  787. X    if(length > MAXPATFILE) {
  788. X        fprintf(stderr, "%s: maximum pattern file size is %d\n", Progname, MAXPATFILE);
  789. X        exit(2);
  790. X    }
  791. X    }
  792. X    buf[length] = '\n';
  793. X    i = 0; p=1;
  794. X    while(i<length) {
  795. X    patt[p] = pat_ptr;
  796. X    if(WORDBOUND) *pat_ptr++ = W_DELIM;
  797. X    if(WHOLELINE) *pat_ptr++ = L_DELIM;
  798. X    while((*pat_ptr = buf[i++]) != '\n') pat_ptr++;
  799. X    if(WORDBOUND) *pat_ptr++ = W_DELIM;
  800. X    if(WHOLELINE) *pat_ptr++ = L_DELIM;           /* Can't be both on */
  801. X    *pat_ptr++ = 0;
  802. X    p++;  
  803. X    }
  804. X    if(p>max_num) {
  805. X    fprintf(stderr, "%s: maximum number of patterns is %d\n", Progname, max_num); 
  806. X    exit(2);
  807. X    }
  808. X    for(i=1; i<20; i++) *pat_ptr = i;  /* boundary safety zone */
  809. X    for(i=0; i< MAXSYM; i++) tr[i] = i;
  810. X    if(NOUPPER) {
  811. X    for(i='A'; i<= 'Z'; i++) tr[i] = i + 'a' - 'A';
  812. X    }
  813. X    if(WORDBOUND) {
  814. X    tr['\n'] = tr['\t'] = tr[' '] = tr[','] = tr[';'] = tr[':'] = W_DELIM;
  815. X        tr['!'] = tr['"'] = tr['?'] = tr['<'] = tr['>'] = W_DELIM;
  816. X    tr['='] = tr['['] = tr[']'] = W_DELIM; 
  817. X        tr['('] = tr[')'] = tr['$'] = tr['/'] = tr['\\'] = W_DELIM;
  818. X    }
  819. X    for(i=0; i< MAXSYM; i++) tr1[i] = tr[i]&Mask;
  820. X#ifdef DEBUG
  821. X  for(i=1; i<p; i++) printf("%s\n", patt[i]); fflush(stdout);
  822. X  printf("\n\n");
  823. X#endif
  824. X    num_pat = p-1;
  825. X    p_size = MAXPAT;
  826. X    for(i=1 ; i <= num_pat; i++) {
  827. X    p = strlen(patt[i]);
  828. X    pat_len[i] = p;
  829. X    if(p!=0 && p < p_size) p_size = p;
  830. X    }
  831. X    if(p_size == 0) {
  832. X    fprintf(stderr, "%s: the pattern file is empty\n");
  833. X    exit(2);
  834. X    }
  835. X    if(length > 400 && p_size > 2) LONG = 1;
  836. X    if(p_size == 1) SHORT = 1;
  837. X    for(i=0; i<MAXMEMBER1; i++) SHIFT1[i] = p_size - 2;
  838. X    for(i=0; i<MAXHASH; i++) {
  839. X    HASH[i] = 0;
  840. X    }
  841. X    for(i=1; i<= num_pat; i++) f_prep(i, patt[i]);
  842. X}
  843. X
  844. X
  845. mgrep(fd)
  846. int fd;
  847. X{ 
  848. X    register char r_newline = '\n';
  849. X    unsigned char text[2*BLOCKSIZE+MAXLINE]; 
  850. X    register int buf_end, num_read, i=0, j, start, end, residue = 0;
  851. X
  852. X    text[MAXLINE-1] = '\n';  /* initial case */
  853. X    start = MAXLINE-1;
  854. X
  855. X    while( (num_read = read(fd, text+MAXLINE, BLOCKSIZE)) > 0) 
  856. X    {
  857. X       if(INVERSE && COUNT) countline(text+MAXLINE, num_read);
  858. X       buf_end = end = MAXLINE + num_read -1 ;
  859. X       while(text[end]  != r_newline && end > MAXLINE) end--;
  860. X       residue = buf_end - end  + 1 ;
  861. X       text[start-1] = r_newline;
  862. X       if(SHORT) m_short(text, start, end);
  863. X       else      monkey1(text, start, end);
  864. X       if(FILENAMEONLY && num_of_matched) {
  865. X        printf("%s\n", CurrentFileName);
  866. X        return;
  867. X       }
  868. X       start = MAXLINE - residue;
  869. X       if(start < 0) {
  870. X            start = 1; 
  871. X       }
  872. X       strncpy(text+start, text+end, residue);
  873. X    } /* end of while(num_read = ... */
  874. X    text[MAXLINE] = '\n';
  875. X    text[start-1] = '\n';
  876. X    if(residue > 1) {
  877. X        if(SHORT) m_short(text, start, end);
  878. X        else      monkey1(text, start, end);
  879. X    }
  880. X    return;
  881. X} /* end mgrep */
  882. X
  883. countline(text, len)
  884. unsigned char *text; int len;
  885. X{
  886. int i;
  887. X    for (i=0; i<len; i++) if(text[i] == '\n') total_line++;
  888. X}
  889. X
  890. X
  891. monkey1( text, start, end  ) 
  892. int start, end; register unsigned char *text;
  893. X{
  894. register unsigned char *textend;
  895. register unsigned hash, i;
  896. register unsigned char shift; 
  897. register int  m1, j, Long=LONG; 
  898. int pat_index, m=p_size; 
  899. int MATCHED=0;
  900. register unsigned char *qx;
  901. register struct pat_list *p;
  902. unsigned char *lastout;
  903. int OUT=0;
  904. X
  905. textend = text + end;
  906. m1 = m - 1;
  907. lastout = text+start+1;
  908. text = text + start + m1 ;
  909. while (text <= textend) {
  910. X    hash=tr1[*text];
  911. X    hash=(hash<<4)+(tr1[*(text-1)]);
  912. X    if(Long) hash=(hash<<4)+(tr1[*(text-2)]);
  913. X    shift = SHIFT1[hash];
  914. X    if(shift == 0) {
  915. X        hash=0;
  916. X        for(i=0;i<=m1;i++)  {
  917. X            hash=(hash<<4)+(tr1[*(text-i)]);
  918. X        }
  919. X        hash=hash&mm;
  920. X        p = HASH[hash];
  921. X        while(p != 0) {
  922. X            pat_index = p->index;
  923. X            p = p->next;
  924. X            qx = text-m1;
  925. X            j = 0;
  926. X            while(tr[patt[pat_index][j]] == tr[*(qx++)]) j++;
  927. X                if (j > m1 ) { 
  928. X               if(pat_len[pat_index] <= j) {
  929. X                if(text > textend) return;
  930. X                        num_of_matched++;
  931. X                        if(FILENAMEONLY || SILENT)  return;
  932. X                MATCHED=1;
  933. X                    if(COUNT) {
  934. X                      while (*text != '\n') text++;
  935. X                }
  936. X                else {
  937. X                    if(!INVERSE) {
  938. X                      if(FNAME) printf("%s: ",CurrentFileName);
  939. X                                  while(*(--text) != '\n');
  940. X                                  while(*(++text) != '\n') putchar(*text);
  941. X                      printf("\n");
  942. X                    }
  943. X                    else {
  944. X                      if(FNAME) printf("%s: ",CurrentFileName);
  945. X                                  while(*(--text) != '\n');
  946. X                    if(lastout < text) OUT=1;
  947. X                    while(lastout < text) putchar(*lastout++);
  948. X                    if(OUT) {
  949. X                        putchar('\n');
  950. X                        OUT=0;
  951. X                    }
  952. X                                  while(*(++text) != '\n');
  953. X                    lastout=text+1;
  954. X                    }
  955. X                }
  956. X/*
  957. X                else {
  958. X                      if(FNAME) printf("%s: ",CurrentFileName);
  959. X                                  while(*(--text) != '\n');
  960. X                                  while(*(++text) != '\n') putchar(*text);
  961. X                      printf("\n");
  962. X                }
  963. X*/
  964. X               }
  965. X                    }
  966. X            if(MATCHED) break;
  967. X        }
  968. X        if(!MATCHED) shift = 1;
  969. X        else {
  970. X            MATCHED = 0;
  971. X            shift = m1;
  972. X        }
  973. X        }
  974. X    text = text + shift;
  975. X  }
  976. X  if(INVERSE && !COUNT) while(lastout <= textend) putchar(*lastout++);
  977. X}
  978. X
  979. m_short( text, start, end  ) 
  980. int start, end; register unsigned char *text;
  981. X{
  982. register unsigned char *textend;
  983. register unsigned i; 
  984. register int  j; 
  985. register struct pat_list *p, *q;
  986. register int pat_index; 
  987. int MATCHED=0;
  988. int OUT=0;
  989. unsigned char *lastout;
  990. unsigned char *qx;
  991. X
  992. textend = text + end;
  993. lastout = text + start + 1;
  994. text = text + start - 1 ;
  995. while (++text <= textend) {
  996. X        p = HASH[*text];
  997. X        while(p != 0) {
  998. X            pat_index = p->index;
  999. X            p = p->next;
  1000. X            qx = text;
  1001. X            j = 0;
  1002. X            while(tr[patt[pat_index][j]] == tr[*(qx++)]) j++;
  1003. X            if(pat_len[pat_index] <= j) {
  1004. X                if(text >= textend) return;
  1005. X                        num_of_matched++;
  1006. X                        if(FILENAMEONLY || SILENT)  return;
  1007. X                    if(COUNT) {
  1008. X                      while (*text != '\n') text++;
  1009. X                }
  1010. X                else {
  1011. X                      if(FNAME) printf("%s: ",CurrentFileName);
  1012. X                    if(!INVERSE) {
  1013. X                                  while(*(--text) != '\n');
  1014. X                                  while(*(++text) != '\n') putchar(*text);
  1015. X                      printf("\n");
  1016. X                    MATCHED = 1;
  1017. X                    }
  1018. X                    else {
  1019. X                                  while(*(--text) != '\n');
  1020. X                    if(lastout < text) OUT=1;
  1021. X                    while(lastout < text) putchar(*lastout++);
  1022. X                    if(OUT) {
  1023. X                        putchar('\n');
  1024. X                        OUT=0;
  1025. X                    }
  1026. X                                  while(*(++text) != '\n');
  1027. X                    lastout=text+1;
  1028. X                    MATCHED = 1;
  1029. X                    }
  1030. X                }
  1031. X                    }
  1032. X            if(MATCHED) break;
  1033. X        }
  1034. X        MATCHED = 0;
  1035. X  } /* while */
  1036. X  if(INVERSE && !COUNT) while(lastout <= textend) putchar(*lastout++);
  1037. X}
  1038. X
  1039. f_prep(pat_index, Pattern)
  1040. unsigned char *Pattern ; int pat_index;
  1041. X{
  1042. int i, j, m;
  1043. register unsigned hash, Mask=15;
  1044. X    m = p_size;
  1045. X    for (i = m-1; i>=(1+LONG); i--) {
  1046. X        hash = (Pattern[i] & Mask);
  1047. X        hash = (hash << 4) + (Pattern[i-1]& Mask);
  1048. X        if(LONG) hash = (hash << 4) + (Pattern[i-2] & Mask);
  1049. X        if(SHIFT1[hash] >= m-1-i) SHIFT1[hash] = m-1-i;
  1050. X    }
  1051. X    if(SHORT) Mask = 255;  /* 011111111 */
  1052. X    hash = 0;
  1053. X    for(i = m-1; i>=0; i--)  {
  1054. X        hash = (hash << 4) + (tr[Pattern[i]]&Mask);
  1055. X    }
  1056. X/*
  1057. X    if(INVERSE) hash = Pattern[1];
  1058. X*/
  1059. X    hash=hash&mm;
  1060. X    qt = (struct pat_list *) malloc(sizeof(struct pat_list));
  1061. X    qt->index = pat_index;
  1062. X    pt = HASH[hash];
  1063. X    qt->next = pt;
  1064. X    HASH[hash] = qt;
  1065. X}
  1066. X
  1067. X
  1068. END_OF_FILE
  1069. if test 8386 -ne `wc -c <'mgrep.c'`; then
  1070.     echo shar: \"'mgrep.c'\" unpacked with wrong size!
  1071. fi
  1072. # end of 'mgrep.c'
  1073. fi
  1074. if test -f 'parse.c' -a "${1}" != "-c" ; then 
  1075.   echo shar: Will not clobber existing file \"'parse.c'\"
  1076. else
  1077. echo shar: Extracting \"'parse.c'\" \(9497 characters\)
  1078. sed "s/^X//" >'parse.c' <<'END_OF_FILE'
  1079. X/* the functions in this file parse a string that represents
  1080. X   a regular expression, and return a pointer to a syntax
  1081. X   tree for that regular expression.                */
  1082. X
  1083. X#include <stdio.h>
  1084. X#include "re.h"
  1085. X
  1086. X#define FALSE    0
  1087. X#define TRUE    1
  1088. X
  1089. X#define NextChar(s)     *(*s)++
  1090. X#define Unexpected(s, c) (**s == NUL || **s == c)
  1091. X#define Invalid_range(x, y)    (x == NUL || x == '-' || x == ']' || x < y)
  1092. X
  1093. extern Stack Push();
  1094. extern Re_node Pop();
  1095. extern Re_node Top();
  1096. extern int Size();
  1097. extern Pset pset_union();
  1098. extern Pset create_pos();
  1099. X
  1100. int final_pos, pos_cnt = 0;
  1101. X
  1102. X/* retract_token() moves the string pointer back, effectively "unseeing"
  1103. X   the last character seen.  It is used only to retract a right paren --
  1104. X   the idea is that the incarnation of parse_re() that saw the corresponding
  1105. X   left paren is supposed to take care of matching the right paren.  This
  1106. X   is necessary to prevent recursive calls from mistakenly eating up someone
  1107. X   else's right parens.                            */
  1108. X
  1109. X#define retract_token(s)    --(*s)
  1110. X
  1111. X/* mk_leaf() creates a leaf node that is (usually) a literal node.    */
  1112. X
  1113. Re_node mk_leaf(opval, type, ch, cset)
  1114. short opval, type;
  1115. char ch;
  1116. Ch_Set cset;
  1117. X{
  1118. X    Re_node node; Re_Lit l;
  1119. X
  1120. X    l = (Re_Lit) new_node(l);
  1121. X    node = (Re_node) new_node(node);
  1122. X    if (l == NULL || node == NULL) return NULL;
  1123. X    lit_type(l) = type;
  1124. X    lit_pos(l)  = pos_cnt++;
  1125. X    if (type == C_SET) lit_cset(l) = cset;
  1126. X    else lit_char(l) = ch;            /* type == C_LIT */
  1127. X    Op(node) = opval;
  1128. X    Lit(node) = l;
  1129. X    Nullable(node) = FALSE;
  1130. X    Firstpos(node) = create_pos(lit_pos(l));
  1131. X    Lastpos(node) = Firstpos(node);
  1132. X    return node;
  1133. X}
  1134. X
  1135. X/* parse_cset() takes a pointer to a pointer to a string and parses
  1136. X   a prefix of it denoting a character set literal.  It returns a pointer
  1137. X   to a Re_node node, NULL if there is an error.            */
  1138. X
  1139. Re_node parse_cset(s)
  1140. char **s;
  1141. X{
  1142. X    Ch_Set cs_ptr, curr_ptr, prev_ptr;
  1143. X    char ch;
  1144. X    Ch_Range range;
  1145. X
  1146. X    if (Unexpected(s, ']')) return NULL;
  1147. X    curr_ptr = (Ch_Set) new_node(curr_ptr); cs_ptr = curr_ptr;
  1148. X    while (!Unexpected(s, ']')) {
  1149. X        range = (Ch_Range)new_node(range);
  1150. X    curr_ptr->elt = range;
  1151. X    ch = NextChar(s);
  1152. X    if (ch == '-') return NULL;    /* invalid range */
  1153. X    range->low_bd = ch;
  1154. X    if (**s == NUL) return NULL;
  1155. X    else if (**s == '-') {        /* character range */
  1156. X        (*s)++;
  1157. X        if (Invalid_range(**s, ch)) return NULL;
  1158. X        else range->hi_bd = NextChar(s);
  1159. X    }
  1160. X    else range->hi_bd = ch;
  1161. X    prev_ptr = curr_ptr;
  1162. X    curr_ptr = (Ch_Set) new_node(curr_ptr);
  1163. X    prev_ptr->rest = curr_ptr;
  1164. X    };
  1165. X    if (**s == ']') {
  1166. X    prev_ptr->rest = NULL;
  1167. X    return mk_leaf(LITERAL, C_SET, NUL, cs_ptr);
  1168. X    }
  1169. X    else return NULL;
  1170. X} /* parse_cset */
  1171. X
  1172. X
  1173. X/* parse_wildcard() "parses" a wildcard -- a wildcard is treated as a
  1174. X   character range whose values span all ASCII values.  parse_wildcard()
  1175. X   creates a node representing such a range.                */
  1176. X
  1177. Re_node parse_wildcard()
  1178. X{
  1179. X    Ch_Set s; Ch_Range r;
  1180. X
  1181. X    r = (Ch_Range) new_node(r);
  1182. X    r->low_bd = ASCII_MIN;    /* smallest ASCII value */
  1183. X    r->hi_bd  = ASCII_MAX;    /* greatest ASCII value */
  1184. X    s = (Ch_Set) new_node(s);
  1185. X    s->elt = r;
  1186. X    s->rest = NULL;
  1187. X    return mk_leaf(LITERAL, C_SET, NUL, s);
  1188. X}
  1189. X
  1190. X/* parse_chlit() parses a character literal.  It is assumed that the
  1191. X   character in question does not have any special meaning.  It returns
  1192. X   a pointer to a node for that literal.                */
  1193. X
  1194. Re_node parse_chlit(ch)
  1195. char ch;
  1196. X{
  1197. X    if (ch == NUL) return NULL;
  1198. X    else return mk_leaf(LITERAL, C_LIT, ch, NULL);
  1199. X}
  1200. X
  1201. X
  1202. X/* get_token() returns the next token -- this may be a character
  1203. X   literal, a character set, an escaped character, a punctuation (i.e.
  1204. X   parenthesis), or an operator.  It traverses the character string
  1205. X   representing the RE, given by a pointer s; leaves s positioned
  1206. X   immediately after the unit it parsed, and returns a pointer to
  1207. X   a token node for that unit.  */
  1208. X
  1209. Tok_node get_token(s)
  1210. char **s;
  1211. X{
  1212. X    Tok_node rn = NULL;
  1213. X
  1214. X    if (s == NULL || *s == NULL) return NULL;    /* error */
  1215. X    rn = (Tok_node) new_node(rn);
  1216. X    if (**s == NUL) tok_type(rn) = EOS; /* end of string */
  1217. X    else {
  1218. X    switch (**s) {
  1219. X        case '.':            /* wildcard */
  1220. X        tok_type(rn) = LITERAL;
  1221. X        tok_val(rn) =  parse_wildcard();
  1222. X        if (tok_val(rn) == NULL) return NULL;
  1223. X        break;
  1224. X        case '[':            /* character set literal */
  1225. X        (*s)++;
  1226. X        tok_type(rn) = LITERAL;
  1227. X        tok_val(rn) = parse_cset(s);
  1228. X        if (tok_val(rn) == NULL) return NULL;
  1229. X        break;
  1230. X        case '(':
  1231. X            tok_type(rn) = LPAREN;
  1232. X        break;
  1233. X        case ')' : 
  1234. X            tok_type(rn) = RPAREN;
  1235. X        break;
  1236. X        case '*' :
  1237. X            tok_type(rn) = OPSTAR;
  1238. X        break;
  1239. X        case '|' :
  1240. X            tok_type(rn) = OPALT;
  1241. X        break;
  1242. X        case '?' : 
  1243. X            tok_type(rn) = OPOPT;
  1244. X        break;
  1245. X        case '\\':            /* escaped character */
  1246. X        (*s)++;
  1247. X        default :            /* must be ordinary character */
  1248. X        tok_type(rn) = LITERAL;
  1249. X        tok_val(rn) = parse_chlit(**s);
  1250. X        if (tok_val(rn) == NULL) return NULL;
  1251. X        break;
  1252. X    } /* switch (**s) */
  1253. X    (*s)++;
  1254. X    } /* else */
  1255. X    return rn;
  1256. X}
  1257. X
  1258. X/* cat2() takes a stack of RE-nodes and, if the stack contains
  1259. X   more than one node, returns the stack obtained by condensing
  1260. X   the top two nodes of the stack into a single CAT-node.  If there
  1261. X   is only one node on the stack,  nothing is done.            */
  1262. X
  1263. Stack cat2(stk)
  1264. Stack *stk;
  1265. X{
  1266. X    Re_node r;
  1267. X
  1268. X    if (stk == NULL) return NULL;
  1269. X    if (*stk == NULL || (*stk)->next == NULL) return *stk;
  1270. X    r = (Re_node) new_node(r);
  1271. X    if (r == NULL) return NULL;        /* can't allocate memory */
  1272. X    Op(r) = OPCAT;
  1273. X    Rchild(r) = Pop(stk);
  1274. X    Lchild(r) = Pop(stk);
  1275. X    if (Push(stk, r) == NULL) return NULL;
  1276. X    Nullable(r) = Nullable(Lchild(r)) && Nullable(Rchild(r));
  1277. X    if (Nullable(Lchild(r)))
  1278. X    Firstpos(r) = pset_union(Firstpos(Lchild(r)), Firstpos(Rchild(r)));
  1279. X    else Firstpos(r) = Firstpos(Lchild(r));
  1280. X    if (Nullable(Rchild(r)))
  1281. X    Lastpos(r) = pset_union(Lastpos(Lchild(r)), Lastpos(Rchild(r)));
  1282. X    else Lastpos(r) = Lastpos(Rchild(r));
  1283. X    return *stk;
  1284. X}
  1285. X
  1286. X/* wrap() takes a stack and an operator, takes the top element of the
  1287. X   stack and "wraps" that operator around it, then puts this back on the
  1288. X   stack and returns the resulting stack.                */
  1289. X
  1290. Stack wrap(s, opv)
  1291. Stack *s;
  1292. short opv;
  1293. X{
  1294. X    Re_node r;
  1295. X
  1296. X    if (s == NULL || *s == NULL) return NULL;
  1297. X    r = (Re_node) new_node(r);
  1298. X    if (r == NULL) return NULL;
  1299. X    Op(r) = opv;
  1300. X    Child(r) = Pop(s);
  1301. X    if (Push(s, r) == NULL) return NULL;
  1302. X    Nullable(r) = TRUE;
  1303. X    Firstpos(r) = Firstpos(Child(r));
  1304. X    Lastpos(r)  = Lastpos(Child(r));
  1305. X    return *s;
  1306. X}
  1307. X
  1308. X/* mk_alt() takes a stack and a regular expression, creates an ALT-node
  1309. X   from the top of the stack and the given RE, and replaces the top-of-stack
  1310. X   by the resulting ALT-node.                        */   
  1311. X
  1312. Stack mk_alt(s, r)
  1313. Stack *s;
  1314. Re_node r;
  1315. X{
  1316. X    Re_node node;
  1317. X
  1318. X    if (s == NULL || *s == NULL || r == NULL) return NULL;
  1319. X    node = (Re_node) new_node(node);
  1320. X    if (node == NULL) return NULL;
  1321. X    Op(node) = OPALT;
  1322. X    Lchild(node) = Pop(s);
  1323. X    Rchild(node) = r;
  1324. X    if (Push(s, node) == NULL) return NULL;
  1325. X    Nullable(node) = Nullable(Lchild(node)) || Nullable(Rchild(node));
  1326. X    Firstpos(node) = pset_union(Firstpos(Lchild(node)), Firstpos(Rchild(node)));
  1327. X    Lastpos(node) = pset_union(Lastpos(Lchild(node)), Lastpos(Rchild(node)));
  1328. X    return *s;
  1329. X}
  1330. X
  1331. X/* parse_re() takes a pointer to a string and traverses that string,
  1332. X   returning a pointer to a syntax tree for the regular expression
  1333. X   represented by that string, NULL if there is an error.        */
  1334. X
  1335. Re_node parse_re(s, end)
  1336. char **s;
  1337. short end;
  1338. X{
  1339. X    Stack stk = NULL, temp;
  1340. X    Tok_node next_token;
  1341. X    Re_node re = NULL;
  1342. X
  1343. X    if (s == NULL || *s == NULL) return NULL;
  1344. X    while (TRUE) {
  1345. X    next_token = get_token(s);
  1346. X    if (next_token == NULL) return NULL;
  1347. X    switch (tok_type(next_token)) {
  1348. X        case RPAREN:
  1349. X        retract_token(s);
  1350. X        case EOS:
  1351. X        if (end == tok_type(next_token)) return Top(cat2(&stk));
  1352. X        else return NULL;
  1353. X        case LPAREN:
  1354. X        re = parse_re(s, RPAREN);
  1355. X        if (Push(&stk, re) == NULL) return NULL;
  1356. X        if (tok_type(get_token(s)) != RPAREN || re == NULL) return NULL;
  1357. X        if (Size(stk) > 2) {
  1358. X            temp = stk->next;
  1359. X            stk->next = cat2(&temp);    /* condense CAT nodes */
  1360. X            if (stk->next == NULL) return NULL;
  1361. X            else stk->size = stk->next->size + 1;
  1362. X        }
  1363. X        break;
  1364. X        case OPSTAR:
  1365. X        if (wrap(&stk, OPSTAR) == NULL) return NULL;
  1366. X        break;
  1367. X        case OPOPT:
  1368. X        if (wrap(&stk, OPOPT) == NULL) return NULL;
  1369. X            break;
  1370. X        case OPALT:
  1371. X        if (cat2(&stk) == NULL) return NULL;
  1372. X        re = parse_re(s, end);
  1373. X            if (re == NULL) return NULL;
  1374. X        if (mk_alt(&stk, re) == NULL) return NULL;
  1375. X        break;
  1376. X        case LITERAL:
  1377. X        if (Push(&stk, tok_val(next_token)) == NULL) return NULL;
  1378. X        if (Size(stk) > 2) {
  1379. X            temp = stk->next;
  1380. X            stk->next = cat2(&temp);    /* condense CAT nodes */
  1381. X            if (stk->next == NULL) return NULL;
  1382. X            else stk->size = stk->next->size + 1;
  1383. X        }
  1384. X        break;
  1385. X        default:
  1386. X        printf("parse_re: unknown token type %d\n", tok_type(next_token));
  1387. X        break;
  1388. X    }
  1389. X    }
  1390. X}
  1391. X
  1392. X/* parse() essentially just calls parse_re().  Its purpose is to stick an
  1393. X   end-of-string token at the end of the syntax tree returned by parse_re().
  1394. X   It should really be done in parse_re, but the recursion there makes it
  1395. X   more desirable to have it here.                    */
  1396. X
  1397. Re_node parse(s)
  1398. char *s;
  1399. X{
  1400. X    Re_node tree, temp;
  1401. X    Stack stk = NULL;
  1402. X
  1403. X    tree = parse_re(&s, NUL);
  1404. X    if (tree == NULL || Push(&stk, tree) == NULL) return NULL;
  1405. X    temp = mk_leaf(EOS, C_LIT, NUL, NULL);
  1406. X    if (temp == NULL || Push(&stk, temp) == NULL) return NULL;
  1407. X    final_pos = --pos_cnt;
  1408. X    return Top(cat2(&stk));
  1409. X}
  1410. X
  1411. END_OF_FILE
  1412. if test 9497 -ne `wc -c <'parse.c'`; then
  1413.     echo shar: \"'parse.c'\" unpacked with wrong size!
  1414. fi
  1415. # end of 'parse.c'
  1416. fi
  1417. echo shar: End of archive 2 \(of 3\).
  1418. cp /dev/null ark2isdone
  1419. MISSING=""
  1420. for I in 1 2 3 ; do
  1421.     if test ! -f ark${I}isdone ; then
  1422.     MISSING="${MISSING} ${I}"
  1423.     fi
  1424. done
  1425. if test "${MISSING}" = "" ; then
  1426.     echo You have unpacked all 3 archives.
  1427.     rm -f ark[1-9]isdone
  1428. else
  1429.     echo You still need to unpack the following archives:
  1430.     echo "        " ${MISSING}
  1431. fi
  1432. ##  End of shell archive.
  1433. exit 0
  1434.