10.4. A pattern matching program
This section presents a complete program which makes use of option letters
as program arguments to control the way it performs its job.
The program first processes any arguments that resemble options; the first
argument which is not an option is remembered for use as a ‘search
string’. Any remaining arguments are used to specify file names which are
to be read as input to the program; if no file names are provided, the
program reads from its standard input instead. If a match for the search
string is found in a line of input text, that whole line is printed on the
standard output.
The options function is used to process all option letters
supplied to the program. This program recognises five options:
-c , -i , -l , -n , and
-v . None of these options is required to be followed by an
option argument. When the program is run with one or more of these options
its behaviour is modified as follows:
-c
- the program prints a count of the total number of matching lines it
found in the input file(s). No lines of text are printed.
-i
- when searching for a match, the case of letters in both the input lines
and string is ignored.
-l
- each line of text printed on the output is prefixed with the line number
being examined in the current input file.
-n
- each line of text printed on the output is prefixed with the name of the
file that contained the line.
-v
- the program prints only lines which do not match the string
supplied.
When the program finishes, it returns an exit status to indicate one of
the following situations:
EXIT_SUCCESS
- at least one match was found.
EXIT_FAILURE
- no match was found, or some error occurred.
The program makes extensive use of standard library functions to do all of
the hard work. For example, all of the file handling is performed by calls
to stdio functions. Notice too that the real heart of the
program, the string matching, is simply handled by a call to the
strstr library function.
Here is the code for the whole program. Of course, to get this to work you
would need to compile it together with the code for the options routine
presented above.
/*
* Simple program to print lines from a text file which contain
* the "word" supplied on the command line.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/*
* Declarations for the pattern program
*
*/
#define CFLAG 0x001 /* only count the number of matching lines */
#define IFLAG 0x002 /* ignore case of letters */
#define LFLAG 0x004 /* show line numbers */
#define NFLAG 0x008 /* show input file names */
#define VFLAG 0x010 /* show lines which do NOT match */
extern int OptIndex; /* current index into argv[] */
extern char *OptArg; /* global option argument pointer */
/*
* Fetch command line switches from arguments to main()
*/
int options(int, char **, const char *);
/*
* Record the required options ready to control program beaviour
*/
unsigned set_flags(int, char **, const char *);
/*
* Check each line of the input file for a match
*/
int look_in(const char *, const char *, unsigned);
/*
* Print a line from the input file on the standard output
* in the format specified by the command line switches
*/
void print_line(unsigned mask, const char *fname,
int lnno, const char *text);
static const char
/* Legal options for pattern */
*OptString = "cilnv",
/* message when options or arguments incorrect */
*errmssg = "usage: pattern [-cilnv] word [filename]\n";
int main(int argc, char *argv[])
{
unsigned flags = 0;
int success = 0;
char *search_string;
if(argc < 2){
fprintf(stderr, errmssg);
exit(EXIT_FAILURE);
}
flags = set_flags(argc, argv, OptString);
if(argv[OptIndex])
search_string = argv[OptIndex++];
else {
fprintf(stderr, errmssg);
exit(EXIT_FAILURE);
}
if(flags & IFLAG){
/* ignore case by dealing only with lowercase */
char *p;
for(p = search_string ; *p ; p++)
if(isupper(*p))
*p = tolower(*p);
}
if(argv[OptIndex] == NULL){
/* no file name given, so use stdin */
success = look_in(NULL, search_string, flags);
} else while(argv[OptIndex] != NULL)
success += look_in(argv[OptIndex++],
search_string, flags);
if(flags & CFLAG)
printf("%d\n", success);
exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
}
unsigned set_flags(int argc, char **argv, const char *opts)
{
unsigned flags = 0;
int ch = 0;
while((ch = options(argc, argv, opts)) != -1){
switch(ch){
case 'c':
flags |= CFLAG;
break;
case 'i':
flags |= IFLAG;
break;
case 'l':
flags |= LFLAG;
break;
case 'n':
flags |= NFLAG;
break;
case 'v':
flags |= VFLAG;
break;
case '?':
fprintf(stderr, errmssg);
exit(EXIT_FAILURE);
}
}
return flags;
}
int look_in(const char *infile, const char *pat, unsigned flgs)
{
FILE *in;
/*
* line[0] stores the input line as read,
* line[1] is converted to lower-case if necessary
*/
char line[2][BUFSIZ];
int lineno = 0;
int matches = 0;
if(infile){
if((in = fopen(infile, "r")) == NULL){
perror("pattern");
return 0;
}
} else
in = stdin;
while(fgets(line[0], BUFSIZ, in)){
char *line_to_use = line[0];
lineno++;
if(flgs & IFLAG){
/* ignore case */
char *p;
strcpy(line[1], line[0]);
for(p = line[1] ; *p ; *p++)
if(isupper(*p))
*p = tolower(*p);
line_to_use = line[1];
}
if(strstr(line_to_use, pat)){
matches++;
if(!(flgs & VFLAG))
print_line(flgs, infile, lineno, line[0]);
} else if(flgs & VFLAG)
print_line(flgs, infile, lineno, line[0]);
}
fclose(in);
return matches;
}
void print_line(unsigned mask, const char *fname,
int lnno, const char *text)
{
if(mask & CFLAG)
return;
if(mask & NFLAG)
printf("%s:", *fname ? fname : "stdin");
if(mask & LFLAG)
printf(" %d :", lnno);
printf("%s", text);
} Example 10.3
|
Printer-friendly version
The C Book
This book is published as a matter of historical interest.
Please read the
copyright and disclaimer information.
GBdirect Ltd provides up-to-date training and consultancy in
C,
Embedded C,
C++
and a wide range of
other subjects based on
open standards if you happen to be interested.
|