-
Notifications
You must be signed in to change notification settings - Fork 0
/
mygrep.c
178 lines (160 loc) · 5.36 KB
/
mygrep.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <regex.h>
#include "isdir.h"
#ifndef EXIT_TROUBLE
# define EXIT_TROUBLE 2
#endif
#define MAXLINE 1000
struct global_args_t {
int is_invert; // for option -v
int show_line_number; // for option -n
int show_filename; // flag for multi file of -H option
} gargs;
void display_usage(int status, char* message, int errcode)
{
if (message != NULL || errcode != 0)
fprintf(stderr, "ERROR: %s\nError Code: %d\n----------------------------------------\n", message, errcode);
puts("A tiny grep impletment as the homework of Linux Programming");
puts("Usage: mygrep [OPTION]... PATTERN [FILE]...");
puts("Search for PATTERN in each FILE or standard input.");
puts("Options:");
puts("-h Show this help");
puts("-E Interpret PATTERN as an extended regular expression.");
puts("-e PATTERN Use PATTERN as the pattern. This can be used to protect a");
puts(" pattern beginning with a hyphen (-)." );
puts("-H Print the filename for each match.");
puts("-i Ignore case distinctions.");
puts("-n Print line number with output lines.");
puts("-v Select non-matching lines.");
exit(status);
}
char *read_pattern(const char* source) {
char *pattern = malloc( (strlen(source) + 1) * sizeof(char) );
if (pattern != 0) {
strcpy(pattern, source);
return pattern;
} else {
display_usage(EXIT_TROUBLE, "Not enough memory space", -1);
}
return NULL; // won't be here
}
int do_grep(regex_t* preg, char* filename) {
#ifdef DEBUG
printf("----preg %d to grep file %s\n", (int)preg, filename);
#endif
char *label = NULL;
FILE *file = NULL;
if (strcmp(filename, "-") == 0) {
label = "(standard input)";
file = stdin;
} else {
label = filename;
file = fopen(filename, "r");
if (file == NULL)
display_usage(EXIT_TROUBLE, "Open file failed.", errno);
}
char linebuf[MAXLINE];
int regexec_code = 0;
int line_number = 0;
while ( fgets(linebuf, MAXLINE, file) != NULL ) {
line_number++;
int len = strlen(linebuf) - 1;
if ( linebuf[len] == '\n' )
linebuf[len] = 0; //replace newline with null
regexec_code = regexec(preg, linebuf, 0, NULL, 0);
if (regexec_code > REG_NOMATCH) { // >1?
regfree(preg);
display_usage(EXIT_TROUBLE, "Match Error.", regexec_code);
}
/* this condition equals to:
(regexec_code == 0 && gargs.is_invert == 0)
|| (regexec_code == 1 && gargs.is_invert == 1)
*/
if ( !(regexec_code ^ gargs.is_invert) ) {
if (gargs.show_filename && gargs.show_line_number)
printf("%s:%d:%s\n", label, line_number, linebuf);
else if (gargs.show_filename && !gargs.show_line_number)
printf("%s:%s\n", label, linebuf);
else if (!gargs.show_filename && gargs.show_line_number)
printf("%d:%s\n", line_number, linebuf);
else
puts(linebuf);
}
}
fclose(file);
return regexec_code;
}
int main(int argc, char **argv) {
char *pattern = NULL;
int status = 0;
regex_t *preg = malloc(sizeof(regex_t));
int regcomp_flags = REG_BASIC;
int regex_errcode = 0;
gargs.is_invert = 0;
gargs.show_filename = 0;
gargs.show_line_number = 0;
static const char *optstr = "Ee:Hhinv";
int opt = 0;
while ((opt = getopt(argc, argv, optstr)) != -1) {
switch(opt) {
case 'e':
pattern = read_pattern(optarg);
break;
case 'E':
regcomp_flags |= REG_EXTENDED;
break;
case 'H':
gargs.show_filename = 1;
break;
case 'h':
display_usage(EXIT_SUCCESS, NULL, EXIT_SUCCESS);
break;
case 'i':
regcomp_flags |= REG_ICASE;
break;
case 'n':
gargs.show_line_number = 1;
break;
case 'v':
gargs.is_invert = 1;
break;
default: // shouldn't reach here
break;
}
}
if ( !pattern ) { // no -e option
if (argc - optind < 1) {
display_usage(EXIT_TROUBLE, "Please provide pattern.", -1);
} else {
pattern = read_pattern(argv[optind++]);
}
}
if ( (regex_errcode = regcomp(preg, pattern, regcomp_flags)) != 0 ) {
regfree(preg);
display_usage(EXIT_TROUBLE, "Pattern invalid.", regex_errcode);
}
free(pattern);
#ifdef DEBUG
printf("optind:%d, argc:%d\n\n", optind, argc);
#endif
if (argc == optind) { // use stdin
status = do_grep(preg, "-");
regfree(preg);
exit(status);
}
if (argc - optind > 1)
gargs.show_filename = 1;
while (optind < argc) {
char *filename = argv[optind++];
if (isdir(filename))
fprintf(stderr, "[WARNING] %s is a directory.\n", filename);
//display_usage(EXIT_TROUBLE, "Please provide a file instead of directory", -1);
status = do_grep(preg, filename);
}
regfree(preg);
exit(status);
}