linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michal Kubecek <mkubecek@suse.cz>
To: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
	"John W. Linville" <linville@tuxdriver.com>
Subject: [RFC PATCH ethtool v2 08/23] netlink: add helpers for command line parsing
Date: Mon, 30 Jul 2018 14:56:24 +0200 (CEST)	[thread overview]
Message-ID: <70e163a6ce3f75f44876b30bf28b26b132b4f27a.1532954671.git.mkubecek@suse.cz> (raw)
In-Reply-To: <cover.1532954671.git.mkubecek@suse.cz>

Support for easier parsing of subcommand parameters and their values from
command line. Existing parser helpers in ethtool.c are closely tied to
ioctl() interface and using them would be often inconvenient.

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
---
 Makefile.am       |   1 +
 netlink/netlink.h |   4 +
 netlink/parser.c  | 527 ++++++++++++++++++++++++++++++++++++++++++++++
 netlink/parser.h  |  45 ++++
 4 files changed, 577 insertions(+)
 create mode 100644 netlink/parser.c
 create mode 100644 netlink/parser.h

diff --git a/Makefile.am b/Makefile.am
index 629f04e1202b..7cc4adc53c59 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,6 +23,7 @@ if ETHTOOL_ENABLE_NETLINK
 ethtool_SOURCES += \
 		  netlink/netlink.c netlink/netlink.h netlink/extapi.h \
 		  netlink/strset.c netlink/strset.h netlink/monitor.c \
+		  netlink/parser.c netlink/parser.h \
 		  netlink/drvinfo.c netlink/settings.c \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h
