summaryrefslogtreecommitdiffstats
path: root/parse.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2016-03-12 16:07:34 -0500
committerTavian Barnes <tavianator@tavianator.com>2016-03-12 16:07:34 -0500
commitf1401f09c59363170f021c4a1676735be2e3cc44 (patch)
tree2b534caed6c1e1dbcca06814f20d603e641f7bdc /parse.c
parent037361965a14e1899020bf16c9112936e39020e8 (diff)
downloadbfs-f1401f09c59363170f021c4a1676735be2e3cc44.tar.xz
Implement -size.
Diffstat (limited to 'parse.c')
-rw-r--r--parse.c122
1 files changed, 108 insertions, 14 deletions
diff --git a/parse.c b/parse.c
index 154d566..53d42eb 100644
--- a/parse.c
+++ b/parse.c
@@ -213,34 +213,72 @@ static const char *skip_paths(struct parser_state *state) {
}
}
+/** Integer parsing flags. */
+enum intflags {
+ IF_INT = 0,
+ IF_LONG = 1,
+ IF_LONG_LONG = 2,
+ IF_SIZE_MASK = 0x3,
+ IF_UNSIGNED = 1 << 2,
+ IF_PARTIAL_OK = 1 << 3,
+};
+
/**
* Parse an integer.
*/
-static bool parse_int(const struct parser_state *state, const char *str, int *value) {
+static const char *parse_int(const struct parser_state *state, const char *str, void *result, enum intflags flags) {
char *endptr;
- long result = strtol(str, &endptr, 10);
- if (*str == '\0' || *endptr != '\0') {
+ errno = 0;
+ long long value = strtoll(str, &endptr, 10);
+ if (errno != 0) {
goto bad;
}
- if (result < INT_MIN || result > INT_MAX) {
+ if (endptr == str) {
goto bad;
}
- *value = result;
- return true;
+ if (!(flags & IF_PARTIAL_OK) && *endptr != '\0') {
+ goto bad;
+ }
+
+ if ((flags & IF_UNSIGNED) && value < 0) {
+ goto bad;
+ }
+
+ switch (flags & IF_SIZE_MASK) {
+ case IF_INT:
+ if (value < INT_MIN || value > INT_MAX) {
+ goto bad;
+ }
+ *(int *)result = value;
+ break;
+
+ case IF_LONG:
+ if (value < LONG_MIN || value > LONG_MAX) {
+ goto bad;
+ }
+ *(long *)result = value;
+ break;
+
+ case IF_LONG_LONG:
+ *(long long *)result = value;
+ break;
+ }
+
+ return endptr;
bad:
pretty_error(state->cmdline->stderr_colors,
"error: '%s' is not a valid integer.\n", str);
- return false;
+ return NULL;
}
/**
* Parse an integer and a comparison flag.
*/
-static bool parse_icmp(const struct parser_state *state, const char *str, struct expr *expr) {
+static const char *parse_icmp(const struct parser_state *state, const char *str, struct expr *expr, enum intflags flags) {
switch (str[0]) {
case '-':
expr->cmp = CMP_LESS;
@@ -255,7 +293,7 @@ static bool parse_icmp(const struct parser_state *state, const char *str, struct
break;
}
- return parse_int(state, str, &expr->idata);
+ return parse_int(state, str, &expr->idata, flags | IF_LONG_LONG | IF_UNSIGNED);
}
/**
@@ -383,7 +421,7 @@ static struct expr *parse_test_icmp(struct parser_state *state, eval_fn *eval) {
return NULL;
}
- if (!parse_icmp(state, expr->sdata, expr)) {
+ if (!parse_icmp(state, expr->sdata, expr, 0)) {
free_expr(expr);
return NULL;
}
@@ -430,7 +468,7 @@ static struct expr *parse_debug(struct parser_state *state) {
* Parse -On.
*/
static struct expr *parse_optlevel(struct parser_state *state) {
- if (!parse_int(state, state->args[0] + 2, &state->cmdline->optlevel)) {
+ if (!parse_int(state, state->args[0] + 2, &state->cmdline->optlevel, IF_INT)) {
return NULL;
}
@@ -525,7 +563,7 @@ static struct expr *parse_depth(struct parser_state *state, int *depth) {
return NULL;
}
- if (!parse_int(state, value, depth)) {
+ if (!parse_int(state, value, depth, IF_INT)) {
return NULL;
}
@@ -553,7 +591,7 @@ static struct expr *parse_group(struct parser_state *state) {
error = strerror(errno);
goto error;
} else if (isdigit(expr->sdata[0])) {
- if (!parse_int(state, expr->sdata, &expr->idata)) {
+ if (!parse_int(state, expr->sdata, &expr->idata, IF_LONG_LONG)) {
goto fail;
}
} else {
@@ -594,7 +632,7 @@ static struct expr *parse_user(struct parser_state *state) {
error = strerror(errno);
goto error;
} else if (isdigit(expr->sdata[0])) {
- if (!parse_int(state, expr->sdata, &expr->idata)) {
+ if (!parse_int(state, expr->sdata, &expr->idata, IF_LONG_LONG)) {
goto fail;
}
} else {
@@ -772,6 +810,60 @@ static struct expr *parse_samefile(struct parser_state *state) {
}
/**
+ * Parse -size N[bcwkMG]?.
+ */
+static struct expr *parse_size(struct parser_state *state) {
+ struct expr *expr = parse_unary_test(state, eval_size);
+ if (!expr) {
+ return NULL;
+ }
+
+ const char *unit = parse_icmp(state, expr->sdata, expr, IF_PARTIAL_OK);
+ if (!unit) {
+ goto fail;
+ }
+
+ if (strlen(unit) > 1) {
+ goto bad_unit;
+ }
+
+ switch (*unit) {
+ case '\0':
+ case 'b':
+ expr->sizeunit = SIZE_BLOCKS;
+ break;
+ case 'c':
+ expr->sizeunit = SIZE_BYTES;
+ break;
+ case 'w':
+ expr->sizeunit = SIZE_WORDS;
+ break;
+ case 'k':
+ expr->sizeunit = SIZE_KB;
+ break;
+ case 'M':
+ expr->sizeunit = SIZE_MB;
+ break;
+ case 'G':
+ expr->sizeunit = SIZE_GB;
+ break;
+
+ default:
+ goto bad_unit;
+ }
+
+ return expr;
+
+bad_unit:
+ pretty_error(state->cmdline->stderr_colors,
+ "error: %s %s: Expected a size unit of 'b', 'c', 'w', 'k', 'M', or 'G'; found %s.\n",
+ expr->args[0], expr->args[1], unit);
+fail:
+ free(expr);
+ return NULL;
+}
+
+/**
* Parse -x?type [bcdpfls].
*/
static struct expr *parse_type(struct parser_state *state, eval_fn *eval) {
@@ -1050,6 +1142,8 @@ static struct expr *parse_literal(struct parser_state *state) {
case 's':
if (strcmp(arg, "-samefile") == 0) {
return parse_samefile(state);
+ } else if (strcmp(arg, "-size") == 0) {
+ return parse_size(state);
}
break;