summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-04-23 00:00:37 -0400
committerTavian Barnes <tavianator@tavianator.com>2017-04-23 00:17:49 -0400
commit1efa932e4aeb007eddb6424a90bf0fc05dba7e4d (patch)
tree288ae5aa5385f985cbd4818497e01cf845591617
parent8df078ada9045ffceb541e224985a4e4191e1526 (diff)
downloadbfs-1efa932e4aeb007eddb6424a90bf0fc05dba7e4d.tar.xz
Implement -fstype
Fixes #6!
-rw-r--r--Makefile2
-rw-r--r--bfs.h5
-rw-r--r--eval.c14
-rw-r--r--mtab.c174
-rw-r--r--mtab.h46
-rw-r--r--parse.c30
-rw-r--r--printf.c23
-rw-r--r--printf.h7
-rwxr-xr-xtests.sh6
-rw-r--r--tests/test_fstype.out19
10 files changed, 319 insertions, 7 deletions
diff --git a/Makefile b/Makefile
index 50148f5..a4f4cdd 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@ ALL_LDFLAGS = $(ALL_CFLAGS) $(LDFLAGS)
all: bfs
-bfs: bftw.o color.o dstring.o eval.o exec.o main.o parse.o printf.o typo.o util.o
+bfs: bftw.o color.o dstring.o eval.o exec.o main.o mtab.o parse.o printf.o typo.o util.o
$(CC) $(ALL_LDFLAGS) $^ -o $@
release: CFLAGS := -O3 -flto $(WFLAGS) -DNDEBUG
diff --git a/bfs.h b/bfs.h
index 191cb9e..3fd66a5 100644
--- a/bfs.h
+++ b/bfs.h
@@ -15,6 +15,7 @@
#include "color.h"
#include "exec.h"
#include "printf.h"
+#include "mtab.h"
#include <regex.h>
#include <stdbool.h>
#include <stddef.h>
@@ -90,6 +91,9 @@ struct cmdline {
/** Colored stderr. */
CFILE *cerr;
+ /** Table of mounted file systems. */
+ struct bfs_mtab *mtab;
+
/** -mindepth option. */
int mindepth;
/** -maxdepth option. */
@@ -292,6 +296,7 @@ bool eval_nouser(const struct expr *expr, struct eval_state *state);
bool eval_depth(const struct expr *expr, struct eval_state *state);
bool eval_empty(const struct expr *expr, struct eval_state *state);
+bool eval_fstype(const struct expr *expr, struct eval_state *state);
bool eval_hidden(const struct expr *expr, struct eval_state *state);
bool eval_inum(const struct expr *expr, struct eval_state *state);
bool eval_links(const struct expr *expr, struct eval_state *state);
diff --git a/eval.c b/eval.c
index 8c33d21..5de25ec 100644
--- a/eval.c
+++ b/eval.c
@@ -13,6 +13,7 @@
#include "bftw.h"
#include "color.h"
#include "dstring.h"
+#include "mtab.h"
#include "util.h"
#include <assert.h>
#include <dirent.h>
@@ -367,6 +368,19 @@ done:
}
/**
+ * -fstype test.
+ */
+bool eval_fstype(const struct expr *expr, struct eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ const char *type = bfs_fstype(state->cmdline->mtab, statbuf);
+ return strcmp(type, expr->sdata) == 0;
+}
+
+/**
* -hidden test.
*/
bool eval_hidden(const struct expr *expr, struct eval_state *state) {
diff --git a/mtab.c b/mtab.c
new file mode 100644
index 0000000..0311c50
--- /dev/null
+++ b/mtab.c
@@ -0,0 +1,174 @@
+/*********************************************************************
+ * bfs *
+ * Copyright (C) 2017 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This program is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+#include "mtab.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if __linux__
+# include <mntent.h>
+#elif BSD
+# include <sys/mount.h>
+# include <sys/ucred.h>
+#endif
+
+/**
+ * A mount point in the mount table.
+ */
+struct bfs_mtab_entry {
+ /** The device number for this mount point. */
+ dev_t dev;
+ /** The file system type of this mount point. */
+ char *type;
+};
+
+struct bfs_mtab {
+ /** The array of mtab entries. */
+ struct bfs_mtab_entry *table;
+ /** The size of the array. */
+ size_t size;
+ /** Capacity of the array. */
+ size_t capacity;
+};
+
+/**
+ * Add an entry to the mount table.
+ */
+static int bfs_mtab_push(struct bfs_mtab *mtab, dev_t dev, const char *type) {
+ size_t size = mtab->size + 1;
+
+ if (size >= mtab->capacity) {
+ size_t capacity = 2*size;
+ struct bfs_mtab_entry *table = realloc(mtab->table, capacity*sizeof(*table));
+ if (!table) {
+ return -1;
+ }
+ mtab->table = table;
+ mtab->capacity = capacity;
+ }
+
+ struct bfs_mtab_entry *entry = mtab->table + (size - 1);
+ entry->dev = dev;
+ entry->type = strdup(type);
+ if (!entry->type) {
+ return -1;
+ }
+
+ mtab->size = size;
+ return 0;
+}
+
+struct bfs_mtab *parse_bfs_mtab() {
+#if __linux__
+
+ FILE *file = setmntent("/etc/mtab", "r");
+ if (!file) {
+ goto fail;
+ }
+
+ struct bfs_mtab *mtab = malloc(sizeof(*mtab));
+ if (!mtab) {
+ goto fail_file;
+ }
+ mtab->table = NULL;
+ mtab->size = 0;
+ mtab->capacity = 0;
+
+ struct mntent *mnt;
+ while ((mnt = getmntent(file))) {
+ struct stat sb;
+ if (stat(mnt->mnt_dir, &sb) != 0) {
+ continue;
+ }
+
+ if (bfs_mtab_push(mtab, sb.st_dev, mnt->mnt_type) != 0) {
+ goto fail_mtab;
+ }
+ }
+
+ endmntent(file);
+ return mtab;
+
+fail_mtab:
+ free_bfs_mtab(mtab);
+fail_file:
+ endmntent(file);
+fail:
+ return NULL;
+
+#elif BSD
+
+ struct statfs *mntbuf;
+ int size = getmntinfo(&mntbuf, MNT_WAIT);
+ if (size < 0) {
+ return NULL;
+ }
+
+ struct bfs_mtab *mtab = malloc(sizeof(*mtab));
+ if (!mtab) {
+ goto fail;
+ }
+
+ mtab->size = 0;
+ mtab->table = malloc(size*sizeof(*mtab->table));
+ if (!mtab->table) {
+ goto fail_mtab;
+ }
+ mtab->capacity = size;
+
+ for (struct statfs *mnt = mntbuf; mnt < mntbuf + size; ++mnt) {
+ struct stat sb;
+ if (stat(mnt->f_mntonname, &sb) != 0) {
+ continue;
+ }
+
+ if (bfs_mtab_push(mtab, sb.st_dev, mnt->f_fstypename) != 0) {
+ goto fail_mtab;
+ }
+ }
+
+ return mtab;
+
+fail_mtab:
+ free_bfs_mtab(mtab);
+fail:
+ return NULL;
+
+#else
+
+ errno = ENOTSUP;
+ return NULL;
+#endif
+}
+
+const char *bfs_fstype(const struct bfs_mtab *mtab, const struct stat *statbuf) {
+ for (struct bfs_mtab_entry *mnt = mtab->table; mnt < mtab->table + mtab->size; ++mnt) {
+ if (statbuf->st_dev == mnt->dev) {
+ return mnt->type;
+ }
+ }
+
+ return "unknown";
+}
+
+void free_bfs_mtab(struct bfs_mtab *mtab) {
+ if (mtab) {
+ for (struct bfs_mtab_entry *mnt = mtab->table; mnt < mtab->table + mtab->size; ++mnt) {
+ free(mnt->type);
+ }
+ free(mtab->table);
+ free(mtab);
+ }
+}
diff --git a/mtab.h b/mtab.h
new file mode 100644
index 0000000..38407fc
--- /dev/null
+++ b/mtab.h
@@ -0,0 +1,46 @@
+/*********************************************************************
+ * bfs *
+ * Copyright (C) 2017 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This program is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+#ifndef BFS_MTAB_H
+#define BFS_MTAB_H
+
+#include <sys/stat.h>
+
+/**
+ * A file system mount table.
+ */
+struct bfs_mtab;
+
+/**
+ * Parse the mount table.
+ *
+ * @return The parsed mount table, or NULL on error.
+ */
+struct bfs_mtab *parse_bfs_mtab(void);
+
+/**
+ * Determine the file system type that a file is on.
+ *
+ * @param mtab
+ * The current mount table.
+ * @param statbuf
+ * The stat() buffer for the file in question.
+ * @return The type of file system containing this file, "unknown" if not known,
+ * or NULL on error.
+ */
+const char *bfs_fstype(const struct bfs_mtab *mtab, const struct stat *statbuf);
+
+/**
+ * Free a mount table.
+ */
+void free_bfs_mtab(struct bfs_mtab *mtab);
+
+#endif // BFS_MTAB_H
diff --git a/parse.c b/parse.c
index f8ed09c..25791cf 100644
--- a/parse.c
+++ b/parse.c
@@ -191,6 +191,8 @@ void free_cmdline(struct cmdline *cmdline) {
if (cmdline) {
free_expr(cmdline->expr);
+ free_bfs_mtab(cmdline->mtab);
+
cfclose(cmdline->cerr);
cfclose(cmdline->cout);
free_colors(cmdline->colors);
@@ -1039,7 +1041,7 @@ static struct expr *parse_fprintf(struct parser_state *state, int arg1, int arg2
goto fail;
}
- expr->printf = parse_bfs_printf(format, state->cmdline->cerr);
+ expr->printf = parse_bfs_printf(format, state->cmdline);
if (!expr->printf) {
goto fail;
}
@@ -1052,6 +1054,18 @@ fail:
}
/**
+ * Parse -fstype TYPE.
+ */
+static struct expr *parse_fstype(struct parser_state *state, int arg1, int arg2) {
+ if (!state->cmdline->mtab) {
+ cfprintf(state->cmdline->cerr, "%{er}error: %s: Couldn't parse the mount table.%{rs}\n", state->argv[0]);
+ return NULL;
+ }
+
+ return parse_unary_test(state, eval_fstype);
+}
+
+/**
* Parse -gid/-group.
*/
static struct expr *parse_group(struct parser_state *state, int arg1, int arg2) {
@@ -1598,7 +1612,7 @@ static struct expr *parse_printf(struct parser_state *state, int arg1, int arg2)
expr->cfile = state->cmdline->cout;
- expr->printf = parse_bfs_printf(expr->sdata, state->cmdline->cerr);
+ expr->printf = parse_bfs_printf(expr->sdata, state->cmdline);
if (!expr->printf) {
free_expr(expr);
return NULL;
@@ -1993,6 +2007,8 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) {
cfprintf(cout, " %{blu}-false%{rs}\n");
cfprintf(cout, " %{blu}-true%{rs}\n");
cfprintf(cout, " Always false/true\n");
+ cfprintf(cout, " %{blu}-fstype%{rs} %{bld}TYPE%{rs}\n");
+ cfprintf(cout, " Find files on file systems with the given %{bld}TYPE%{rs}\n");
cfprintf(cout, " %{blu}-gid%{rs} %{bld}[-+]N%{rs}\n");
cfprintf(cout, " %{blu}-uid%{rs} %{bld}[-+]N%{rs}\n");
cfprintf(cout, " Find files owned by group/user ID %{bld}N%{rs}\n");
@@ -2149,6 +2165,7 @@ static const struct table_entry parse_table[] = {
{"fprint", false, parse_fprint},
{"fprint0", false, parse_fprint0},
{"fprintf", false, parse_fprintf},
+ {"fstype", false, parse_fstype},
{"gid", false, parse_group},
{"group", false, parse_group},
{"help", false, parse_help},
@@ -2801,6 +2818,10 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) {
}
cmdline->roots = NULL;
+ cmdline->colors = NULL;
+ cmdline->cout = NULL;
+ cmdline->cerr = NULL;
+ cmdline->mtab = NULL;
cmdline->mindepth = 0;
cmdline->maxdepth = INT_MAX;
cmdline->flags = BFTW_RECOVER;
@@ -2819,6 +2840,11 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) {
goto fail;
}
+ cmdline->mtab = parse_bfs_mtab();
+ if (!cmdline->mtab) {
+ cfprintf(cmdline->cerr, "%{wr}warning: Couldn't parse the mount table: %s.%{rs}\n\n", strerror(errno));
+ }
+
struct parser_state state = {
.cmdline = cmdline,
.argv = argv + 1,
diff --git a/printf.c b/printf.c
index cc8989f..50a9dcf 100644
--- a/printf.c
+++ b/printf.c
@@ -13,6 +13,7 @@
#include "bfs.h"
#include "color.h"
#include "dstring.h"
+#include "mtab.h"
#include "util.h"
#include <assert.h>
#include <errno.h>
@@ -39,6 +40,8 @@ struct bfs_printf_directive {
enum time_field time_field;
/** Character data associated with this directive. */
char c;
+ /** The current mount table. */
+ const struct bfs_mtab *mtab;
/** The next printf directive in the chain. */
struct bfs_printf_directive *next;
};
@@ -175,6 +178,12 @@ static int bfs_printf_f(FILE *file, const struct bfs_printf_directive *directive
return fprintf(file, directive->str, ftwbuf->path + ftwbuf->nameoff);
}
+/** %F: file system type */
+static int bfs_printf_F(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
+ const char *type = bfs_fstype(directive->mtab, ftwbuf->statbuf);
+ return fprintf(file, directive->str, type);
+}
+
/** %G: gid */
static int bfs_printf_G(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)ftwbuf->statbuf->st_gid);
@@ -393,6 +402,7 @@ static struct bfs_printf_directive *new_directive() {
}
directive->time_field = 0;
directive->c = 0;
+ directive->mtab = NULL;
directive->next = NULL;
return directive;
@@ -433,7 +443,9 @@ static int append_literal(struct bfs_printf_directive ***tail, struct bfs_printf
return 0;
}
-struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr) {
+struct bfs_printf *parse_bfs_printf(const char *format, const struct cmdline *cmdline) {
+ CFILE *cerr = cmdline->cerr;
+
struct bfs_printf *command = malloc(sizeof(*command));
if (!command) {
perror("malloc()");
@@ -584,6 +596,15 @@ struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr) {
case 'f':
directive->fn = bfs_printf_f;
break;
+ case 'F':
+ if (!cmdline->mtab) {
+ cfprintf(cerr, "%{er}error: '%s': Couldn't parse the mount table.%{rs}\n", format);
+ goto directive_error;
+ }
+ directive->fn = bfs_printf_F;
+ directive->mtab = cmdline->mtab;
+ command->needs_stat = true;
+ break;
case 'g':
directive->fn = bfs_printf_g;
command->needs_stat = true;
diff --git a/printf.h b/printf.h
index 32171ca..d57921f 100644
--- a/printf.h
+++ b/printf.h
@@ -17,6 +17,7 @@
#include <stdbool.h>
#include <stdio.h>
+struct cmdline;
struct bfs_printf_directive;
/**
@@ -34,11 +35,11 @@ struct bfs_printf {
*
* @param format
* The format string to parse.
- * @param cerr
- * For error messages.
+ * @param cmdline
+ * The command line.
* @return The parsed printf command, or NULL on failure.
*/
-struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr);
+struct bfs_printf *parse_bfs_printf(const char *format, const struct cmdline *cmdline);
/**
* Evaluate a parsed format string.
diff --git a/tests.sh b/tests.sh
index 5fed800..e3cdb52 100755
--- a/tests.sh
+++ b/tests.sh
@@ -292,6 +292,7 @@ gnu_tests=(
test_printf_leak
test_quit_after_print
test_quit_before_print
+ test_fstype
test_not
test_and
test_or
@@ -1019,6 +1020,11 @@ function test_printf_leak() {
bfs_diff basic -maxdepth 0 -printf '%p'
}
+function test_fstype() {
+ fstype="$($BFS -printf '%F\n' | head -n1)"
+ bfs_diff basic -fstype "$fstype"
+}
+
function test_path_flag_expr() {
bfs_diff links/h -H -type l
}
diff --git a/tests/test_fstype.out b/tests/test_fstype.out
new file mode 100644
index 0000000..bb3cd8d
--- /dev/null
+++ b/tests/test_fstype.out
@@ -0,0 +1,19 @@
+basic
+basic/a
+basic/b
+basic/c
+basic/e
+basic/g
+basic/i
+basic/j
+basic/k
+basic/l
+basic/c/d
+basic/e/f
+basic/g/h
+basic/j/foo
+basic/k/foo
+basic/l/foo
+basic/k/foo/bar
+basic/l/foo/bar
+basic/l/foo/bar/baz