summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-10-21 16:02:30 -0400
committerTavian Barnes <tavianator@tavianator.com>2017-10-21 16:08:14 -0400
commit4a08c0300cf6b0ee3bb5f755007641635ba21f9d (patch)
tree4fcce4cf80bc6575eb053c6691398d04be1eb917
parent4e38f139f92b8b3729f82c37f0904c2b77d3eb58 (diff)
downloadbfs-4a08c0300cf6b0ee3bb5f755007641635ba21f9d.tar.xz
parse: Keep track of what files are already open
Fixes #22
-rw-r--r--cmdline.h9
-rw-r--r--color.c2
-rw-r--r--expr.h4
-rw-r--r--parse.c139
-rwxr-xr-xtests.sh24
-rw-r--r--tests/test_fprint_duplicate.out57
6 files changed, 185 insertions, 50 deletions
diff --git a/cmdline.h b/cmdline.h
index 9a46f7f..7c1891c 100644
--- a/cmdline.h
+++ b/cmdline.h
@@ -50,6 +50,11 @@ struct root {
};
/**
+ * An open file for the command line.
+ */
+struct open_file;
+
+/**
* The parsed command line.
*/
struct cmdline {
@@ -89,7 +94,9 @@ struct cmdline {
/** The command line expression. */
struct expr *expr;
- /** The number of open files used by the expression tree. */
+ /** All the open files owned by the command line. */
+ struct open_file *open_files;
+ /** The number of open files owned by the command line. */
int nopen_files;
};
diff --git a/color.c b/color.c
index 1025c9e..8df6a1f 100644
--- a/color.c
+++ b/color.c
@@ -288,8 +288,6 @@ int cfclose(CFILE *cfile) {
if (cfile) {
if (cfile->close) {
ret = fclose(cfile->file);
- } else if (cfile->file) {
- ret = fflush(cfile->file);
}
free(cfile);
}
diff --git a/expr.h b/expr.h
index c0e2b92..0eba0da 100644
--- a/expr.h
+++ b/expr.h
@@ -219,9 +219,7 @@ void dump_expr(CFILE *cfile, const struct expr *expr, bool verbose);
/**
* Free an expression tree.
- *
- * @return 0 if successful, -1 on error.
*/
-int free_expr(struct expr *expr);
+void free_expr(struct expr *expr);
#endif // EXPR_H
diff --git a/parse.c b/parse.c
index b4b7ddc..2708b9a 100644
--- a/parse.c
+++ b/parse.c
@@ -80,36 +80,23 @@ struct expr expr_false = {
/**
* Free an expression.
*/
-int free_expr(struct expr *expr) {
- int ret = 0;
-
- if (expr && expr != &expr_true && expr != &expr_false) {
- if (expr->cfile && expr->cfile->close) {
- if (cfclose(expr->cfile) != 0) {
- perror("cfclose()");
- ret = -1;
- }
- }
-
- if (expr->regex) {
- regfree(expr->regex);
- free(expr->regex);
- }
+void free_expr(struct expr *expr) {
+ if (!expr || expr == &expr_true || expr == &expr_false) {
+ return;
+ }
- free_bfs_printf(expr->printf);
- free_bfs_exec(expr->execbuf);
+ if (expr->regex) {
+ regfree(expr->regex);
+ free(expr->regex);
+ }
- if (free_expr(expr->lhs) != 0) {
- ret = -1;
- }
- if (free_expr(expr->rhs) != 0) {
- ret = -1;
- }
+ free_bfs_printf(expr->printf);
+ free_bfs_exec(expr->execbuf);
- free(expr);
- }
+ free_expr(expr->lhs);
+ free_expr(expr->rhs);
- return ret;
+ free(expr);
}
struct expr *new_expr(eval_fn *eval, size_t argc, char **argv) {
@@ -232,27 +219,60 @@ void dump_expr(CFILE *cfile, const struct expr *expr, bool verbose) {
}
/**
+ * An open file for the command line.
+ */
+struct open_file {
+ /** The file itself. */
+ CFILE *cfile;
+ /** The path to the file (for diagnostics). */
+ const char *path;
+ /** Device number (for deduplication). */
+ dev_t dev;
+ /** Inode number (for deduplication). */
+ ino_t ino;
+ /** The next open file in the chain. */
+ struct open_file *next;
+};
+
+/**
* Free the parsed command line.
*/
int free_cmdline(struct cmdline *cmdline) {
int ret = 0;
if (cmdline) {
- if (free_expr(cmdline->expr) != 0) {
- ret = -1;
- }
+ CFILE *cout = cmdline->cout;
+ CFILE *cerr = cmdline->cerr;
+
+ free_expr(cmdline->expr);
free_bfs_mtab(cmdline->mtab);
- if (cfclose(cmdline->cerr) != 0) {
- perror("cfclose()");
- ret = -1;
+ struct open_file *ofile = cmdline->open_files;
+ while (ofile) {
+ struct open_file *next = ofile->next;
+
+ if (cfclose(ofile->cfile) != 0) {
+ if (cerr) {
+ cfprintf(cerr, "%{er}error: '%s': %s%{rs}\n", ofile->path, strerror(errno));
+ }
+ ret = -1;
+ }
+
+ free(ofile);
+ ofile = next;
}
- if (cfclose(cmdline->cout) != 0) {
- perror("cfclose()");
+
+ if (cout && fflush(cout->file) != 0) {
+ if (cerr) {
+ cfprintf(cerr, "%{er}error: standard output: %s%{rs}\n", strerror(errno));
+ }
ret = -1;
}
+ cfclose(cout);
+ cfclose(cerr);
+
free_colors(cmdline->colors);
struct root *root = cmdline->roots;
@@ -349,14 +369,52 @@ static void init_print_expr(struct parser_state *state, struct expr *expr) {
* Open a file for an expression.
*/
static int expr_open(struct parser_state *state, struct expr *expr, const char *path) {
- expr->cfile = cfopen(path, state->use_color ? state->cmdline->colors : NULL);
- if (!expr->cfile) {
- cfprintf(state->cmdline->cerr, "%{er}error: '%s': %s%{rs}\n", path, strerror(errno));
- return -1;
+ int ret = -1;
+
+ struct cmdline *cmdline = state->cmdline;
+
+ CFILE *cfile = cfopen(path, state->use_color ? cmdline->colors : NULL);
+ if (!cfile) {
+ cfprintf(cmdline->cerr, "%{er}error: '%s': %s%{rs}\n", path, strerror(errno));
+ goto out;
}
- ++state->cmdline->nopen_files;
- return 0;
+ struct stat sb;
+ if (fstat(fileno(cfile->file), &sb) != 0) {
+ cfprintf(cmdline->cerr, "%{er}error: '%s': %s%{rs}\n", path, strerror(errno));
+ goto out_close;
+ }
+
+ for (struct open_file *ofile = cmdline->open_files; ofile; ofile = ofile->next) {
+ if (ofile->dev == sb.st_dev && ofile->ino == sb.st_ino) {
+ expr->cfile = ofile->cfile;
+ ret = 0;
+ goto out_close;
+ }
+ }
+
+ struct open_file *ofile = malloc(sizeof(*ofile));
+ if (!ofile) {
+ goto out_close;
+ }
+
+ ofile->cfile = cfile;
+ ofile->path = path;
+ ofile->dev = sb.st_dev;
+ ofile->ino = sb.st_ino;
+ ofile->next = cmdline->open_files;
+ cmdline->open_files = ofile;
+ ++cmdline->nopen_files;
+
+ expr->cfile = cfile;
+
+ ret = 0;
+ goto out;
+
+out_close:
+ cfclose(cfile);
+out:
+ return ret;
}
/**
@@ -2932,6 +2990,7 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) {
cmdline->xargs_safe = false;
cmdline->ignore_races = false;
cmdline->expr = &expr_true;
+ cmdline->open_files = NULL;
cmdline->nopen_files = 0;
cmdline->argv = malloc((argc + 1)*sizeof(*cmdline->argv));
diff --git a/tests.sh b/tests.sh
index 0f52581..9775292 100755
--- a/tests.sh
+++ b/tests.sh
@@ -330,6 +330,7 @@ gnu_tests=(
test_flag_weird_names
test_follow_comma
test_fprint
+ test_fprint_duplicate
test_double_dash
test_flag_double_dash
test_ignore_readdir_race
@@ -931,16 +932,31 @@ function test_follow_comma() {
}
function test_fprint() {
+ invoke_bfs basic -fprint scratch/test_fprint.out
+ sort -o scratch/test_fprint.out scratch/test_fprint.out
+
if [ "$UPDATE" ]; then
- invoke_bfs basic -fprint "$TESTS/test_fprint.out"
- sort -o "$TESTS/test_fprint.out" "$TESTS/test_fprint.out"
+ cp scratch/test_fprint.out "$TESTS/test_fprint.out"
else
- invoke_bfs basic -fprint scratch/test_fprint.out
- sort -o scratch/test_fprint.out scratch/test_fprint.out
diff -u scratch/test_fprint.out "$TESTS/test_fprint.out"
fi
}
+function test_fprint_duplicate() {
+ touchp scratch/test_fprint_duplicate.out
+ ln scratch/test_fprint_duplicate.out scratch/test_fprint_duplicate.hard
+ ln -s test_fprint_duplicate.out scratch/test_fprint_duplicate.soft
+
+ invoke_bfs basic -fprint scratch/test_fprint_duplicate.out -fprint scratch/test_fprint_duplicate.hard -fprint scratch/test_fprint_duplicate.soft
+ sort -o scratch/test_fprint_duplicate.out scratch/test_fprint_duplicate.out
+
+ if [ "$UPDATE" ]; then
+ cp scratch/test_fprint_duplicate.out "$TESTS/test_fprint_duplicate.out"
+ else
+ diff -u scratch/test_fprint_duplicate.out "$TESTS/test_fprint_duplicate.out"
+ fi
+}
+
function test_double_dash() {
cd basic
bfs_diff -- . -type f
diff --git a/tests/test_fprint_duplicate.out b/tests/test_fprint_duplicate.out
new file mode 100644
index 0000000..2575f35
--- /dev/null
+++ b/tests/test_fprint_duplicate.out
@@ -0,0 +1,57 @@
+basic
+basic
+basic
+basic/a
+basic/a
+basic/a
+basic/b
+basic/b
+basic/b
+basic/c
+basic/c
+basic/c
+basic/c/d
+basic/c/d
+basic/c/d
+basic/e
+basic/e
+basic/e
+basic/e/f
+basic/e/f
+basic/e/f
+basic/g
+basic/g
+basic/g
+basic/g/h
+basic/g/h
+basic/g/h
+basic/i
+basic/i
+basic/i
+basic/j
+basic/j
+basic/j
+basic/j/foo
+basic/j/foo
+basic/j/foo
+basic/k
+basic/k
+basic/k
+basic/k/foo
+basic/k/foo
+basic/k/foo
+basic/k/foo/bar
+basic/k/foo/bar
+basic/k/foo/bar
+basic/l
+basic/l
+basic/l
+basic/l/foo
+basic/l/foo
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar
+basic/l/foo/bar
+basic/l/foo/bar/baz
+basic/l/foo/bar/baz
+basic/l/foo/bar/baz