This extends the concept of the magic script parameter. In existing
nbdkit plugins if the first parameter is "bare" (does not contain
'='), then it is parsed magically to allow scripting languages to
work:
nbdkit perl foo.pl
is parsed as:
nbdkit perl script=foo.pl
This generalises the concept, allowing a plugin.magic_config_key to be
defined. If this is non-NULL then any bare parameters on the command
line:
nbdkit file foo
where file_plugin.magic_config_key = "file" is parsed as:
nbdkit file file=foo
If magic_config_key == NULL then the previous behaviour is used.
There is a small (but hopefully not noticable) change to the behaviour
of ‘--dump-plugin’. It is now handled after all command line
parameters have been parsed but before the .config_complete method is
called (whereas previously it would be called after the first command
line parameter was parsed).
Note this change only applies to plugin parameters.
---
docs/nbdkit-plugin.pod | 17 +++++++++++
docs/nbdkit.pod | 8 +++---
include/nbdkit-plugin.h | 2 ++
src/filters.c | 12 ++++++++
src/internal.h | 1 +
src/main.c | 62 ++++++++++++++++++++++++-----------------
src/plugins.c | 9 ++++++
7 files changed, 81 insertions(+), 30 deletions(-)
diff --git a/docs/nbdkit-plugin.pod b/docs/nbdkit-plugin.pod
index 515c032..570a142 100644
--- a/docs/nbdkit-plugin.pod
+++ b/docs/nbdkit-plugin.pod
@@ -396,6 +396,23 @@ with an error.
If there is an error, C<.config> should call C<nbdkit_error> with an
error message and return C<-1>.
+=head2 C<.magic_config_key>
+
+ const char *magic_config_key;
+
+This optional string can be used to set a "magic" key used when
+parsing plugin parameters. It affects how "bare parameters" (those
+which do not contain an C<=> character) are parsed on the command
+line.
+
+If C<magic_config_key != NULL> then any bare parameters are passed to
+the C<.config> method as: S<C<config (magic_config_key, argv[i]);>>.
+
+If C<magic_config_key> is not set then we behave as in nbdkit E<lt>
+1.7: If the first parameter on the command line is bare then it is
+passed to the C<.config> method as: S<C<config ("script",
value);>>.
+Any other bare parameters give errors.
+
=head2 C<.config_complete>
int config_complete (void);
diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod
index 981e571..cf1c5ca 100644
--- a/docs/nbdkit.pod
+++ b/docs/nbdkit.pod
@@ -404,11 +404,11 @@ To dump information about a plugin, do:
=head2 Magic script parameter
-As a special case, if the first plugin argument does not contain an
-C<'='> character then it is assumed to be C<script=value>.
+For some plugins, especially those supporting scripting languages like
+Perl, if the first plugin argument does not contain an C<'='>
+character then it is assumed to be C<script=value>.
-That allows scripting language plugins like L<nbdkit-perl-plugin(1)>
-to do:
+That allows scripting language plugins to do:
nbdkit perl foo.pl [args...]
diff --git a/include/nbdkit-plugin.h b/include/nbdkit-plugin.h
index c95e26c..31aa6c7 100644
--- a/include/nbdkit-plugin.h
+++ b/include/nbdkit-plugin.h
@@ -121,6 +121,8 @@ struct nbdkit_plugin {
int (*trim) (void *handle, uint32_t count, uint64_t offset, uint32_t flags);
int (*zero) (void *handle, uint32_t count, uint64_t offset, uint32_t flags);
#endif
+
+ const char *magic_config_key;
};
extern void nbdkit_set_error (int err);
diff --git a/src/filters.c b/src/filters.c
index e6826f2..3626742 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -205,6 +205,17 @@ filter_config_complete (struct backend *b)
f->backend.next->config_complete (f->backend.next);
}
+/* magic_config_key only applies to plugins, so this passes the
+ * request through to the plugin (hence the name).
+ */
+static const char *
+plugin_magic_config_key (struct backend *b)
+{
+ struct backend_filter *f = container_of (b, struct backend_filter, backend);
+
+ return f->backend.next->magic_config_key (f->backend.next);
+}
+
static int
next_open (void *nxdata, int readonly)
{
@@ -619,6 +630,7 @@ static struct backend filter_functions = {
.dump_fields = filter_dump_fields,
.config = filter_config,
.config_complete = filter_config_complete,
+ .magic_config_key = plugin_magic_config_key,
.open = filter_open,
.prepare = filter_prepare,
.finalize = filter_finalize,
diff --git a/src/internal.h b/src/internal.h
index 1c5aa72..d4fc534 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -171,6 +171,7 @@ struct backend {
void (*dump_fields) (struct backend *);
void (*config) (struct backend *, const char *key, const char *value);
void (*config_complete) (struct backend *);
+ const char *(*magic_config_key) (struct backend *);
int (*open) (struct backend *, struct connection *conn, int readonly);
int (*prepare) (struct backend *, struct connection *conn);
int (*finalize) (struct backend *, struct connection *conn);
diff --git a/src/main.c b/src/main.c
index 8a83a32..9c18d6f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -231,6 +231,7 @@ main (int argc, char *argv[])
const char *filename;
} *filter_filenames = NULL;
size_t i;
+ const char *magic_config_key;
threadlocal_init ();
@@ -680,37 +681,46 @@ main (int argc, char *argv[])
exit (EXIT_SUCCESS);
}
- /* Find key=value configuration parameters for this plugin.
- * The first one is magical in that if it doesn't contain '=' then
- * we assume it is 'script=...'.
+ /* Call config and config_complete to parse the parameters.
+ *
+ * If the plugin provides magic_config_key then any "bare" values
+ * (ones not containing "=") are prefixed with this key.
+ *
+ * For backwards compatibility with old plugins, and to support
+ * scripting languages, if magic_config_key == NULL then if the
+ * first parameter is bare it is prefixed with the key "script", and
+ * any other bare parameters are errors.
*/
- if (optind < argc && (p = strchr (argv[optind], '=')) == NULL) {
- backend->config (backend, "script", argv[optind]);
- ++optind;
- }
-
- /* This must run after parsing the possible script parameter so that
- * the script can be loaded for scripting languages. Note that all
- * scripting languages load the script as soon as they see the
- * script=... parameter (and do not wait for config_complete).
- */
- if (dump_plugin) {
- backend->dump_fields (backend);
- exit (EXIT_SUCCESS);
- }
-
- while (optind < argc) {
- if ((p = strchr (argv[optind], '=')) != NULL) {
+ magic_config_key = backend->magic_config_key (backend);
+ for (i = 0; optind < argc; ++i, ++optind) {
+ p = strchr (argv[optind], '=');
+ if (p) { /* key=value */
*p = '\0';
backend->config (backend, argv[optind], p+1);
- ++optind;
}
- else {
- fprintf (stderr,
- "%s: expecting key=value on the command line but got: %s\n",
- program_name, argv[optind]);
- exit (EXIT_FAILURE);
+ else if (magic_config_key == NULL) {
+ if (i == 0) /* magic script parameter */
+ backend->config (backend, "script", argv[optind]);
+ else {
+ fprintf (stderr,
+ "%s: expecting key=value on the command line but got: %s\n",
+ program_name, argv[optind]);
+ exit (EXIT_FAILURE);
+ }
}
+ else { /* magic config key */
+ backend->config (backend, magic_config_key, argv[optind]);
+ }
+ }
+
+ /* This must run after parsing the parameters so that the script can
+ * be loaded for scripting languages. But it must be called before
+ * config_complete so that the plugin doesn't check for missing
+ * parameters.
+ */
+ if (dump_plugin) {
+ backend->dump_fields (backend);
+ exit (EXIT_SUCCESS);
}
backend->config_complete (backend);
diff --git a/src/plugins.c b/src/plugins.c
index 780bbd9..2bea6ac 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -232,6 +232,14 @@ plugin_config_complete (struct backend *b)
exit (EXIT_FAILURE);
}
+static const char *
+plugin_magic_config_key (struct backend *b)
+{
+ struct backend_plugin *p = container_of (b, struct backend_plugin, backend);
+
+ return p->plugin.magic_config_key;
+}
+
static int
plugin_open (struct backend *b, struct connection *conn, int readonly)
{
@@ -630,6 +638,7 @@ static struct backend plugin_functions = {
.dump_fields = plugin_dump_fields,
.config = plugin_config,
.config_complete = plugin_config_complete,
+ .magic_config_key = plugin_magic_config_key,
.open = plugin_open,
.prepare = plugin_prepare,
.finalize = plugin_finalize,
--
2.18.0