Although you can write 'nbdkit data "@$((4*1024)) 1"', it is more
convenient to be able to write 'nbdkit data "@4k 1"'. To do that,
make use of our existing code for suffix scaling when we know we have
a decimal value, while still accepting non-decimal values without
scaling for backwards compatability. Since we must do substring
parsing (there is likely more data after the offset directive), we
insist that after the value is parsed, the string must be on
whitespace before the next directive.
Signed-off-by: Eric Blake <eblake(a)redhat.com>
---
plugins/data/nbdkit-data-plugin.pod | 3 +-
plugins/data/format.c | 115 ++++++++++++++--------------
tests/test-data-format.sh | 8 +-
3 files changed, 65 insertions(+), 61 deletions(-)
diff --git a/plugins/data/nbdkit-data-plugin.pod b/plugins/data/nbdkit-data-plugin.pod
index e94102ba..bb57e912 100644
--- a/plugins/data/nbdkit-data-plugin.pod
+++ b/plugins/data/nbdkit-data-plugin.pod
@@ -230,7 +230,8 @@ generates the 4 byte sequence C<0 0 0 1>.
Moves the current offset to C<OFFSET>. The offset may be specified as
either decimal, octal (prefixed by C<0>) or hexadecimal (prefixed by
-C<0x>). Offset C<@0> is the first byte of the disk.
+C<0x>); when using decimal, it is also possible to supply a scaling
+suffix such as C<k> or C<M>. Offset C<@0> is the first byte of the
disk.
=item B<@+>N
diff --git a/plugins/data/format.c b/plugins/data/format.c
index 81255b07..796a493c 100644
--- a/plugins/data/format.c
+++ b/plugins/data/format.c
@@ -41,6 +41,7 @@
#include <string.h>
#include <assert.h>
#include <signal.h>
+#include <ctype.h>
#define NBDKIT_API_VERSION 2
#include <nbdkit-plugin.h>
@@ -51,6 +52,7 @@
#include "checked-overflow.h"
#include "cleanup.h"
#include "hexdigit.h"
+#include "human-size.h"
#include "ispowerof2.h"
#include "minmax.h"
#include "nbdkit-string.h"
@@ -494,6 +496,9 @@ parser (int level, const char *value, size_t *start, size_t len,
int j, n;
int64_t i64, m;
size_t flen;
+ char sign;
+ char *end;
+ const char *err, *pstr;
switch (value[i]) {
case '#': /* # comment */
@@ -505,68 +510,60 @@ parser (int level, const char *value, size_t *start, size_t len,
case '@': /* @OFFSET */
if (++i == len) goto parse_error;
switch (value[i]) {
- case '+': /* @+N */
- if (++i == len) goto parse_error;
- if (sscanf (&value[i], "%" SCNi64 "%n", &i64, &n)
== 1) {
- if (i64 < 0) {
- nbdkit_error ("data parameter after @+ must not be negative");
- return -1;
- }
- i += n;
- APPEND_EXPR (new_node (expr (EXPR_REL_OFFSET, i64)));
- }
- else
- goto parse_error;
- break;
- case '-': /* @-N */
- if (++i == len) goto parse_error;
- if (sscanf (&value[i], "%" SCNi64 "%n", &i64, &n)
== 1) {
- if (i64 < 0) {
- nbdkit_error ("data parameter after @- must not be negative");
- return -1;
- }
- i += n;
- APPEND_EXPR (new_node (expr (EXPR_REL_OFFSET, -i64)));
- }
- else
- goto parse_error;
- break;
- case '^': /* @^ALIGNMENT */
- if (++i == len) goto parse_error;
- /* We must use %i into i64 in order to parse 0x etc. */
- if (sscanf (&value[i], "%" SCNi64 "%n", &i64, &n)
== 1) {
- if (i64 < 0) {
- nbdkit_error ("data parameter after @^ must not be negative");
- return -1;
- }
- /* XXX fix this arbitrary restriction */
- if (!is_power_of_2 (i64)) {
- nbdkit_error ("data parameter @^%" PRIi64 " must be a power of
2",
- i64);
- return -1;
- }
- i += n;
- APPEND_EXPR (new_node (expr (EXPR_ALIGN_OFFSET, (uint64_t) i64)));
- }
- else
- goto parse_error;
- break;
- case '0': case '1': case '2': case '3': case
'4':
- case '5': case '6': case '7': case '8': case
'9':
- /* We must use %i into i64 in order to parse 0x etc. */
- if (sscanf (&value[i], "%" SCNi64 "%n", &i64, &n)
== 1) {
- if (i64 < 0) {
- nbdkit_error ("data parameter @OFFSET must not be negative");
- return -1;
- }
- i += n;
- APPEND_EXPR (new_node (expr (EXPR_ABS_OFFSET, (uint64_t) i64)));
- }
- else
- goto parse_error;
+ case '+':
+ case '-':
+ case '^':
+ sign = value[i++];
break;
default:
+ sign = '\0';
+ }
+ if (value[i] == '0') {
+ /* hex or octal, no scaling suffix possible */
+ if (sscanf (&value[i], "%" SCNi64 "%n", &i64, &n)
== 1) {
+ if (i64 < 0) {
+ nbdkit_error ("data parameter after @ must not be negative");
+ return -1;
+ }
+ i += n;
+ }
+ else
+ goto parse_error;
+ }
+ else if (isdigit(value[i])) {
+ /* decimal, scaling suffix allowed */
+ i64 = human_size_parse_substr (&value[i], &end, &err, &pstr);
+ if (i64 == -1) {
+ nbdkit_error ("%s: %s", err, pstr);
+ return -1;
+ }
+ if (*end && !isspace (*end))
+ goto parse_error;
+ i = end - value;
+ }
+ else
+ /* empty string or unknown byte */
goto parse_error;
+
+ switch (sign) {
+ case '+': /* @+N */
+ APPEND_EXPR (new_node (expr (EXPR_REL_OFFSET, i64)));
+ break;
+ case '-': /* @-N */
+ APPEND_EXPR (new_node (expr (EXPR_REL_OFFSET, -i64)));
+ break;
+ case '^': /* @^ALIGNMENT */
+ /* XXX fix this arbitrary restriction */
+ if (!is_power_of_2 (i64)) {
+ nbdkit_error ("data parameter @^%" PRIi64 " must be a power of
2",
+ i64);
+ return -1;
+ }
+ APPEND_EXPR (new_node (expr (EXPR_ALIGN_OFFSET, (uint64_t) i64)));
+ break;
+ default:
+ APPEND_EXPR (new_node (expr (EXPR_ABS_OFFSET, (uint64_t) i64)));
+ break;
}
break;
diff --git a/tests/test-data-format.sh b/tests/test-data-format.sh
index 59f2b102..43929136 100755
--- a/tests/test-data-format.sh
+++ b/tests/test-data-format.sh
@@ -66,7 +66,7 @@ if size > 0:
actual = h.pread(size, 0)
else:
actual = b\"\"
-def trunc(s): return s[:128] + (s[128:] and b\"...\")
+def trunc(s): return s[:128] + (s[128:] and b\"...\" + s[-4:])
print(\"actual: %r\" % trunc(actual))
print(\"expected: %r\" % trunc(expected))
assert actual == expected
@@ -117,6 +117,12 @@ do_test '@+4 "\x00"'
'b"\x00\x00\x00\x00\x00"'
do_test '@+4 @-1 "\x00"' 'b"\x00\x00\x00\x00"'
do_test '1 @^4 "\x00"' 'b"\x01\x00\x00\x00\x00"'
+#----------------------------------------------------------------------
+# Larger offsets.
+do_test '@1k "\x01"' 'bytearray(1024) + b"\x01"'
+do_test '@0x400 "\x01"' 'bytearray(1024) + b"\x01"'
+do_test '@02000 "\x01"' 'bytearray(1024) + b"\x01"'
+
#----------------------------------------------------------------------
# Comments.
do_test '#' 'b""'
--
2.49.0