grep: option to use GNU regex matching instead of POSIX one.
 This fixes problems with NULs in files being scanned, but
 costs +800 bytes. The same can be done to sed (TODO).

diff --git a/findutils/grep.c b/findutils/grep.c
index 030e624..f2ed01e 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -96,6 +96,7 @@
 	int lines_before;
 	int lines_after;
 	char **before_buf;
+	USE_EXTRA_COMPAT(size_t *before_buf_size;)
 	int last_line_printed;
 #endif
 	/* globals used internally */
@@ -117,6 +118,7 @@
 #define lines_before      (G.lines_before        )
 #define lines_after       (G.lines_after         )
 #define before_buf        (G.before_buf          )
+#define before_buf_size   (G.before_buf_size     )
 #define last_line_printed (G.last_line_printed   )
 #define pattern_head      (G.pattern_head        )
 #define cur_file          (G.cur_file            )
@@ -124,14 +126,24 @@
 
 typedef struct grep_list_data_t {
 	char *pattern;
-	regex_t preg;
+/* for GNU regex, matched_range must be persistent across grep_file() calls */
+#if !ENABLE_EXTRA_COMPAT
+	regex_t compiled_regex;
+	regmatch_t matched_range;
+#else
+	struct re_pattern_buffer compiled_regex;
+	struct re_registers matched_range;
+#endif
 #define ALLOCATED 1
 #define COMPILED 2
 	int flg_mem_alocated_compiled;
 } grep_list_data_t;
 
-
-static void print_line(const char *line, int linenum, char decoration)
+#if !ENABLE_EXTRA_COMPAT
+#define print_line(line, line_len, linenum, decoration) \
+	print_line(line, linenum, decoration)
+#endif
+static void print_line(const char *line, size_t line_len, int linenum, char decoration)
 {
 #if ENABLE_FEATURE_GREP_CONTEXT
 	/* Happens when we go to next file, immediately hit match
@@ -139,8 +151,9 @@
 	if (linenum < 1)
 		return;
 	/* possibly print the little '--' separator */
-	if ((lines_before || lines_after) && did_print_line &&
-			last_line_printed != linenum - 1) {
+	if ((lines_before || lines_after) && did_print_line
+	 && last_line_printed != linenum - 1
+	) {
 		puts("--");
 	}
 	/* guard against printing "--" before first line of first file */
@@ -152,17 +165,50 @@
 	if (PRINT_LINE_NUM)
 		printf("%i%c", linenum, decoration);
 	/* Emulate weird GNU grep behavior with -ov */
-	if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o))
+	if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) {
+#if !ENABLE_EXTRA_COMPAT
 		puts(line);
+#else
+		fwrite(line, 1, line_len, stdout);
+		putchar('\n');
+#endif
+	}
 }
 
+#if ENABLE_EXTRA_COMPAT
+/* Unlike getline, this one removes trailing '\n' */
+static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file)
+{
+	ssize_t res_sz;
+	char *line;
+
+	res_sz = getline(line_ptr, line_alloc_len, file);
+	line = *line_ptr;
+
+	if (res_sz > 0) {
+		if (line[res_sz - 1] == '\n')
+			line[--res_sz] = '\0';
+	} else {
+		free(line); /* uclibc allocates a buffer even on EOF. WTF? */
+	}
+	return res_sz;
+}
+#endif
+
 static int grep_file(FILE *file)
 {
-	char *line;
 	smalluint found;
 	int linenum = 0;
 	int nmatches = 0;
-	regmatch_t regmatch;
+#if !ENABLE_EXTRA_COMPAT
+	char *line;
+#else
+	char *line = NULL;
+	ssize_t line_len;
+	size_t line_alloc_len;
+#define rm_so start[0]
+#define rm_eo end[0]
+#endif
 #if ENABLE_FEATURE_GREP_CONTEXT
 	int print_n_lines_after = 0;
 	int curpos = 0; /* track where we are in the circular 'before' buffer */
@@ -171,7 +217,13 @@
 	enum { print_n_lines_after = 0 };
 #endif /* ENABLE_FEATURE_GREP_CONTEXT */
 
-	while ((line = xmalloc_fgetline(file)) != NULL) {
+	while (
+#if !ENABLE_EXTRA_COMPAT
+		(line = xmalloc_fgetline(file)) != NULL
+#else
+		(line_len = bb_getline(&line, &line_alloc_len, file)) >= 0
+#endif
+	) {
 		llist_t *pattern_ptr = pattern_head;
 		grep_list_data_t *gl = gl; /* for gcc */
 
@@ -184,19 +236,35 @@
 			} else {
 				if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
 					gl->flg_mem_alocated_compiled |= COMPILED;
-					xregcomp(&(gl->preg), gl->pattern, reflags);
+#if !ENABLE_EXTRA_COMPAT
+					xregcomp(&gl->compiled_regex, gl->pattern, reflags);
+#else
+					memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex));
+					if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex))
+						bb_error_msg_and_die("bad regex '%s'", gl->pattern);
+#endif
 				}
