1 /*
2 
3   grepcidr 2.0 - Filter IPv4 and IPv6 addresses matching CIDR patterns
4   Copyright (C) 2004 - 2014  Jem E. Berkes <jem@berkes.ca>
5   www.berkes.ca
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 
21 */
22 
23 #include <ctype.h>
24 #include <errno.h>
25 #include <limits.h>
26 #include <memory.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 
36 #define EXIT_OK		0
37 #define EXIT_NOMATCH	1
38 #define EXIT_ERROR	2
39 
40 #define TXT_VERSION	"grepcidr 2.0\nCopyright (C) 2004 - 2014  Jem E. Berkes <jem@berkes.ca>\n"
41 #define TXT_USAGE	"Usage:\n" \
42 			"\tgrepcidr [-V] [-cisvx] PATTERN [FILE...]\n" \
43 			"\tgrepcidr [-V] [-cisvx] [-e PATTERN | -f PATFILE] [FILE...]\n"
44 #define TXT_USAGE2	"grepcidr: Specify PATTERN or -f FILE to read patterns from\n"
45 #define TXT_BADPAT	"grepcidr: Not a valid pattern"
46 #define TXT_FATAL	"grepcidr: Fatal error: unexpected size of data type(s) on this system!\n"
47 #define TXT_MEMORY	"grepcidr: Fatal error: out of memory!\n"
48 
49 /* Use GREPERROR instead of perror */
50 #define GREPERROR(prefix) fprintf(stderr, "grepcidr: %s: %s\n", prefix, strerror(errno));
51 
52 #define MAXFIELD	512
53 #define TOKEN_SEPS	" \t,\r\n"	/* so user can specify multiple patterns on command line */
54 #define INIT_NETWORKS	8192
55 
56 /*
57 	Specifies a network. Whether originally in CIDR format (IP/mask)
58 	or a range of IPs (IP_start-IP_end), spec is converted to a range.
59 	The range is min to max (32-bit IPs) inclusive.
60 */
61 struct netspec
62 {
63 	unsigned int min;
64 	unsigned int max;
65 };
66 
67 /* IPv6 version of pattern */
68 struct netspec6
69 {
70 	unsigned char min[16];
71 	unsigned char max[16];
72 };
73 
74 
75 /* Macro to test for valid IP address in four integers */
76 #define VALID_IP(IP) ((IP[0]<256) && (IP[1]<256) && (IP[2]<256) && (IP[3]<256))
77 /* Macro to build 32-bit IP from four integers */
78 #define BUILD_IP(IP) ((IP[0]<<24) | (IP[1]<<16) | (IP[2]<<8) | IP[3])
79 
80 /* Parameters and macros for hints-based search */
81 #define HINT_LOOKAHEAD	4	/* e.g. if hints looks at P[4], then use 4 */
82 
83 #define IPV4_FIELD		"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz."
84 #define IPV4_BUFSIZE	16
85 #define IPV4_HINT(P)	(isdigit((int)P[0]) && ((P[1]=='.') || (P[2]=='.') || (P[3]=='.')))
86 
87 #define IPV6_FIELD		"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz:."
88 #define IPV6_BUFSIZE	46
89 #define IPV6_HINT1(P)	((P[0]==':') && (P[1]==':') && isxdigit((int)P[2]))
90 #define IPV6_HINT2(P)	(isxdigit((int)P[0]) && (P[1]==':'))
91 #define IPV6_HINT3(P)	(isxdigit((int)P[0]) && isxdigit((int)P[1]) && (P[2]==':'))
92 #define IPV6_HINT4(P)	(isxdigit((int)P[0]) && isxdigit((int)P[1]) && isxdigit((int)P[2]) && (P[3]==':'))
93 #define IPV6_HINT5(P)	(isxdigit((int)P[0]) && isxdigit((int)P[1]) && isxdigit((int)P[2]) && isxdigit((int)P[3]) && (P[4]==':'))
94 
95 
96 /* Global variables */
97 int anymatch = 0;				/* did anything match? for exit code */
98 int invert = 0;				/* flag for inverted mode */
99 unsigned int counting = 0;		/* when non-zero, counts matches */
100 int include_noip = 0;			/* flag to include lines without IPs when inverting */
101 int strict_align = 0;			/* flag to enforce strict base alignment */
102 int strict_nosearch = 0;			/* flag for original style, single IP match */
103 int match_one = 0;				/* for -v, have matched one IP on this line */
104 int seen_ip = 0;				/* for -v, have seen an IP on this line */
105 int shownames = 0;				/* show file names with output lines */
106 
107 unsigned int patterns = 0;		/* total patterns in IPv4 array */
108 unsigned int capacity = 0;		/* current capacity of IPv4 array */
109 struct netspec* array = NULL;		/* IPv4 array of patterns */
110 
111 unsigned int patterns6 = 0;		/* total patterns in IPv6 array */
112 unsigned int capacity6 = 0;		/* current capacity of IPv6 array */
113 struct netspec6* array6 = NULL;	/* IPv6 array of patterns */
114 
115 /*
116 	Insert new spec inside array of network spec
117 	Dynamically grow array buffer as needed
118 	The array must have already been initially allocated, with valid capacity
119 */
array_insert(struct netspec * newspec)120 void array_insert(struct netspec* newspec)
121 {
122 	if (patterns == capacity)
123 	{
124 		capacity *= 2;
125 		array = realloc(array, capacity*sizeof(struct netspec));
126 	}
127 	array[patterns++] = *newspec;
128 }
129 
array_insert6(struct netspec6 * newspec)130 void array_insert6(struct netspec6* newspec)
131 {
132 	if (patterns6 == capacity6)
133 	{
134 		capacity6 *= 2;
135 		array6 = realloc(array6, capacity6*sizeof(struct netspec6));
136 	}
137 	array6[patterns6++] = *newspec;
138 }
139 
140 
141 /*
142 	Convert IPv4 address string at location p, string length len,
143 	into result which must point to an unsigned int.
144 	Returns 1 on success, 0 on failure
145 */
ipv4_to_uint(const char * p,unsigned long len,unsigned int * result)146 int ipv4_to_uint(const char* p, unsigned long len, unsigned int* result)
147 {
148 	unsigned char parsed[4] = { 0 };
149 	char buf[IPV4_BUFSIZE];
150 	if (len > IPV4_BUFSIZE-1) return 0;	/* too long to be a valid IPv4 */
151 	memset(buf, 0, sizeof(buf));
152 	memcpy(buf, p, len);
153 	if (inet_pton(AF_INET, buf, &parsed) == 1)
154 	{
155 		*result = BUILD_IP(parsed);
156 		return 1;
157 	}
158 	else
159 		return 0;
160 }
161 
162 
163 /*
164 	Convert IPv6 address string at location p, string length len,
165 	into result which must point to 16 byte unsigned char array.
166 	This uses system's inet_pton() which is quite fast on Linux.
167 	Returns 1 on success, 0 on failure
168 */
ipv6_to_uchar(const char * p,unsigned long len,unsigned char * result)169 int ipv6_to_uchar(const char* p, unsigned long len, unsigned char* result)
170 {
171 	char buf[IPV6_BUFSIZE];
172 	if (len > IPV6_BUFSIZE-1) return 0;	/* too long to be a valid IPv6 */
173 	memset(buf, 0, sizeof(buf));
174 	memcpy(buf, p, len);
175 	if (inet_pton(AF_INET6, buf, result) == 1)
176 		return 1;
177 	else
178 		return 0;
179 }
180 
181 
182 /*
183 	Increment IPv6 address (array at input) and store result.
184 	Both input and result must point to 16 byte unsigned char array.
185 */
ipv6_increment(unsigned char * input,unsigned char * result)186 void ipv6_increment(unsigned char* input, unsigned char* result)
187 {
188 	int i, carry=1;
189 	for (i=15; i>=0; i--)	/* network byte order is big endian */
190 	{
191 		int sum = input[i] + carry;
192 		result[i] = (unsigned char)(sum & 0xFF);
193 		carry = sum >> 8;
194 	}
195 }
196 
197 
198 /*
199 	Given string, fills in the struct netspec (must be allocated)
200 	Accept CIDR IP/mask format or IP_start-IP_end range.
201 	Returns true (nonzero) on success, false (zero) on failure.
202 */
net_parse(const char * line,struct netspec * spec)203 int net_parse(const char* line, struct netspec* spec)
204 {
205 	unsigned int IP1[4], IP2[4];
206 	int maskbits = 32;	/* if using CIDR IP/mask format */
207 
208 	/* Try parsing IP/mask, CIDR format */
209 	if (strchr(line, '/') && (sscanf(line, "%u.%u.%u.%u/%d", &IP1[0], &IP1[1], &IP1[2], &IP1[3], &maskbits) == 5)
210 		&& VALID_IP(IP1))
211 	{
212 		unsigned int ipaddress = BUILD_IP(IP1);
213 		if (maskbits == 0)
214 		{
215 			if (strict_align && (ipaddress != 0))
216 				return 0;	/* invalid */
217 			spec->min = 0;
218 			spec->max = 0xFFFFFFFF;
219 			return 1;
220 		}
221 		else if ((maskbits < 0) || (maskbits > 32))
222 			return 0;	/* invalid */
223 		if (strict_align && (ipaddress & (((1 << (32-maskbits))-1) & 0xFFFFFFFF)))
224 			return 0;	/* invalid, there are non-zero host bits */
225 		spec->min = ipaddress & (~((1 << (32-maskbits))-1) & 0xFFFFFFFF);
226 		spec->max = spec->min | (((1 << (32-maskbits))-1) & 0xFFFFFFFF);
227 		return 1;
228 	}
229 	/* Try parsing a range. Spaces around hyphen are optional. */
230 	else if (strchr(line, '-') && (sscanf(line, "%u.%u.%u.%u - %u.%u.%u.%u", &IP1[0], &IP1[1], &IP1[2], &IP1[3],
231 		&IP2[0], &IP2[1], &IP2[2], &IP2[3]) == 8) && VALID_IP(IP1) && VALID_IP(IP2))
232 	{
233 		spec->min = BUILD_IP(IP1);
234 		spec->max = BUILD_IP(IP2);
235 		if (spec->max >= spec->min)
236 			return 1;
237 		else
238 			return 0;
239 	}
240 	/* Try simple IP address */
241 	else if ((sscanf(line, "%u.%u.%u.%u", &IP1[0], &IP1[1], &IP1[2], &IP1[3]) == 4) && VALID_IP(IP1))
242 	{
243 		spec->min = BUILD_IP(IP1);
244 		spec->max = spec->min;
245 		return 1;
246 	}
247 	return 0;	/* could not parse */
248 }
249 
250 
251 /*
252 	Parse an IPv6 pattern (struct netspec6)
253 	Accepts IP or IP/x format
254 	Returns true (nonzero) on success, false (zero) on failure.
255 */
net_parse6(const char * line,struct netspec6 * v6spec)256 int net_parse6(const char* line, struct netspec6* v6spec)
257 {
258 	size_t field_len = strspn(line, IPV6_FIELD);
259 	unsigned char address[16] = { 0 };
260 	int maskbits = 128;
261 
262 	if (!ipv6_to_uchar(line, field_len, address))
263 		return 0;	/* no IPv6 found here */
264 	/* Simple IPv6 address is in address */
265 	memcpy(v6spec->min, address, 16);
266 	memcpy(v6spec->max, address, 16);
267 	if (sscanf(line+field_len, "/%d", &maskbits) == 1)
268 	{
269 		unsigned char mask;
270 		int bytenum;
271 		if ((maskbits < 0) || (maskbits > 128))
272 			return 0;	/* invalid */
273 		if (maskbits == 0)
274 			bytenum = -1;
275 		else
276 		{
277 			bytenum = (maskbits-1)/8;
278 			mask = (unsigned char)0xFF << (7 - ((maskbits-1)%8));
279 			v6spec->min[bytenum] &= mask;
280 			v6spec->max[bytenum] |= (unsigned char)~mask;
281 		}
282 		for (++bytenum; bytenum<16; bytenum++)
283 		{
284 			v6spec->min[bytenum] = 0;
285 			v6spec->max[bytenum] = 0xFF;
286 		}
287 		if (strict_align && (memcmp(v6spec->min, address, 16) != 0))
288 			return 0;	/* bad CIDR alignment */
289 	}
290 	return 1;
291 }
292 
293 
294 /* Compare two netspecs, for sorting. Comparison is done on minimum of range */
netsort(const void * a,const void * b)295 int netsort(const void* a, const void* b)
296 {
297 	unsigned int c1 = ((struct netspec*)a)->min;
298 	unsigned int c2 = ((struct netspec*)b)->min;
299 	if (c1 < c2) return -1;
300 	if (c1 > c2) return +1;
301 	return 0;
302 }
303 
304 
netsort6(const void * a,const void * b)305 int netsort6(const void* a, const void* b)
306 {
307 	unsigned char* c1 = ((struct netspec6*)a)->min;
308 	unsigned char* c2 = ((struct netspec6*)b)->min;
309 	int n = memcmp(c1, c2, 16);
310 	if (n < 0) return -1;
311 	if (n > 0) return +1;
312 	return 0;
313 }
314 
315 
316 /* Compare two netspecs, for searching. Test if key (only min) is inside range */
netsearch(const void * a,const void * b)317 int netsearch(const void* a, const void* b)
318 {
319 	unsigned int key = ((struct netspec*)a)->min;
320 	unsigned int min = ((struct netspec*)b)->min;
321 	unsigned int max = ((struct netspec*)b)->max;
322 	if (key < min) return -1;
323 	if (key > max) return +1;
324 	return 0;
325 }
326 
327 
netsearch6(const void * a,const void * b)328 int netsearch6(const void* a, const void* b)
329 {
330 	unsigned char* key = ((struct netspec6*)a)->min;
331 	unsigned char* min = ((struct netspec6*)b)->min;
332 	unsigned char* max = ((struct netspec6*)b)->max;
333 	if (memcmp(key, min, 16) < 0) return -1;
334 	if (memcmp(key, max, 16) > 0) return +1;
335 	return 0;
336 }
337 
338 
339 /* Action to take upon a matching line, print the line or count it */
print_or_count(char * line,const char * filename)340 void print_or_count(char* line, const char* filename)
341 {
342 	anymatch = 1;
343 	if (counting)
344 		counting++;
345 	else
346 	{
347 		if (filename && shownames)
348 			printf("%s:", filename);
349 		printf("%s", line);
350 	}
351 }
352 
353 
354 /*
355 	Search for this IP (passed in key) among loaded network patterns and determine if it's a "match"
356 	Provide either an IPv4 or IPv6 key, but not both.  Returns true or false, whether IP matched patterns.
357 */
match_ip(struct netspec * v4key,struct netspec6 * v6key,char * line,const char * filename)358 int match_ip(struct netspec* v4key, struct netspec6* v6key, char* line, const char* filename)
359 {
360 	int match = 0;
361 	seen_ip = 1;
362 	if (v4key && bsearch(v4key, array, patterns, sizeof(struct netspec), netsearch))
363 		match = 1;
364 	else if (v6key && bsearch(v6key, array6, patterns6, sizeof(struct netspec6), netsearch6))
365 		match = 1;
366 	if (match)
367 	{
368 		match_one = 1;
369 		if (!invert) print_or_count(line, filename);	/* take action if not using -v */
370 	}
371 	return match;
372 }
373 
374 
375 /* Load patterns defining networks */
load_patterns(const char * pat_filename,char * pat_strings)376 void load_patterns(const char* pat_filename, char* pat_strings)
377 {
378 	if (pat_filename)
379 	{
380 		FILE* data = fopen(pat_filename, "r");
381 		if (data)
382 		{
383 			char line[MAXFIELD];
384 			while (fgets(line, sizeof(line), data))
385 			{
386 				struct netspec ipv4_pat;
387 				struct netspec6 ipv6_pat;
388 				if ((*line=='#')||(*line=='\n')||(*line=='\r'))
389 					continue;	/* skip blank lines and comments */
390 				if (net_parse(line, &ipv4_pat))
391 					array_insert(&ipv4_pat);
392 				else if (net_parse6(line, &ipv6_pat))
393 					array_insert6(&ipv6_pat);
394 				else
395 					fprintf(stderr, TXT_BADPAT ": %s", line);
396 			}
397 			fclose(data);
398 		}
399 		else
400 		{
401 			GREPERROR(pat_filename);
402 			exit(EXIT_ERROR);
403 		}
404 	}
405 	if (pat_strings)
406 	{
407 		char* token = strtok(pat_strings, TOKEN_SEPS);
408 		while (token)
409 		{
410 			struct netspec ipv4_pat;
411 			struct netspec6 ipv6_pat;
412 			if (net_parse(token, &ipv4_pat))
413 				array_insert(&ipv4_pat);
414 			else if (net_parse6(token, &ipv6_pat))
415 				array_insert6(&ipv6_pat);
416 			else
417 				fprintf(stderr, TXT_BADPAT ": %s\n", token);
418 			token = strtok(NULL, TOKEN_SEPS);
419 		}
420 	}
421 
422 	/* Prepare array for rapid searching */
423 	if (patterns)
424 	{
425 		unsigned int item;
426 		qsort(array, patterns, sizeof(struct netspec), netsort);
427 		/* cure overlaps so that ranges are disjoint and consistent */
428 		for (item=1; item<patterns; item++)
429 		{
430 			if (array[item].max <= array[item-1].max)
431 				array[item] = array[item-1];
432 			else if (array[item].min <= array[item-1].max)
433 				array[item].min = array[item-1].max + 1;	/* overflow possibility */
434 		}
435 	}
436 	if (patterns6)
437 	{
438 		unsigned int item;
439 		qsort(array6, patterns6, sizeof(struct netspec6), netsort6);
440 		/* cure overlaps so that ranges are disjoint and consistent */
441 		for (item=1; item<patterns6; item++)
442 		{
443 			if (memcmp(array6[item].max, array6[item-1].max, 16) <= 0)
444 				array6[item] = array6[item-1];
445 			else if (memcmp(array6[item].min, array6[item-1].max, 16) <= 0)
446 				ipv6_increment(array6[item-1].max, array6[item].min);
447 		}
448 	}
449 }
450 
451 
452 /*
453 	Scan the buffer (one line) for IPv4 and IPv6 addresses.
454 	While moving through buffer, look for 'hints' that a valid address may
455 	exist at this location before trying parser.  This is much faster than regex.
456 	We check p[0] so that we stop at terminating nul char.
457 */
scan_with_hints(char * buffer,unsigned long bufsize,const char * filename)458 void scan_with_hints(char* buffer, unsigned long bufsize, const char* filename)
459 {
460 	char* p;
461 	char* max = buffer + bufsize - 1 - HINT_LOOKAHEAD;
462 	if (bufsize <= HINT_LOOKAHEAD)
463 		return;
464 	for (p = buffer; (p < max) && p[0]; p++)
465 	{
466 		/* Search for IPv4 */
467 		if (patterns && IPV4_HINT(p))
468 		{
469 			size_t field_len = strspn(p, IPV4_FIELD);
470 			struct netspec v4key;
471 			if (ipv4_to_uint(p, field_len, &v4key.min))
472 			{
473 				if (match_ip(&v4key, NULL, buffer, filename))
474 					return;	/* found match, stop scanning inside line */
475 			}
476 			p += field_len - 1;
477 			continue;
478 		}
479 		/* Search for IPv6 */
480 		if (patterns6)
481 		{
482 			if (IPV6_HINT1(p)||IPV6_HINT2(p)||IPV6_HINT3(p)||IPV6_HINT4(p)||IPV6_HINT5(p))
483 			{
484 				size_t field_len = strspn(p, IPV6_FIELD);
485 				struct netspec6 v6key;
486 				if (ipv6_to_uchar(p, field_len, v6key.min))
487 				{
488 					if (match_ip(NULL, &v6key, buffer, filename))
489 						return;	/* found match, stop scanning inside line */
490 				}
491 				p += field_len - 1;
492 				continue;
493 			}
494 		}
495 	}
496 }
497 
498 
499 /*
500 	Somewhat like GNU getline(), returns an arbitrarily long whole line in *bufptr
501 	Returns 0 when end of stream occurs and no characters are read, or 1 if a line is read.
502 	Buffer remains allocated in multiple calls, but caller should free *bufptr when done.
503 */
fgets_whole_line(char ** bufptr,size_t * bufsize,FILE * stream)504 int fgets_whole_line(char **bufptr, size_t *bufsize, FILE* stream)
505 {
506 	size_t bufcur = 0;
507 	if (*bufptr == NULL)
508 	{
509 		*bufptr = malloc(MAXFIELD);
510 		*bufsize = MAXFIELD;
511 		if (*bufptr == NULL)
512 		{
513 			fprintf(stderr, TXT_MEMORY);
514 			exit(EXIT_ERROR);
515 		}
516 	}
517 	while (fgets((*bufptr)+bufcur, (*bufsize)-bufcur, stream))
518 	{
519 		size_t len = bufcur + strlen(*bufptr + bufcur);
520 		if (len < 1) return 1;
521 		if ((bufptr[0][len-1] == '\n') || feof(stream))
522 			return 1;
523 		*bufsize *= 2;
524 		*bufptr = realloc(*bufptr, *bufsize);
525 		if (*bufptr == NULL)
526 		{
527 			fprintf(stderr, TXT_MEMORY);
528 			exit(EXIT_ERROR);
529 		}
530 		bufcur = len;
531 	}
532 	return 0;
533 }
534 
535 
536 /* Match IPs from input stream to network patterns */
search_stream(FILE * input_stream,const char * filename)537 void search_stream(FILE* input_stream, const char* filename)
538 {
539 	char* line = NULL;
540 	size_t linesize = 0;
541 	while (fgets_whole_line(&line, &linesize, input_stream))
542 	{
543 		match_one = 0;
544 		seen_ip = include_noip;
545 		if (strict_nosearch)
546 		{
547 			/* Faster old-style search, look for single IP from start of line */
548 			int ipv4_match = 0;
549 			if (patterns)
550 			{
551 				size_t field_len = strspn(line, IPV4_FIELD);
552 				struct netspec v4key;
553 				if (ipv4_to_uint(line, field_len, &v4key.min))
554 					ipv4_match = match_ip(&v4key, NULL, line, filename);
555 			}
556 			if (!ipv4_match && patterns6)
557 			{
558 				size_t field_len = strspn(line, IPV6_FIELD);
559 				struct netspec6 v6key;
560 				if (ipv6_to_uchar(line, field_len, v6key.min))
561 					match_ip(NULL, &v6key, line, filename);
562 			}
563 		}
564 		else
565 			scan_with_hints(line, linesize, filename);	/* scan whole line */
566 
567 		/* If using -i or -v, take action once the whole line has been processed */
568 		if (invert && seen_ip && !match_one)
569 			print_or_count(line, filename);
570 	}
571 	free(line);
572 }
573 
574 
575 
main(int argc,char * argv[])576 int main(int argc, char* argv[])
577 {
578 	static char shortopts[] = "ce:f:isvxV";
579 	char* pat_filename = NULL;		/* filename containing patterns */
580 	char* pat_strings = NULL;		/* pattern strings on command line */
581 	int foundopt;
582 
583 	if ((CHAR_BIT != 8) || (sizeof(unsigned int) < 4) ||
584 		(sizeof(struct in_addr) != 4) || (sizeof(struct in6_addr) != 16))
585 	{
586 		fprintf(stderr, TXT_FATAL);
587 		return EXIT_ERROR;
588 	}
589 
590 	if (argc == 1)
591 	{
592 		fprintf(stderr, TXT_USAGE);
593 		return EXIT_ERROR;
594 	}
595 
596 	while ((foundopt = getopt(argc, argv, shortopts)) != -1)
597 	{
598 		switch (foundopt)
599 		{
600 			case 'V':
601 				puts(TXT_VERSION);
602 				return EXIT_ERROR;
603 
604 			case 'c':
605 				counting = 1;
606 				break;
607 
608 			case 's':
609 				strict_align = 1;
610 				break;
611 
612 			case 'i':
613 				include_noip = 1;
614 			case 'v':
615 				invert = 1;
616 				break;
617 
618 			case 'x':
619 				strict_nosearch = 1;
620 				break;
621 
622 			case 'e':
623 				pat_strings = optarg;
624 				break;
625 
626 			case 'f':
627 				pat_filename = optarg;
628 				break;
629 
630 			default:
631 				fprintf(stderr, TXT_USAGE);
632 				return EXIT_ERROR;
633 		}
634 	}
635 
636 	if (!pat_filename && !pat_strings)
637 	{
638 		if (optind < argc)
639 			pat_strings = argv[optind++];
640 		else
641 		{
642 			fprintf(stderr, TXT_USAGE2);
643 			return EXIT_ERROR;
644 		}
645 	}
646 
647 	/* Initial array allocation */
648 	capacity = INIT_NETWORKS;
649 	array = (struct netspec*) malloc(capacity*sizeof(struct netspec));
650 	capacity6 = INIT_NETWORKS;
651 	array6 = (struct netspec6*) malloc(capacity6*sizeof(struct netspec6));
652 	if (!array || !array6)
653 	{
654 		fprintf(stderr, TXT_MEMORY);
655 		return EXIT_ERROR;
656 	}
657 
658 	load_patterns(pat_filename, pat_strings);
659 	do
660 	{	/* Search each specified file name, or just stdin */
661 		const char* curfilename = NULL;
662 		FILE* inp_stream;
663 		if (optind >= argc)
664 			inp_stream = stdin;
665 		else
666 		{
667 			/* One or more file names are specified on the command line */
668 			if (optind+1 < argc) shownames = 1;	/* more than one file */
669 			curfilename = argv[optind++];
670 			inp_stream = fopen(curfilename, "r");
671 			if (!inp_stream)
672 			{
673 				GREPERROR(curfilename);
674 				return EXIT_ERROR;
675 			}
676 		}
677 		/* Ready to search this stream or file */
678 		search_stream(inp_stream, curfilename);
679 		if (inp_stream != stdin)
680 			fclose(inp_stream);
681 	} while (optind < argc);
682 
683 	if (counting)
684 		printf("%u\n", counting-1);
685 	/* Cleanup */
686 	if (array)
687 		free(array);
688 	if (array6)
689 		free(array6);
690 	if (anymatch)
691 		return EXIT_OK;
692 	else
693 		return EXIT_NOMATCH;
694 }
695