diff --git a/netlink/netlink.h b/netlink/netlink.h
index a572a165718e..32c2f9f33ba1 100644
--- a/netlink/netlink.h
+++ b/netlink/netlink.h
@@ -30,6 +30,10 @@ struct nl_context {
 	void *msg;
 	const char *devname;
 	bool is_dump;
+	const char *cmd;
+	const char *param;
+	char **argp;
+	int argc;
 	int exit_code;
 	bool is_monitor;
 	uint8_t filter_cmd;
diff --git a/netlink/parser.c b/netlink/parser.c
new file mode 100644
index 000000000000..589004d1f9f9
--- /dev/null
+++ b/netlink/parser.c
@@ -0,0 +1,527 @@
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+static void parser_err_unknown_param(struct nl_context *nlctx)
+{
+	fprintf(stderr, "ethtool (%s): unknown parameter '%s'\n", nlctx->cmd,
+		nlctx->param);
+}
+
+static void parser_err_dup_param(struct nl_context *nlctx)
+{
+	fprintf(stderr, "ethtool (%s): duplicate parameter '%s'\n", nlctx->cmd,
+		nlctx->param);
+}
+
+static void parser_err_min_argc(struct nl_context *nlctx, unsigned int min_argc)
+{
+	if (min_argc == 1)
+		fprintf(stderr, "ethtool (%s): no value for parameter '%s'\n",
+			nlctx->cmd, nlctx->param);
+	else
+		fprintf(stderr,
+			"ethtool (%s): parameter '%s' requires %u words\n",
+			nlctx->cmd, nlctx->param, min_argc);
+}
+
+static void parser_err_invalid_value(struct nl_context *nlctx, const char *val)
+{
+	fprintf(stderr, "ethtool (%s): invalid value '%s' for parameter '%s'\n",
+		nlctx->cmd, val, nlctx->param);
+}
+
+static void parser_err_invalid_flag(struct nl_context *nlctx, const char *flag)
+{
+	fprintf(stderr, "ethtool (%s): flag '%s' for parameter '%s' is not "
+			"followed by 'on' or 'off'\n",
+		nlctx->cmd, flag, nlctx->param);
+}
+
+static int __parse_u32(const char *arg, u32 *result, u32 min, u32 max, int base)
+{
+	unsigned long long val;
+	char *endptr;
+
+	if (!arg || !arg[0])
+		return -EINVAL;
+	val = strtoul(arg, &endptr, base);
+	if (*endptr || val < min || val > max)
+		return -EINVAL;
+
+	*result = (u32)val;
+	return 0;
+}
+
+static int parse_u32d(const char *arg, u32 *result)
+{
+	return __parse_u32(arg, result, 0, 0xffffffff, 10);
+}
+
+static int parse_x32(const char *arg, u32 *result)
+{
+	return __parse_u32(arg, result, 0, 0xffffffff, 16);
+}
+
+static int parse_u32(const char *arg, u32 *result)
+{
+	if (!arg)
+		return -EINVAL;
+	if ((arg[0] == '0') && (arg[1] == 'x' || arg[1] == 'X'))
+		return parse_x32(arg + 2, result);
+	else
+		return parse_u32d(arg, result);
+}
+
+static int parse_u8(const char *arg, u8 *result)
+{
+	u32 val;
+	int ret = parse_u32(arg, &val);
+
+	if (ret < 0)
+		return ret;
+	if (val > UINT8_MAX)
+		return -EINVAL;
+
+	*result = (u8)val;
+	return 0;
+}
+
+static int lookup_u8(const char *arg, u8 *result,
+		     const struct lookup_entry_u8 *tbl)
+{
+	if (!arg)
+		return -EINVAL;
+	while (tbl->arg) {
+		if (!strcmp(tbl->arg, arg)) {
+			*result = tbl->val;
+			return 0;
+		}
+		tbl++;
+	}
+
+	return -EINVAL;
+}
+
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+			const void *data)
+{
+	const char *arg = *nlctx->argp;
+	u32 val;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	ret = parse_u32(arg, &val);
+	if (ret < 0) {
+		parser_err_invalid_value(nlctx, arg);
+		return ret;
+	}
+
+	return ethnla_put_u32(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data)
+{
+	const char *arg = *nlctx->argp;
+	u8 val;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	ret = parse_u8(arg, &val);
+	if (ret < 0) {
+		parser_err_invalid_value(nlctx, arg);
+		return ret;
+	}
+
+	return ethnla_put_u8(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_bool(struct nl_context *nlctx, uint16_t type, const void *data)
+{
+	const char *arg = *nlctx->argp;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	if (!strcmp(arg, "on"))
+		ret = ethnla_put_u8(nlctx, type, 1);
+	else if (!strcmp(arg, "off"))
+		ret = ethnla_put_u8(nlctx, type, 0);
+	else {
+		parser_err_invalid_value(nlctx, arg);
+		return -EINVAL;
+	}
+
+	return ret ? -EMSGSIZE : 0;
+}
+
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data)
+{
+	const char *arg = *nlctx->argp;
+	u8 val;
+	int ret;
+
+	nlctx->argp++;
+	nlctx->argc--;
+	ret = lookup_u8(arg, &val, data);
+	if (ret < 0) {
+		parser_err_invalid_value(nlctx, arg);
+		return ret;
+	}
+
+	return ethnla_put_u8(nlctx, type, val) ? -EMSGSIZE : 0;
+}
+
+int nl_parse_bitfield32(struct nl_context *nlctx, uint16_t type,
+			const void *data)
+{
+	const char *arg = *nlctx->argp;
+	const struct flag_info *flags = data;
+	u32 value, selector;
+	int ret;
+
+	ret = parse_u32(arg, &value);
+	if (isdigit(arg[0])) {
+		char *mask = strchr(arg, '/');
+
+		if (mask) {
+			/* numeric value / mask */
+			*mask = '\0';
+			mask++;
+			ret = parse_u32(mask, &selector);
+			if (ret < 0) {
+				parser_err_invalid_value(nlctx, mask);
+				return ret;
+			}
+		} else {
+			/* numeric value */
+			selector = ~(u32)0;
+		}
+		ret = parse_u32(arg, &value);
+		if (ret < 0) {
+			parser_err_invalid_value(nlctx, arg);
+			return ret;
+		}
+		nlctx->argp++;
+		nlctx->argc--;
+	} else {
+		/* flag on/off... [--] */
+		value = 0;
+		selector = 0;
+		while (nlctx->argc > 0) {
+			const struct flag_info *flag = flags;
+
+			if (!strcmp(*nlctx->argp, "--")) {
+				nlctx->argp++;
+				nlctx->argc--;
+				break;
+			}
+			if (nlctx->argc < 2 ||
+			    (strcmp(nlctx->argp[1], "on") &&
+			     strcmp(nlctx->argp[1], "off"))) {
+				parser_err_invalid_flag(nlctx, *nlctx->argp);
+				return -EINVAL;
+			}
+			while (flag->name && strcmp(*nlctx->argp, flag->name))
+				flag++;
+			if (!flag->name) {
+				parser_err_invalid_value(nlctx, *nlctx->argp);
+				return -EINVAL;
+			}
+			selector |= flag->value;
+			if (!strcmp(nlctx->argp[1], "on"))
+				value |= flag->value;
+
+			nlctx->argp += 2;
+			nlctx->argc -= 2;
+		}
+	}
+
+	return ethnla_put_bitfield32(nlctx, type, value, selector);
+}
+
+static bool is_hex(char c)
+{
+	if (isdigit(c))
+		return true;
+	else
+		return (c >= 'a' && c <= 'f');
+}
+
+/* Return true if a bitset argument should be parsed as numeric, i.e.
+ * (a) it starts with '0x'
+ * (b) it consists only of hex digits and at most one slash which can be
+ *     optionally followed by "0x"
+ */
+static bool is_numeric_bitset(const char *arg)
+{
+	const char *p = arg;
+	bool has_slash = false;
+
+	if (arg[0] == '0' && arg[1] == 'x')
+		return true;
+	while (*p) {
+		if (*p == '/') {
+			if (has_slash)
+				return false;
+			has_slash = true;
+			p++;
+			if (p[0] == '0' && p[1] == 'x')
+				p += 2;
+			continue;
+		}
+		if (!is_hex(*p))
+			return false;
+		p++;
+	}
+	return true;
+}
+
+/* number of significant bits */
+static unsigned int nsb(u32 x)
+{
+	unsigned int ret = 0;
+
+	if (!(x & 0xffff0000U)) {
+		x >>= 16;
+		ret += 16;
+	}
+	if (!(x & 0xff00U)) {
+		x >>= 8;
+		ret += 8;
+	}
+	if (!(x & 0xf0U)) {
+		x >>= 4;
+		ret += 4;
+	}
+	if (!(x & 0xcU)) {
+		x >>= 2;
+		ret += 2;
+	}
+	if (!(x & 0x2U)) {
+		x >>= 1;
+		ret += 1;
+	}
+
+	return ret + x;
+}
+
+/* Parse hex string (without leading "0x") into a bitmap consisting of 32-bit
+ * words. Caller must make sure arg is at least len characters long and dst has
+ * place for at least (len + 7) / 8 32-bit words.
+ * Returns number of significant bits in the bitmap on success and negative
+ * value on error.
+ */
+static int parse_hex_string(const char *arg, unsigned int len, u32 *dst)
+{
+	const char *p = arg;
+	unsigned int nwords = (len + 7) / 8;
+	unsigned int nbits = 0;
+	char buff[9];
+
+	memset(dst, '\0', nwords * sizeof(u32));
+	while (len > 0) {
+		unsigned int chunk = (len % 8) ?: 8;
+		unsigned long val;
+		char *endp;
+
+		memcpy(buff, p, chunk);
+		buff[chunk] = '\0';
+		val = strtoul(buff, &endp, 16);
+		if (*endp)
+			return -EINVAL;
+		*dst++ = (u32)val;
+		if (nbits)
+			nbits += 8 * chunk;
+		else
+			nbits = nsb(val);
+
+		p += chunk;
+		len -= chunk;
+	}
+	return nbits;
+}
+
+static int nl_parse_bitset_compact(struct nl_context *nlctx, uint16_t type)
+{
+	const char *arg = *nlctx->argp;
+	unsigned int nwords, len1, len2;
+	bool has_mask = false;
+	struct nlattr *nest;
+	const char *maskptr;
+	u32 *value = NULL;
+	u32 *mask = NULL;
+	int nbits;
+	int ret;
+
+	if (arg[0] == '0' && arg[1] == 'x')
+		arg += 2;
+
+	maskptr = strchr(arg, '/');
+	len1 = maskptr ? ( maskptr - arg) : strlen(arg);
+	nwords = (len1 + 7) / 8;
+	nbits = 0;
+
+	value = malloc(nwords * sizeof(u32));
+	if (!value)
+		return -ENOMEM;
+	ret = -ENOMEM;
+	mask = malloc(nwords * sizeof(u32));
+	if (!mask)
+		goto free_value;
+
+	if (maskptr) {
+		has_mask = true;
+		maskptr++;
+		if (maskptr[0] == '0' && maskptr[1] == 'x')
+			maskptr += 2;
+		len2 = strlen(maskptr);
+		if (len2 > len1)
+			nwords = (len2 + 7) / 8;
+		mask = malloc(nwords * sizeof(u32));
+		if (!mask)
+			return -ENOMEM;
+		ret = parse_hex_string(maskptr, strlen(maskptr),
+					  mask);
+		if (ret < 0)
+			goto free_mask;
+		nbits = ret;
+	}
+
+	ret = parse_hex_string(arg, len1, value);
+	if (ret < 0)
+		goto free_value;
+	nbits = nbits ?: ret;
+	nwords = (nbits + 31) / 32;
+
+	ret = -EMSGSIZE;
+	if (!(nest = ethnla_nest_start(nlctx, type)) ||
+	    ethnla_put_u32(nlctx, ETHA_BITSET_SIZE, nbits) ||
+	    ethnla_put(nlctx, ETHA_BITSET_VALUE, nwords * sizeof(u32), value) ||
+	    (has_mask &&
+	     ethnla_put(nlctx, ETHA_BITSET_MASK, nwords * sizeof(u32), mask)))
+		goto free_mask;
+	mnl_attr_nest_end(nlctx->nlhdr, nest);
+	ret = 0;
+
+free_mask:
+	free(mask);
+free_value:
+	free(value);
+	nlctx->argp++;
+	nlctx->argc--;
+	return ret;
+}
+
+static int nl_parse_bitset_list(struct nl_context *nlctx, uint16_t type,
+				const void *data)
+{
+	struct nlmsghdr *nlhdr = nlctx->nlhdr;
+	struct nlattr *bitset_attr;
+	struct nlattr *bits_attr;
+	struct nlattr *bit_attr;
+	int ret;
+
+	ret = -EMSGSIZE;
+	bitset_attr = ethnla_nest_start(nlctx, type);
+	if (!bitset_attr)
+		return ret;
+	bits_attr = ethnla_nest_start(nlctx, ETHA_BITSET_BITS);
+	if (!bits_attr)
+		goto err;
+
+	while (nlctx->argc > 0) {
+		if (!strcmp(*nlctx->argp, "--")) {
+			nlctx->argp++;
+			nlctx->argc--;
+			break;
+		}
+		ret = -EINVAL;
+		if (nlctx->argc < 2 ||
+		    (strcmp(nlctx->argp[1], "on") &&
+		     strcmp(nlctx->argp[1], "off"))) {
+			parser_err_invalid_flag(nlctx, *nlctx->argp);
+			goto err;
+		}
+
+		ret = -EMSGSIZE;
+		bit_attr = ethnla_nest_start(nlctx, ETHA_BITS_BIT);
+		if (!bit_attr)
+			goto err;
+		if (ethnla_put_strz(nlctx, ETHA_BIT_NAME, nlctx->argp[0]))
+			goto err;
+		if (ethnla_put_flag(nlctx, ETHA_BIT_VALUE,
+				    !strcmp(nlctx->argp[1], "on")))
+			goto err;
+		mnl_attr_nest_end(nlhdr, bit_attr);
+
+		nlctx->argp += 2;
+		nlctx->argc -= 2;
+	}
+
+	mnl_attr_nest_end(nlhdr, bits_attr);
+	mnl_attr_nest_end(nlhdr, bitset_attr);
+	return 0;
+err:
+	mnl_attr_nest_cancel(nlhdr, bitset_attr);
+	return ret;
+}
+
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data)
+{
+	if (is_numeric_bitset(*nlctx->argp))
+		return nl_parse_bitset_compact(nlctx, type);
+	else
+		return nl_parse_bitset_list(nlctx, type, data);
+}
+
+int nl_parser(struct cmd_context *ctx, const struct param_parser *params,
+	      unsigned int max_type)
+{
+	struct nl_context *nlctx = ctx->nlctx;
+	bool attr_set[max_type];
+	int ret;
+
+	memset(attr_set, '\0', max_type * sizeof(attr_set[0]));
+
+	while (nlctx->argc > 0) {
+		const struct param_parser *parser = params;
+
+		nlctx->param = *nlctx->argp;
+		while (parser->arg) {
+			if (!strcmp(nlctx->param, parser->arg))
+				break;
+			parser++;
+		}
+		if (!parser->arg) {
+			parser_err_unknown_param(nlctx);
+			return -EINVAL;
+		}
+		nlctx->argp++;
+		nlctx->argc--;
+		if (attr_set[parser - params]) {
+			parser_err_dup_param(nlctx);
+			return -EINVAL;
+		}
+		if (nlctx->argc < parser->min_argc) {
+			parser_err_min_argc(nlctx, parser->min_argc);
+			return -EINVAL;
+		}
+
+		ret = parser->handler(nlctx, parser->type, parser->handler_data);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/netlink/parser.h b/netlink/parser.h
new file mode 100644
index 000000000000..7308da25f093
--- /dev/null
+++ b/netlink/parser.h
@@ -0,0 +1,45 @@
+#ifndef ETHTOOL_NETLINK_PARSER_H__
+#define ETHTOOL_NETLINK_PARSER_H__
+
+#include "netlink.h"
+
+struct lookup_entry_u32 {
+	const char	*arg;
+	u32		val;
+};
+
+struct lookup_entry_u16 {
+	const char	*arg;
+	u16		val;
+};
+
+struct lookup_entry_u8{
+	const char	*arg;
+	u8		val;
+};
+
+typedef int (*param_parser_cb_t)(struct nl_context *, uint16_t, const void *);
+
+struct param_parser {
+	const char		*arg;
+	uint16_t		type;
+	param_parser_cb_t	handler;
+	const void		*handler_data;
+	unsigned int		min_argc;
+};
+
+int nl_parse_direct_u32(struct nl_context *nlctx, uint16_t type,
+			const void *data);
+int nl_parse_direct_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data);
+int nl_parse_bool(struct nl_context *nlctx, uint16_t type, const void *data);
+int nl_parse_lookup_u8(struct nl_context *nlctx, uint16_t type,
+		       const void *data);
+int nl_parse_bitfield32(struct nl_context *nlctx, uint16_t type,
+			const void *data);
+int nl_parse_bitset(struct nl_context *nlctx, uint16_t type, const void *data);
+
+int nl_parser(struct cmd_context *ctx, const struct param_parser *params,
+	      unsigned int max_type);
+
+#endif /* ETHTOOL_NETLINK_PARSER_H__ */
-- 
2.18.0


  parent reply	other threads:[~2018-07-30 12:56 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-30 12:55 [RFC PATCH ethtool v2 00/23] ethtool netlink interface (userspace side) (WiP) Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 01/23] move UAPI header copies to a separate directory Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 02/23] update UAPI header copies Michal Kubecek
2018-07-30 12:55 ` [RFC PATCH ethtool v2 03/23] netlink: add netlink interface Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 04/23] netlink: add support for string sets Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 05/23] netlink: add notification monitor Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 06/23] netlink: add netlink handler for gdrv (-i) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 07/23] netlink: add netlink handler for gset (no option) Michal Kubecek
2018-07-30 12:56 ` Michal Kubecek [this message]
2018-07-30 12:56 ` [RFC PATCH ethtool v2 09/23] netlink: add netlink handler for sset (-s) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 10/23] netlink: add netlink handler for gfeatures (-k) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 11/23] netlink: add netlink handler for sfeatures (-K) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 12/23] netlink: add netlink handler for gcoalesce (-c) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 13/23] netlink: add netlink handler for gring (-g) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 14/23] netlink: add netlink handler for gpause (-a) Michal Kubecek
2018-07-30 12:56 ` [RFC PATCH ethtool v2 15/23] netlink: add netlink handler for gchannels (-l) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 16/23] netlink: add netlink handler for geee (--show-eee) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 17/23] netlink: add netlink handler for gfec (--show-fec) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 18/23] netlink: add netlink handler for scoalesce (-C) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 19/23] netlink: add netlink handler for sring (-G) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 20/23] netlink: add netlink handler for spause (-A) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 21/23] netlink: add netlink handler for schannels (-L) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 22/23] netlink: add netlink handler for seee (--set-eee) Michal Kubecek
2018-07-30 12:57 ` [RFC PATCH ethtool v2 23/23] netlink: add netlink handler for sfec (--set-fec) Michal Kubecek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=70e163a6ce3f75f44876b30bf28b26b132b4f27a.1532954671.git.mkubecek@suse.cz \
    --to=mkubecek@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linville@tuxdriver.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).