-				regmatch.rm_so = 0;
-				regmatch.rm_eo = 0;
-				if (regexec(&(gl->preg), line, 1, &regmatch, 0) == 0) {
+#if !ENABLE_EXTRA_COMPAT
+				gl->matched_range.rm_so = 0;
+				gl->matched_range.rm_eo = 0;
+#endif
+				if (
+#if !ENABLE_EXTRA_COMPAT
+					regexec(&gl->compiled_regex, line, 1, &gl->matched_range, 0) == 0
+#else
+					re_search(&gl->compiled_regex, line, line_len,
+							/*start:*/ 0, /*range:*/ line_len,
+							&gl->matched_range) >= 0
+#endif
+				) {
 					if (!(option_mask32 & OPT_w))
 						found = 1;
 					else {
 						char c = ' ';
-						if (regmatch.rm_so)
-							c = line[regmatch.rm_so - 1];
+						if (gl->matched_range.rm_so)
+							c = line[gl->matched_range.rm_so - 1];
 						if (!isalnum(c) && c != '_') {
-							c = line[regmatch.rm_eo];
+							c = line[gl->matched_range.rm_eo];
 							if (!c || (!isalnum(c) && c != '_'))
 								found = 1;
 						}
@@ -261,7 +329,7 @@
 
 					/* now print each line in the buffer, clearing them as we go */
 					while (before_buf[idx] != NULL) {
-						print_line(before_buf[idx], first_buf_entry_line_num, '-');
+						print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-');
 						free(before_buf[idx]);
 						before_buf[idx] = NULL;
 						idx = (idx + 1) % lines_before;
@@ -277,13 +345,15 @@
 						/* -Fo just prints the pattern
 						 * (unless -v: -Fov doesnt print anything at all) */
 						if (found)
-							print_line(gl->pattern, linenum, ':');
+							print_line(gl->pattern, strlen(gl->pattern), linenum, ':');
 					} else {
-						line[regmatch.rm_eo] = '\0';
-						print_line(line + regmatch.rm_so, linenum, ':');
+						line[gl->matched_range.rm_eo] = '\0';
+						print_line(line + gl->matched_range.rm_so,
+								gl->matched_range.rm_eo - gl->matched_range.rm_so,
+								linenum, ':');
 					}
 				} else {
-					print_line(line, linenum, ':');
+					print_line(line, line_len, linenum, ':');
 				}
 			}
 		}
@@ -291,12 +361,13 @@
 		else { /* no match */
 			/* if we need to print some context lines after the last match, do so */
 			if (print_n_lines_after) {
-				print_line(line, linenum, '-');
+				print_line(line, strlen(line), linenum, '-');
 				print_n_lines_after--;
 			} else if (lines_before) {
 				/* Add the line to the circular 'before' buffer */
 				free(before_buf[curpos]);
 				before_buf[curpos] = line;
+				USE_EXTRA_COMPAT(before_buf_size[curpos] = line_len;)
 				curpos = (curpos + 1) % lines_before;
 				/* avoid free(line) - we took the line */
 				line = NULL;
@@ -304,13 +375,14 @@
 		}
 
 #endif /* ENABLE_FEATURE_GREP_CONTEXT */
+#if !ENABLE_EXTRA_COMPAT
 		free(line);
-
+#endif
 		/* Did we print all context after last requested match? */
 		if ((option_mask32 & OPT_m)
 		 && !print_n_lines_after && nmatches == max_matches)
 			break;
-	}
+	} /* while (read line) */
 
 	/* special-case file post-processing for options where we don't print line
 	 * matches, just filenames and possibly match counts */
@@ -428,15 +500,16 @@
 			lines_after = Copt;
 		if (!(option_mask32 & OPT_B)) /* not overridden */
 			lines_before = Copt;
-		//option_mask32 |= OPT_A|OPT_B; /* for parser */
 	}
 	/* sanity checks */
 	if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) {
 		option_mask32 &= ~OPT_n;
 		lines_before = 0;
 		lines_after = 0;
-	} else if (lines_before > 0)
-		before_buf = xzalloc(lines_before * sizeof(char *));
+	} else if (lines_before > 0) {
+		before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
+		USE_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
+	}
 #else
 	/* with auto sanity checks */
 	/* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */
@@ -537,7 +610,7 @@
 			if (gl->flg_mem_alocated_compiled & ALLOCATED)
 				free(gl->pattern);
 			if (gl->flg_mem_alocated_compiled & COMPILED)
-				regfree(&(gl->preg));
+				regfree(&gl->compiled_regex);
 			free(gl);
 			free(pattern_head_ptr);
 		}