4 * The information in this document is subject to change
5 * without notice and should not be construed as a commitment
6 * by Digital Equipment Corporation or by DECUS.
8 * Neither Digital Equipment Corporation, DECUS, nor the authors
9 * assume any responsibility for the use or reliability of this
10 * document or the described software.
12 * Copyright (C) 1980, DECUS
15 * General permission to copy or modify, but not for profit, is
16 * hereby granted, provided that the above copyright notice is
17 * included and reference made to the fact that reproduction
18 * privileges were granted by DECUS.
30 char *cclass(char *,char *);
32 void badpat(char *,char *,char *);
33 void grep(FILE *, char *);
35 char * pmatch(char *,char *);
41 * Runs on the Decus compiler or on vms.
42 * Converted for BDS compiler (under CP/M-80), 20-Jan-83, by Chris Kern.
44 * Converted to IBM PC with CI-C86 C Compiler June 1983 by David N. Smith
48 * grep :== "$disk:[account]grep" (native)
49 * grep :== "$disk:[account]grep grep" (Decus)
51 * See below for more information.
54 char *documentation
[] = {
55 "grep searches a file for a given pattern. Execute by",
56 " grep [flags] regular_expression file_list",
58 "Flags are single characters preceeded by '-':",
59 " -c Only a count of matching lines is printed",
60 " -f Print file name for matching lines switch, see below",
61 " -n Each line is preceeded by its line number",
62 " -v Only print non-matching lines",
64 "The file_list is a list of files (wildcards are acceptable on RSX modes).",
66 "The file name is normally printed if there is a file given.",
67 "The -f flag reverses this action (print name no file, not if more).",
71 "The regular_expression defines the pattern to search for. Upper- and",
72 "lower-case are always ignored. Blank lines never match. The expression",
73 "should be quoted to prevent file-name translation.",
74 "x An ordinary character (not mentioned below) matches that character.",
75 "'\\' The backslash quotes any character. \"\\$\" matches a dollar-sign.",
76 "'^' A circumflex at the beginning of an expression matches the",
77 " beginning of a line.",
78 "'$' A dollar-sign at the end of an expression matches the end of a line.",
79 "'.' A period matches any character except \"new-line\".",
80 "':a' A colon matches a class of characters described by the following",
81 "':d' character. \":a\" matches any alphabetic, \":d\" matches digits,",
82 "':n' \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
83 "': ' other control characters, such as new-line.",
84 "'*' An expression followed by an asterisk matches zero or more",
85 " occurrances of that expression: \"fo*\" matches \"f\", \"fo\"",
87 "'+' An expression followed by a plus sign matches one or more",
88 " occurrances of that expression: \"fo+\" matches \"fo\", etc.",
89 "'-' An expression followed by a minus sign optionally matches",
91 "'[]' A string enclosed in square brackets matches any character in",
92 " that string, but no others. If the first character in the",
93 " string is a circumflex, the expression matches any character",
94 " except \"new-line\" and the characters in the string. For",
95 " example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
96 " matches \"abc\" but not \"axb\". A range of characters may be",
97 " specified by two characters separated by \"-\". Note that,",
98 " [a-z] matches alphabetics, while [z-a] never matches.",
99 "The concatenation of regular expressions is a regular expression.",
102 /*$define VMS VMS native compiler */
103 /*$define error(s) _error(s) */
127 int debug
= 0; /* Set for debug code */
134 /*******************************************************/
144 usage("No arguments");
145 if (argc
== 2 && argv
[1][0] == '?' && argv
[1][1] == 0) {
152 for (i
=1; i
< argc
; ++i
) {
182 usage("Unknown flag");
187 } else if (!gotpattern
) {
199 fflag
= fflag
^ (nfile
> 0);
200 for (i
=1; i
< argc
; ++i
) {
202 if ((f
=fopen(p
, "r")) == NULL
)
213 /*******************************************************/
217 printf("File %s:\n", s
);
219 /*******************************************************/
223 fprintf(stderr
, "%s: cannot open\n", s
);
225 /*******************************************************/
233 for (dp
= hp
; *dp
; dp
++)
236 /*******************************************************/
240 fprintf(stderr
, "?GREP-E-%s\n", s
);
242 "Usage: grep [-cfnv] pattern [file ...]. grep ? for help\n");
245 /*******************************************************/
247 char *source
; /* Pattern to compile */
249 * Compile the pattern into global pbuf[]
252 register char *s
; /* Source string pointer */
253 register char *lp
; /* Last pattern pointer */
254 register int c
; /* Current character */
256 char *spp
; /* Save beginning of pattern */
257 char *cclass(); /* Compile class routine */
260 printf("Pattern = \"%s\"\n", s
);
264 * STAR, PLUS and MINUS are special.
266 if (c
== '*' || c
== '+' || c
== '-') {
273 badpat("Illegal occurrance op.", source
, s
);
276 spp
= pp
; /* Save pattern end */
277 while (--pp
> lp
) /* Move pattern down */
278 *pp
= pp
[-1]; /* one byte */
279 *pp
= (c
== '*') ? STAR
:
280 (c
== '-') ? MINUS
: PLUS
;
281 pp
= spp
; /* Restore pattern end */
287 lp
= pp
; /* Remember start */
299 s
= cclass(source
, s
);
321 badpat("Unknown : type", source
, s
);
325 else badpat("No : type", source
, s
);
335 store(0); /* Terminate string */
337 for (lp
= pbuf
; lp
< pp
;) {
338 if ((c
= (*lp
++ & 0377)) < ' ')
340 else printf("%c ", c
);
345 /*******************************************************/
348 char *source
; /* Pattern start -- for error msg. */
349 char *src
; /* Class start */
351 * Compile a class (within [])
354 register char *s
; /* Source pointer */
355 register char *cp
; /* Pattern start */
356 register int c
; /* Current character */
366 store(0); /* Byte count */
367 while ((c
= *s
++) && c
!=']') {
368 if (c
== '\\') { /* Store quoted char */
369 if ((c
= *s
++) == '\0') /* Gotta get something */
370 badpat("Class terminates badly", source
, s
);
371 else store(tolower(c
));
374 (pp
- cp
) > 1 && *s
!= ']' && *s
!= '\0') {
375 c
= pp
[-1]; /* Range start */
376 pp
[-1] = RANGE
; /* Range signal */
377 store(c
); /* Re-store start */
378 c
= *s
++; /* Get end char and*/
379 store(tolower(c
)); /* Store it */
382 store(tolower(c
)); /* Store normal char */
386 badpat("Unterminated class", source
, s
);
387 if ((c
= (pp
- cp
)) >= 256)
388 badpat("Class too large", source
, s
);
390 badpat("Empty class", source
, s
);
394 /*******************************************************/
398 if (pp
> &pbuf
[PMAX
-1])
399 error("Pattern too complex\n");
402 /*******************************************************/
403 void badpat(message
, source
, stop
)
404 char *message
; /* Error message */
405 char *source
; /* Pattern start */
406 char *stop
; /* Pattern end */
408 fprintf(stderr
, "-GREP-E-%s, pattern is\"%s\"\n", message
, source
);
409 fprintf(stderr
, "-GREP-E-Stopped at byte %d, '%c'\n",
410 (int)(stop
-source
), stop
[-1]);
411 error("?GREP-E-Bad pattern\n");
413 /*******************************************************/
415 FILE *fp
; /* File to process */
416 char *fn
; /* File name (for -f option) */
418 * Scan the file for the pattern in pbuf[]
421 register int lno
, count
, m
;
424 while (fgets(lbuf
, LMAX
, fp
)) {
427 if ((m
&& !vflag
) || (!m
&& vflag
)) {
436 printf("%s\n", lbuf
);
443 printf("%d\n", count
);
446 /*******************************************************/
449 * Match the current line (in lbuf[]), return 1 if it does.
452 register char *l
; /* Line pointer */
454 for (l
= lbuf
; *l
; l
++) {
460 /*******************************************************/
462 pmatch(line
, pattern
)
463 char *line
; /* (partial) line to match */
464 char *pattern
; /* (partial) pattern to match */
466 register char *l
; /* Current line pointer */
467 register char *p
; /* Current pattern pointer */
468 register char c
; /* Current character */
469 char *e
; /* End for STAR and PLUS match */
470 int op
; /* Pattern operation */
471 int n
; /* Class counter */
472 char *are
; /* Start of STAR match */
475 printf("pmatch(\"%s\")\n", line
);
477 while ((op
= *p
++) != ENDPAT
) {
479 printf("byte[%d] = 0%o, '%c', op = 0%o\n",
480 (int)(l
-line
), *l
, *l
, op
);
483 if (tolower(*l
) != *p
++)
500 if ((c
= *l
++) < '0' || (c
> '9'))
506 if (c
< 'a' || c
> 'z')
512 if (c
>= 'a' && c
<= 'z')
514 else if (c
< '0' || c
> '9')
519 if (c
== 0 || c
> ' ')
531 if (c
>= p
[-2] && c
<= p
[-1])
537 if ((op
== CLASS
) == (n
<= 1))
543 e
= pmatch(l
, p
); /* Look for a match */
544 while (*p
++ != ENDPAT
); /* Skip over pattern */
545 if (e
) /* Got a match? */
546 l
= e
; /* Yes, update string */
547 break; /* Always succeeds */
548 case PLUS
: /* One or more ... */
549 if ((l
= pmatch(l
, p
)) == 0)
550 return(0); /* Gotta have a match */
551 case STAR
: /* Zero or more ... */
552 are
= l
; /* Remember line start */
553 while (*l
&& (e
= pmatch(l
, p
)))
554 l
= e
; /* Get longest match */
555 while (*p
++ != ENDPAT
); /* Skip over pattern */
556 while (l
>= are
) { /* Try to match rest */
557 if ((e
= pmatch(l
, p
)))
559 --l
; /* Nope, try earlier */
561 return(0); /* Nothing else worked */
563 printf("Bad op code %d\n", op
);
564 error("Cannot happen -- match\n");
569 /*******************************************************/
573 fprintf(stderr
, "%s", s
);