Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/809369/?format=api
{ "id": 809369, "url": "http://patchwork.ozlabs.org/api/patches/809369/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netfilter-devel/patch/20170903220356.20178-3-eric@regit.org/", "project": { "id": 26, "url": "http://patchwork.ozlabs.org/api/projects/26/?format=api", "name": "Netfilter Development", "link_name": "netfilter-devel", "list_id": "netfilter-devel.vger.kernel.org", "list_email": "netfilter-devel@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20170903220356.20178-3-eric@regit.org>", "list_archive_url": null, "date": "2017-09-03T22:03:56", "name": "[nft,2/2] src: get rid of printf", "commit_ref": null, "pull_url": null, "state": "changes-requested", "archived": false, "hash": "bee727ef5a306cc323ef6232ce06d8c4cc2c035b", "submitter": { "id": 8198, "url": "http://patchwork.ozlabs.org/api/people/8198/?format=api", "name": "Eric Leblond", "email": "eric@regit.org" }, "delegate": { "id": 6139, "url": "http://patchwork.ozlabs.org/api/users/6139/?format=api", "username": "pablo", "first_name": "Pablo", "last_name": "Neira", "email": "pablo@netfilter.org" }, "mbox": "http://patchwork.ozlabs.org/project/netfilter-devel/patch/20170903220356.20178-3-eric@regit.org/mbox/", "series": [ { "id": 1279, "url": "http://patchwork.ozlabs.org/api/series/1279/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netfilter-devel/list/?series=1279", "date": "2017-09-03T22:03:54", "name": "libnftables preparation work", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/1279/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/809369/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/809369/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<netfilter-devel-owner@vger.kernel.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org", "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netfilter-devel-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)", "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xln7g2BmVz9ryQ\n\tfor <incoming@patchwork.ozlabs.org>;\n\tMon, 4 Sep 2017 08:04:15 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1753027AbdICWEO (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tSun, 3 Sep 2017 18:04:14 -0400", "from home.regit.org ([37.187.126.138]:38262 \"EHLO home.regit.org\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S1753013AbdICWEN (ORCPT <rfc822; netfilter-devel@vger.kernel.org>);\n\tSun, 3 Sep 2017 18:04:13 -0400", "from [2a01:e35:2fb6:1160:f022:11:465e:d69b]\n\t(helo=ice-age2.regit.org) by home.regit.org with esmtpsa\n\t(TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.89)\n\t(envelope-from <eric@regit.org>)\n\tid 1doczu-0002FD-AR; Mon, 04 Sep 2017 00:04:11 +0200" ], "From": "Eric Leblond <eric@regit.org>", "To": "pablo@netfilter.org", "Cc": "netfilter-devel@vger.kernel.org, Eric Leblond <eric@regit.org>", "Subject": "[nft PATCH 2/2] src: get rid of printf", "Date": "Mon, 4 Sep 2017 00:03:56 +0200", "Message-Id": "<20170903220356.20178-3-eric@regit.org>", "X-Mailer": "git-send-email 2.14.1", "In-Reply-To": "<20170903220356.20178-1-eric@regit.org>", "References": "<20170903220356.20178-1-eric@regit.org>", "X-Spam-Score": "-1.0 (-)", "Sender": "netfilter-devel-owner@vger.kernel.org", "Precedence": "bulk", "List-ID": "<netfilter-devel.vger.kernel.org>", "X-Mailing-List": "netfilter-devel@vger.kernel.org" }, "content": "This patch introduces the nft_print_to_output_ctx function that has\nto be used instead of printf to output information that where\npreviously send to stdout. This function accumulate the output in\na buffer that can be fetched by the user with the nft_ctx_get_output()\nfunction.\n\nThis modification will allow the libnftables library to provide an\neasy way to the users to get the output data and display them like\nthey want.\n\nSigned-off-by: Eric Leblond <eric@regit.org>\n---\n include/datatype.h | 5 +-\n include/expression.h | 2 +-\n include/nftables.h | 5 ++\n src/cli.c | 1 +\n src/ct.c | 21 ++---\n src/datatype.c | 66 ++++++++-------\n src/expression.c | 79 ++++++++++--------\n src/exthdr.c | 16 ++--\n src/fib.c | 23 +++---\n src/hash.c | 10 +--\n src/main.c | 45 ++++++++++\n src/meta.c | 32 +++++---\n src/numgen.c | 8 +-\n src/payload.c | 9 +-\n src/rule.c | 228 +++++++++++++++++++++++++++++----------------------\n src/statement.c | 145 +++++++++++++++++---------------\n 16 files changed, 408 insertions(+), 287 deletions(-)", "diff": "diff --git a/include/datatype.h b/include/datatype.h\nindex 2e34591..e9f6079 100644\n--- a/include/datatype.h\n+++ b/include/datatype.h\n@@ -209,7 +209,8 @@ extern void symbolic_constant_print(const struct symbol_table *tbl,\n \t\t\t\t struct output_ctx *octx);\n extern void symbol_table_print(const struct symbol_table *tbl,\n \t\t\t const struct datatype *dtype,\n-\t\t\t enum byteorder byteorder);\n+\t\t\t enum byteorder byteorder,\n+\t\t\t struct output_ctx *octx);\n \n extern struct symbol_table *rt_symbol_table_init(const char *filename);\n extern void rt_symbol_table_free(struct symbol_table *tbl);\n@@ -261,7 +262,7 @@ extern const struct datatype *\n set_datatype_alloc(const struct datatype *orig_dtype, unsigned int byteorder);\n extern void set_datatype_destroy(const struct datatype *dtype);\n \n-extern void time_print(uint64_t seconds);\n+extern void time_print(uint64_t seconds, struct output_ctx *octx);\n extern struct error_record *time_parse(const struct location *loc,\n \t\t\t\t const char *c, uint64_t *res);\n \ndiff --git a/include/expression.h b/include/expression.h\nindex 32d4423..ce6b702 100644\n--- a/include/expression.h\n+++ b/include/expression.h\n@@ -334,7 +334,7 @@ extern struct expr *expr_get(struct expr *expr);\n extern void expr_free(struct expr *expr);\n extern void expr_print(const struct expr *expr, struct output_ctx *octx);\n extern bool expr_cmp(const struct expr *e1, const struct expr *e2);\n-extern void expr_describe(const struct expr *expr);\n+extern void expr_describe(const struct expr *expr, struct output_ctx *octx);\n \n extern const struct datatype *expr_basetype(const struct expr *expr);\n extern void expr_set_type(struct expr *expr, const struct datatype *dtype,\ndiff --git a/include/nftables.h b/include/nftables.h\nindex 7c4e93f..f4d5ce1 100644\n--- a/include/nftables.h\n+++ b/include/nftables.h\n@@ -30,6 +30,8 @@ struct output_ctx {\n \tunsigned int ip2name;\n \tunsigned int handle;\n \tunsigned int echo;\n+\tchar *output_buf;\n+\tsize_t output_buf_len;\n };\n \n struct nft_cache {\n@@ -149,4 +151,7 @@ void realm_table_meta_exit(void);\n void devgroup_table_exit(void);\n void realm_table_rt_exit(void);\n \n+int nft_print_to_output_ctx(struct output_ctx *octx, const char *fmt, ...);\n+char *nft_ctx_get_output(struct nft_ctx *ctx);\n+\n #endif /* NFTABLES_NFTABLES_H */\ndiff --git a/src/cli.c b/src/cli.c\nindex d923ff7..ca4418c 100644\n--- a/src/cli.c\n+++ b/src/cli.c\n@@ -138,6 +138,7 @@ static void cli_complete(char *line)\n \t\t cli_nft->debug_mask);\n \tscanner_push_buffer(scanner, &indesc_cli, line);\n \tnft_run(cli_nft, cli_nf_sock, scanner, state, &msgs);\n+\tprintf(\"%s\", nft_ctx_get_output(cli_nft));\n \terec_print_list(stdout, &msgs, cli_nft->debug_mask);\n \txfree(line);\n \tcache_release(&cli_nft->cache);\ndiff --git a/src/ct.c b/src/ct.c\nindex d64f467..f19608a 100644\n--- a/src/ct.c\n+++ b/src/ct.c\n@@ -141,11 +141,12 @@ static void ct_label_type_print(const struct expr *expr,\n \tfor (s = ct_label_tbl->symbols; s->identifier != NULL; s++) {\n \t\tif (bit != s->value)\n \t\t\tcontinue;\n-\t\tprintf(\"\\\"%s\\\"\", s->identifier);\n+\t\tnft_print_to_output_ctx(octx, \"\\\"%s\\\"\", s->identifier);\n \t\treturn;\n \t}\n \t/* can happen when connlabel.conf is altered after rules were added */\n-\tprintf(\"%ld\\n\", (long)mpz_scan1(expr->value, 0));\n+\tnft_print_to_output_ctx(octx, \"%ld\\n\",\n+\t\t\t\t(long)mpz_scan1(expr->value, 0));\n }\n \n static struct error_record *ct_label_type_parse(const struct expr *sym,\n@@ -269,27 +270,27 @@ static const struct ct_template ct_templates[] = {\n \t\t\t\t\t BYTEORDER_HOST_ENDIAN, 32),\n };\n \n-static void ct_print(enum nft_ct_keys key, int8_t dir)\n+static void ct_print(enum nft_ct_keys key, int8_t dir, struct output_ctx *octx)\n {\n \tconst struct symbolic_constant *s;\n \n-\tprintf(\"ct \");\n+\tnft_print_to_output_ctx(octx, \"ct \");\n \tif (dir < 0)\n \t\tgoto done;\n \n \tfor (s = ct_dir_tbl.symbols; s->identifier != NULL; s++) {\n \t\tif (dir == (int)s->value) {\n-\t\t\tprintf(\"%s \", s->identifier);\n+\t\t\tnft_print_to_output_ctx(octx, \"%s \", s->identifier);\n \t\t\tbreak;\n \t\t}\n \t}\n done:\n-\tprintf(\"%s\", ct_templates[key].token);\n+\tnft_print_to_output_ctx(octx, \"%s\", ct_templates[key].token);\n }\n \n static void ct_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n-\tct_print(expr->ct.key, expr->ct.direction);\n+\tct_print(expr->ct.key, expr->ct.direction, octx);\n }\n \n static bool ct_expr_cmp(const struct expr *e1, const struct expr *e2)\n@@ -445,8 +446,8 @@ void ct_expr_update_type(struct proto_ctx *ctx, struct expr *expr)\n \n static void ct_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tct_print(stmt->ct.key, stmt->ct.direction);\n-\tprintf(\" set \");\n+\tct_print(stmt->ct.key, stmt->ct.direction, octx);\n+\tnft_print_to_output_ctx(octx, \" set \");\n \texpr_print(stmt->ct.expr, octx);\n }\n \n@@ -472,7 +473,7 @@ struct stmt *ct_stmt_alloc(const struct location *loc, enum nft_ct_keys key,\n \n static void notrack_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"notrack\");\n+\tnft_print_to_output_ctx(octx, \"notrack\");\n }\n \n static const struct stmt_ops notrack_stmt_ops = {\ndiff --git a/src/datatype.c b/src/datatype.c\nindex 5bd0c7b..b309c8f 100644\n--- a/src/datatype.c\n+++ b/src/datatype.c\n@@ -192,15 +192,15 @@ void symbolic_constant_print(const struct symbol_table *tbl,\n \t\treturn expr_basetype(expr)->print(expr, octx);\n \n \tif (quotes)\n-\t\tprintf(\"\\\"\");\n+\t\tnft_print_to_output_ctx(octx, \"\\\"\");\n \n \tif (octx->numeric > NUMERIC_ALL)\n-\t\tprintf(\"%\"PRIu64\"\", val);\n+\t\tnft_print_to_output_ctx(octx, \"%\" PRIu64 \"\", val);\n \telse\n-\t\tprintf(\"%s\", s->identifier);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", s->identifier);\n \n \tif (quotes)\n-\t\tprintf(\"\\\"\");\n+\t\tnft_print_to_output_ctx(octx, \"\\\"\");\n }\n \n static void switch_byteorder(void *data, unsigned int len)\n@@ -215,7 +215,8 @@ static void switch_byteorder(void *data, unsigned int len)\n \n void symbol_table_print(const struct symbol_table *tbl,\n \t\t\tconst struct datatype *dtype,\n-\t\t\tenum byteorder byteorder)\n+\t\t\tenum byteorder byteorder,\n+\t\t\tstruct output_ctx *octx)\n {\n \tconst struct symbolic_constant *s;\n \tunsigned int len = dtype->size / BITS_PER_BYTE;\n@@ -228,16 +229,21 @@ void symbol_table_print(const struct symbol_table *tbl,\n \t\t\tswitch_byteorder(&value, len);\n \n \t\tif (tbl->base == BASE_DECIMAL)\n-\t\t\tprintf(\"\\t%-30s\\t%20\"PRIu64\"\\n\", s->identifier, value);\n+\t\t\tnft_print_to_output_ctx(octx,\n+\t\t\t\t\t\t\"\\t%-30s\\t%20\" PRIu64 \"\\n\",\n+\t\t\t\t\t\ts->identifier, value);\n \t\telse\n-\t\t\tprintf(\"\\t%-30s\\t0x%.*\" PRIx64 \"\\n\",\n-\t\t\t s->identifier, 2 * len, value);\n+\t\t\tnft_print_to_output_ctx(octx,\n+\t\t\t\t\t\t\"\\t%-30s\\t0x%.*\" PRIx64 \"\\n\",\n+\t\t\t\t\t\ts->identifier, 2 * len, value);\n \t}\n }\n \n static void invalid_type_print(const struct expr *expr, struct output_ctx *octx)\n {\n-\tgmp_printf(\"0x%Zx [invalid type]\", expr->value);\n+\tchar buf[512];\n+\tgmp_snprintf(buf, sizeof(buf), \"0x%Zx [invalid type]\", expr->value);\n+\tnft_print_to_output_ctx(octx, \"%s\", buf);\n }\n \n const struct datatype invalid_type = {\n@@ -251,30 +257,30 @@ static void verdict_type_print(const struct expr *expr, struct output_ctx *octx)\n {\n \tswitch (expr->verdict) {\n \tcase NFT_CONTINUE:\n-\t\tprintf(\"continue\");\n+\t\tnft_print_to_output_ctx(octx, \"continue\");\n \t\tbreak;\n \tcase NFT_BREAK:\n-\t\tprintf(\"break\");\n+\t\tnft_print_to_output_ctx(octx, \"break\");\n \t\tbreak;\n \tcase NFT_JUMP:\n-\t\tprintf(\"jump %s\", expr->chain);\n+\t\tnft_print_to_output_ctx(octx, \"jump %s\", expr->chain);\n \t\tbreak;\n \tcase NFT_GOTO:\n-\t\tprintf(\"goto %s\", expr->chain);\n+\t\tnft_print_to_output_ctx(octx, \"goto %s\", expr->chain);\n \t\tbreak;\n \tcase NFT_RETURN:\n-\t\tprintf(\"return\");\n+\t\tnft_print_to_output_ctx(octx, \"return\");\n \t\tbreak;\n \tdefault:\n \t\tswitch (expr->verdict & NF_VERDICT_MASK) {\n \t\tcase NF_ACCEPT:\n-\t\t\tprintf(\"accept\");\n+\t\t\tnft_print_to_output_ctx(octx, \"accept\");\n \t\t\tbreak;\n \t\tcase NF_DROP:\n-\t\t\tprintf(\"drop\");\n+\t\t\tnft_print_to_output_ctx(octx, \"drop\");\n \t\t\tbreak;\n \t\tcase NF_QUEUE:\n-\t\t\tprintf(\"queue\");\n+\t\t\tnft_print_to_output_ctx(octx, \"queue\");\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tBUG(\"invalid verdict value %u\\n\", expr->verdict);\n@@ -319,6 +325,7 @@ static void integer_type_print(const struct expr *expr, struct output_ctx *octx)\n {\n \tconst struct datatype *dtype = expr->dtype;\n \tconst char *fmt = \"%Zu\";\n+\tchar buf[256];\n \n \tdo {\n \t\tif (dtype->basefmt != NULL) {\n@@ -327,7 +334,8 @@ static void integer_type_print(const struct expr *expr, struct output_ctx *octx)\n \t\t}\n \t} while ((dtype = dtype->basetype));\n \n-\tgmp_printf(fmt, expr->value);\n+\tgmp_snprintf(buf, sizeof(buf),fmt, expr->value);\n+\tnft_print_to_output_ctx(octx, \"%s\", buf);\n }\n \n static struct error_record *integer_type_parse(const struct expr *sym,\n@@ -364,7 +372,7 @@ static void string_type_print(const struct expr *expr, struct output_ctx *octx)\n \n \tmpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);\n \tdata[len] = '\\0';\n-\tprintf(\"\\\"%s\\\"\", data);\n+\tnft_print_to_output_ctx(octx, \"\\\"%s\\\"\", data);\n }\n \n static struct error_record *string_type_parse(const struct expr *sym,\n@@ -396,7 +404,7 @@ static void lladdr_type_print(const struct expr *expr, struct output_ctx *octx)\n \tmpz_export_data(data, expr->value, BYTEORDER_BIG_ENDIAN, len);\n \n \tfor (i = 0; i < len; i++) {\n-\t\tprintf(\"%s%.2x\", delim, data[i]);\n+\t\tnft_print_to_output_ctx(octx, \"%s%.2x\", delim, data[i]);\n \t\tdelim = \":\";\n \t}\n }\n@@ -449,7 +457,7 @@ static void ipaddr_type_print(const struct expr *expr, struct output_ctx *octx)\n \t\tgetnameinfo((struct sockaddr *)&sin, sizeof(sin), buf,\n \t\t\t sizeof(buf), NULL, 0, NI_NUMERICHOST);\n \t}\n-\tprintf(\"%s\", buf);\n+\tnft_print_to_output_ctx(octx, \"%s\", buf);\n }\n \n static struct error_record *ipaddr_type_parse(const struct expr *sym,\n@@ -507,7 +515,7 @@ static void ip6addr_type_print(const struct expr *expr, struct output_ctx *octx)\n \t\tgetnameinfo((struct sockaddr *)&sin6, sizeof(sin6), buf,\n \t\t\t sizeof(buf), NULL, 0, NI_NUMERICHOST);\n \t}\n-\tprintf(\"%s\", buf);\n+\tnft_print_to_output_ctx(octx, \"%s\", buf);\n }\n \n static struct error_record *ip6addr_type_parse(const struct expr *sym,\n@@ -557,7 +565,7 @@ static void inet_protocol_type_print(const struct expr *expr,\n \tif (octx->numeric < NUMERIC_ALL) {\n \t\tp = getprotobynumber(mpz_get_uint8(expr->value));\n \t\tif (p != NULL) {\n-\t\t\tprintf(\"%s\", p->p_name);\n+\t\t\tnft_print_to_output_ctx(octx, \"%s\", p->p_name);\n \t\t\treturn;\n \t\t}\n \t}\n@@ -821,7 +829,7 @@ const struct datatype icmpx_code_type = {\n \t.sym_tbl\t= &icmpx_code_tbl,\n };\n \n-void time_print(uint64_t seconds)\n+void time_print(uint64_t seconds, struct output_ctx *octx)\n {\n \tuint64_t days, hours, minutes;\n \n@@ -835,13 +843,13 @@ void time_print(uint64_t seconds)\n \tseconds %= 60;\n \n \tif (days > 0)\n-\t\tprintf(\"%\"PRIu64\"d\", days);\n+\t\tnft_print_to_output_ctx(octx, \"%\" PRIu64 \"d\", days);\n \tif (hours > 0)\n-\t\tprintf(\"%\"PRIu64\"h\", hours);\n+\t\tnft_print_to_output_ctx(octx, \"%\" PRIu64 \"h\", hours);\n \tif (minutes > 0)\n-\t\tprintf(\"%\"PRIu64\"m\", minutes);\n+\t\tnft_print_to_output_ctx(octx, \"%\" PRIu64 \"m\", minutes);\n \tif (seconds > 0)\n-\t\tprintf(\"%\"PRIu64\"s\", seconds);\n+\t\tnft_print_to_output_ctx(octx, \"%\" PRIu64 \"s\", seconds);\n }\n \n enum {\n@@ -933,7 +941,7 @@ struct error_record *time_parse(const struct location *loc, const char *str,\n \n static void time_type_print(const struct expr *expr, struct output_ctx *octx)\n {\n-\ttime_print(mpz_get_uint64(expr->value) / MSEC_PER_SEC);\n+\ttime_print(mpz_get_uint64(expr->value) / MSEC_PER_SEC, octx);\n }\n \n static struct error_record *time_type_parse(const struct expr *sym,\ndiff --git a/src/expression.c b/src/expression.c\nindex d41ada3..56cc12e 100644\n--- a/src/expression.c\n+++ b/src/expression.c\n@@ -86,41 +86,44 @@ bool expr_cmp(const struct expr *e1, const struct expr *e2)\n \treturn e1->ops->cmp(e1, e2);\n }\n \n-void expr_describe(const struct expr *expr)\n+void expr_describe(const struct expr *expr, struct output_ctx *octx)\n {\n \tconst struct datatype *dtype = expr->dtype;\n \tconst char *delim = \"\";\n \n-\tprintf(\"%s expression, datatype %s (%s)\",\n-\t\texpr->ops->name, dtype->name, dtype->desc);\n+\tnft_print_to_output_ctx(octx, \"%s expression, datatype %s (%s)\",\n+\t\t\t\texpr->ops->name, dtype->name, dtype->desc);\n \tif (dtype->basetype != NULL) {\n-\t\tprintf(\" (basetype \");\n+\t\tnft_print_to_output_ctx(octx, \" (basetype \");\n \t\tfor (dtype = dtype->basetype; dtype != NULL;\n \t\t dtype = dtype->basetype) {\n-\t\t\tprintf(\"%s%s\", delim, dtype->desc);\n+\t\t\tnft_print_to_output_ctx(octx, \"%s%s\", delim,\n+\t\t\t\t\t\tdtype->desc);\n \t\t\tdelim = \", \";\n \t\t}\n-\t\tprintf(\")\");\n+\t\tnft_print_to_output_ctx(octx, \")\");\n \t}\n \n \tif (expr_basetype(expr)->type == TYPE_STRING) {\n \t\tif (expr->len)\n-\t\t\tprintf(\", %u characters\", expr->len / BITS_PER_BYTE);\n+\t\t\tnft_print_to_output_ctx(octx, \", %u characters\",\n+\t\t\t\t\t\texpr->len / BITS_PER_BYTE);\n \t\telse\n-\t\t\tprintf(\", dynamic length\");\n+\t\t\tnft_print_to_output_ctx(octx, \", dynamic length\");\n \t} else\n-\t\tprintf(\", %u bits\", expr->len);\n+\t\tnft_print_to_output_ctx(octx, \", %u bits\", expr->len);\n \n-\tprintf(\"\\n\");\n+\tnft_print_to_output_ctx(octx, \"\\n\");\n \n \tif (expr->dtype->sym_tbl != NULL) {\n-\t\tprintf(\"\\npre-defined symbolic constants \");\n+\t\tnft_print_to_output_ctx(octx,\n+\t\t\t\t\t\"\\npre-defined symbolic constants \");\n \t\tif (expr->dtype->sym_tbl->base == BASE_DECIMAL)\n-\t\t\tprintf(\"(in decimal):\\n\");\n+\t\t\tnft_print_to_output_ctx(octx, \"(in decimal):\\n\");\n \t\telse\n-\t\t\tprintf(\"(in hexadecimal):\\n\");\n+\t\t\tnft_print_to_output_ctx(octx, \"(in hexadecimal):\\n\");\n \t\tsymbol_table_print(expr->dtype->sym_tbl, expr->dtype,\n-\t\t\t\t expr->byteorder);\n+\t\t\t\t expr->byteorder, octx);\n \t}\n }\n \n@@ -215,7 +218,8 @@ struct expr *verdict_expr_alloc(const struct location *loc,\n \n static void symbol_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n-\tprintf(\"%s%s\", expr->scope != NULL ? \"$\" : \"\", expr->identifier);\n+\tnft_print_to_output_ctx(octx, \"%s%s\", expr->scope != NULL ? \"$\" : \"\",\n+\t\t\t\texpr->identifier);\n }\n \n static void symbol_expr_clone(struct expr *new, const struct expr *expr)\n@@ -398,7 +402,7 @@ struct expr *bitmask_expr_to_binops(struct expr *expr)\n static void prefix_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n \texpr_print(expr->prefix, octx);\n-\tprintf(\"/%u\", expr->prefix_len);\n+\tnft_print_to_output_ctx(octx, \"/%u\", expr->prefix_len);\n }\n \n static void prefix_expr_set_type(const struct expr *expr,\n@@ -513,10 +517,10 @@ static void binop_arg_print(const struct expr *op, const struct expr *arg,\n \t\tprec = 1;\n \n \tif (prec)\n-\t\tprintf(\"(\");\n+\t\tnft_print_to_output_ctx(octx, \"(\");\n \texpr_print(arg, octx);\n \tif (prec)\n-\t\tprintf(\")\");\n+\t\tnft_print_to_output_ctx(octx, \")\");\n }\n \n static bool must_print_eq_op(const struct expr *expr)\n@@ -534,9 +538,10 @@ static void binop_expr_print(const struct expr *expr, struct output_ctx *octx)\n \n \tif (expr_op_symbols[expr->op] &&\n \t (expr->op != OP_EQ || must_print_eq_op(expr)))\n-\t\tprintf(\" %s \", expr_op_symbols[expr->op]);\n+\t\tnft_print_to_output_ctx(octx, \" %s \",\n+\t\t\t\t\texpr_op_symbols[expr->op]);\n \telse\n-\t\tprintf(\" \");\n+\t\tnft_print_to_output_ctx(octx, \" \");\n \n \tbinop_arg_print(expr, expr->right, octx);\n }\n@@ -602,7 +607,7 @@ static void range_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n \toctx->numeric += NUMERIC_ALL + 1;\n \texpr_print(expr->left, octx);\n-\tprintf(\"-\");\n+\tnft_print_to_output_ctx(octx, \"-\");\n \texpr_print(expr->right, octx);\n \toctx->numeric -= NUMERIC_ALL + 1;\n }\n@@ -682,7 +687,7 @@ static void compound_expr_print(const struct expr *expr, const char *delim,\n \tconst char *d = \"\";\n \n \tlist_for_each_entry(i, &expr->expressions, list) {\n-\t\tprintf(\"%s\", d);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", d);\n \t\texpr_print(i, octx);\n \t\td = delim;\n \t}\n@@ -793,16 +798,16 @@ static void set_expr_print(const struct expr *expr, struct output_ctx *octx)\n \tconst char *d = \"\";\n \tint count = 0;\n \n-\tprintf(\"{ \");\n+\tnft_print_to_output_ctx(octx, \"{ \");\n \n \tlist_for_each_entry(i, &expr->expressions, list) {\n-\t\tprintf(\"%s\", d);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", d);\n \t\texpr_print(i, octx);\n \t\tcount++;\n \t\td = calculate_delim(expr, &count);\n \t}\n \n-\tprintf(\" }\");\n+\tnft_print_to_output_ctx(octx, \" }\");\n }\n \n static void set_expr_set_type(const struct expr *expr,\n@@ -840,7 +845,7 @@ struct expr *set_expr_alloc(const struct location *loc, const struct set *set)\n static void mapping_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n \texpr_print(expr->left, octx);\n-\tprintf(\" : \");\n+\tnft_print_to_output_ctx(octx, \" : \");\n \texpr_print(expr->right, octx);\n }\n \n@@ -889,9 +894,9 @@ static void map_expr_print(const struct expr *expr, struct output_ctx *octx)\n \texpr_print(expr->map, octx);\n \tif (expr->mappings->ops->type == EXPR_SET_REF &&\n \t expr->mappings->set->datatype->type == TYPE_VERDICT)\n-\t\tprintf(\" vmap \");\n+\t\tnft_print_to_output_ctx(octx, \" vmap \");\n \telse\n-\t\tprintf(\" map \");\n+\t\tnft_print_to_output_ctx(octx, \" map \");\n \texpr_print(expr->mappings, octx);\n }\n \n@@ -930,11 +935,12 @@ static void set_ref_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n \tif (expr->set->flags & NFT_SET_ANONYMOUS) {\n \t\tif (expr->set->flags & NFT_SET_EVAL)\n-\t\t\tprintf(\"table %s\", expr->set->handle.set);\n+\t\t\tnft_print_to_output_ctx(octx, \"table %s\",\n+\t\t\t\t\t\texpr->set->handle.set);\n \t\telse\n \t\t\texpr_print(expr->set->init, octx);\n \t} else {\n-\t\tprintf(\"@%s\", expr->set->handle.set);\n+\t\tnft_print_to_output_ctx(octx, \"@%s\", expr->set->handle.set);\n \t}\n }\n \n@@ -971,18 +977,19 @@ static void set_elem_expr_print(const struct expr *expr,\n {\n \texpr_print(expr->key, octx);\n \tif (expr->timeout) {\n-\t\tprintf(\" timeout \");\n-\t\ttime_print(expr->timeout / 1000);\n+\t\tnft_print_to_output_ctx(octx, \" timeout \");\n+\t\ttime_print(expr->timeout / 1000, octx);\n \t}\n \tif (!octx->stateless && expr->expiration) {\n-\t\tprintf(\" expires \");\n-\t\ttime_print(expr->expiration / 1000);\n+\t\tnft_print_to_output_ctx(octx, \" expires \");\n+\t\ttime_print(expr->expiration / 1000, octx);\n \t}\n \tif (expr->comment)\n-\t\tprintf(\" comment \\\"%s\\\"\", expr->comment);\n+\t\tnft_print_to_output_ctx(octx, \" comment \\\"%s\\\"\",\n+\t\t\t\t\texpr->comment);\n \n \tif (expr->stmt) {\n-\t\tprintf(\" : \");\n+\t\tnft_print_to_output_ctx(octx, \" : \");\n \t\tstmt_print(expr->stmt, octx);\n \t}\n }\ndiff --git a/src/exthdr.c b/src/exthdr.c\nindex 4add3da..c8a33f8 100644\n--- a/src/exthdr.c\n+++ b/src/exthdr.c\n@@ -34,20 +34,24 @@ static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx)\n \t\tchar buf[9] = {0};\n \n \t\tif (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) {\n-\t\t\tprintf(\"tcp option %s\", expr->exthdr.desc->name);\n+\t\t\tnft_print_to_output_ctx(octx, \"tcp option %s\",\n+\t\t\t\t\t\texpr->exthdr.desc->name);\n \t\t\treturn;\n \t\t}\n \n \t\tif (offset)\n \t\t\tsnprintf(buf, sizeof buf, \"%d\", offset);\n-\t\tprintf(\"tcp option %s%s %s\", expr->exthdr.desc->name, buf,\n-\t\t\t\t\t expr->exthdr.tmpl->token);\n+\t\tnft_print_to_output_ctx(octx, \"tcp option %s%s %s\",\n+\t\t\t\t\texpr->exthdr.desc->name, buf,\n+\t\t\t\t\texpr->exthdr.tmpl->token);\n \t} else {\n \t\tif (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT)\n-\t\t\tprintf(\"exthdr %s\", expr->exthdr.desc->name);\n+\t\t\tnft_print_to_output_ctx(octx, \"exthdr %s\",\n+\t\t\t\t\t\texpr->exthdr.desc->name);\n \t\telse {\n-\t\t\tprintf(\"%s %s\", expr->exthdr.desc ? expr->exthdr.desc->name : \"unknown-exthdr\",\n-\t\t\t\t\texpr->exthdr.tmpl->token);\n+\t\t\tnft_print_to_output_ctx(octx, \"%s %s\",\n+\t\t\t\t\t\texpr->exthdr.desc ? expr->exthdr.desc->name : \"unknown-exthdr\",\n+\t\t\t\t\t\texpr->exthdr.tmpl->token);\n \t\t}\n \t}\n }\ndiff --git a/src/fib.c b/src/fib.c\nindex b3488af..df39e35 100644\n--- a/src/fib.c\n+++ b/src/fib.c\n@@ -60,32 +60,33 @@ static const char *fib_result_str(enum nft_fib_result result)\n \treturn \"unknown\";\n }\n \n-static void __fib_expr_print_f(unsigned int *flags, unsigned int f, const char *s)\n+static void __fib_expr_print_f(unsigned int *flags, unsigned int f,\n+\t\t\t const char *s, struct output_ctx *octx)\n {\n \tif ((*flags & f) == 0)\n \t\treturn;\n \n-\tprintf(\"%s\", s);\n+\tnft_print_to_output_ctx(octx, \"%s\", s);\n \t*flags &= ~f;\n \tif (*flags)\n-\t\tprintf(\" . \");\n+\t\tnft_print_to_output_ctx(octx, \" . \");\n }\n \n static void fib_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n \tunsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT;\n \n-\tprintf(\"fib \");\n-\t__fib_expr_print_f(&flags, NFTA_FIB_F_SADDR, \"saddr\");\n-\t__fib_expr_print_f(&flags, NFTA_FIB_F_DADDR, \"daddr\");\n-\t__fib_expr_print_f(&flags, NFTA_FIB_F_MARK, \"mark\");\n-\t__fib_expr_print_f(&flags, NFTA_FIB_F_IIF, \"iif\");\n-\t__fib_expr_print_f(&flags, NFTA_FIB_F_OIF, \"oif\");\n+\tnft_print_to_output_ctx(octx, \"fib \");\n+\t__fib_expr_print_f(&flags, NFTA_FIB_F_SADDR, \"saddr\", octx);\n+\t__fib_expr_print_f(&flags, NFTA_FIB_F_DADDR, \"daddr\", octx);\n+\t__fib_expr_print_f(&flags, NFTA_FIB_F_MARK, \"mark\", octx);\n+\t__fib_expr_print_f(&flags, NFTA_FIB_F_IIF, \"iif\", octx);\n+\t__fib_expr_print_f(&flags, NFTA_FIB_F_OIF, \"oif\", octx);\n \n \tif (flags)\n-\t\tprintf(\"0x%x\", flags);\n+\t\tnft_print_to_output_ctx(octx, \"0x%x\", flags);\n \n-\tprintf(\" %s\", fib_result_str(expr->fib.result));\n+\tnft_print_to_output_ctx(octx, \" %s\", fib_result_str(expr->fib.result));\n }\n \n static bool fib_expr_cmp(const struct expr *e1, const struct expr *e2)\ndiff --git a/src/hash.c b/src/hash.c\nindex 1a4bfb3..7820a81 100644\n--- a/src/hash.c\n+++ b/src/hash.c\n@@ -19,19 +19,19 @@ static void hash_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n \tswitch (expr->hash.type) {\n \tcase NFT_HASH_SYM:\n-\t\tprintf(\"symhash\");\n+\t\tnft_print_to_output_ctx(octx, \"symhash\");\n \tbreak;\n \tcase NFT_HASH_JENKINS:\n \tdefault:\n-\t\tprintf(\"jhash \");\n+\t\tnft_print_to_output_ctx(octx, \"jhash \");\n \t\texpr_print(expr->hash.expr, octx);\n \t}\n \n-\tprintf(\" mod %u\", expr->hash.mod);\n+\tnft_print_to_output_ctx(octx, \" mod %u\", expr->hash.mod);\n \tif (expr->hash.seed_set)\n-\t\tprintf(\" seed 0x%x\", expr->hash.seed);\n+\t\tnft_print_to_output_ctx(octx, \" seed 0x%x\", expr->hash.seed);\n \tif (expr->hash.offset)\n-\t\tprintf(\" offset %u\", expr->hash.offset);\n+\t\tnft_print_to_output_ctx(octx, \" offset %u\", expr->hash.offset);\n }\n \n static bool hash_expr_cmp(const struct expr *e1, const struct expr *e2)\ndiff --git a/src/main.c b/src/main.c\nindex 94f8a47..5f0142e 100644\n--- a/src/main.c\n+++ b/src/main.c\n@@ -233,6 +233,11 @@ out:\n \treturn ret;\n }\n \n+static void nft_ctx_reset_output_buffer(struct nft_ctx *ctx)\n+{\n+\tctx->output.output_buf[0] = 0;\n+}\n+\n int nft_run(struct nft_ctx *nft, struct mnl_socket *nf_sock,\n \t void *scanner, struct parser_state *state,\n \t struct list_head *msgs)\n@@ -240,6 +245,9 @@ int nft_run(struct nft_ctx *nft, struct mnl_socket *nf_sock,\n \tstruct cmd *cmd, *next;\n \tint ret;\n \n+\t/* reset the output buffer so we have only one command in output*/\n+\tnft_ctx_reset_output_buffer(nft);\n+\n \tret = nft_parse(nft, scanner, state);\n \tif (ret != 0 || state->nerrs > 0) {\n \t\tret = -1;\n@@ -302,6 +310,9 @@ static struct nft_ctx *nft_ctx_new(uint32_t flags)\n \tif (! (flags & NFT_CTX_CUSTOM_NETLINK))\n \t\tnft_ctx_netlink_init(ctx);\n \n+\tctx->output.output_buf_len = 128;\n+\tctx->output.output_buf = xzalloc(ctx->output.output_buf_len);\n+\n \treturn ctx;\n }\n \n@@ -312,10 +323,16 @@ static void nft_ctx_free(const struct nft_ctx *ctx)\n \n \tiface_cache_release();\n \tcache_release(&nft->cache);\n+\txfree(ctx->output.output_buf);\n \txfree(ctx);\n \tnft_exit();\n }\n \n+char *nft_ctx_get_output(struct nft_ctx *ctx)\n+{\n+\treturn ctx->output.output_buf;\n+}\n+\n static int nft_run_cmd_from_buffer(struct nft_ctx *nft,\n \t\t\t\t char *buf, size_t buflen)\n {\n@@ -330,6 +347,7 @@ static int nft_run_cmd_from_buffer(struct nft_ctx *nft,\n \n \tif (nft_run(nft, nft->nf_sock, scanner, &state, &msgs) != 0)\n \t\trc = NFT_EXIT_FAILURE;\n+\tprintf(\"%s\", nft_ctx_get_output(nft));\n \n \terec_print_list(stderr, &msgs, nft->debug_mask);\n \tscanner_destroy(scanner);\n@@ -356,6 +374,7 @@ static int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename)\n \n \tif (nft_run(nft, nft->nf_sock, scanner, &state, &msgs) != 0)\n \t\trc = NFT_EXIT_FAILURE;\n+\tprintf(\"%s\", nft_ctx_get_output(nft));\n err:\n \terec_print_list(stderr, &msgs, nft->debug_mask);\n \tscanner_destroy(scanner);\n@@ -363,6 +382,32 @@ err:\n \treturn rc;\n }\n \n+__attribute__((format(printf, 2, 0)))\n+int nft_print_to_output_ctx(struct output_ctx *octx, const char *fmt, ...)\n+{\n+\tchar buf[4096];\n+\tva_list arg;\n+\tint ret;\n+\tint rlen = octx->output_buf_len - strlen(octx->output_buf);\n+\tva_start(arg, fmt);\n+\tret = vsnprintf(buf, 4096, fmt, arg);\n+\tva_end(arg);\n+\tif (ret > rlen) {\n+\t\tchar *reallocbuf;\n+\t\tsize_t tot_len = strlen(octx->output_buf) + ret + 1;\n+\t\tsize_t reallocsize = 2 * octx->output_buf_len;\n+\t\twhile (reallocsize < tot_len)\n+\t\t\treallocsize *= 2;\n+\t\treallocbuf = realloc(octx->output_buf, reallocsize);\n+\t\tif (!reallocbuf)\n+\t\t\treturn -1;\n+\t\toctx->output_buf = reallocbuf;\n+\t\toctx->output_buf_len = reallocsize;\n+\t}\n+\tstrncat(octx->output_buf, buf, octx->output_buf_len - strlen(octx->output_buf) - 1);\n+\treturn 0;\n+}\n+\n int main(int argc, char * const *argv)\n {\n \tchar *buf = NULL, *filename = NULL;\ndiff --git a/src/meta.c b/src/meta.c\nindex 9c80893..8d271a8 100644\n--- a/src/meta.c\n+++ b/src/meta.c\n@@ -54,13 +54,15 @@ static void tchandle_type_print(const struct expr *expr,\n \n \tswitch(handle) {\n \tcase TC_H_ROOT:\n-\t\tprintf(\"root\");\n+\t\tnft_print_to_output_ctx(octx, \"root\");\n \t\tbreak;\n \tcase TC_H_UNSPEC:\n-\t\tprintf(\"none\");\n+\t\tnft_print_to_output_ctx(octx, \"none\");\n \t\tbreak;\n \tdefault:\n-\t\tprintf(\"%0x:%0x\", TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));\n+\t\tnft_print_to_output_ctx(octx, \"%0x:%0x\",\n+\t\t\t\t\tTC_H_MAJ(handle) >> 16,\n+\t\t\t\t\tTC_H_MIN(handle));\n \t\tbreak;\n \t}\n }\n@@ -134,9 +136,9 @@ static void ifindex_type_print(const struct expr *expr, struct output_ctx *octx)\n \n \tifindex = mpz_get_uint32(expr->value);\n \tif (nft_if_indextoname(ifindex, name))\n-\t\tprintf(\"\\\"%s\\\"\", name);\n+\t\tnft_print_to_output_ctx(octx, \"\\\"%s\\\"\", name);\n \telse\n-\t\tprintf(\"%d\", ifindex);\n+\t\tnft_print_to_output_ctx(octx, \"%d\", ifindex);\n }\n \n static struct error_record *ifindex_type_parse(const struct expr *sym,\n@@ -209,9 +211,9 @@ static void uid_type_print(const struct expr *expr, struct output_ctx *octx)\n \n \t\tpw = getpwuid(uid);\n \t\tif (pw != NULL)\n-\t\t\tprintf(\"\\\"%s\\\"\", pw->pw_name);\n+\t\t\tnft_print_to_output_ctx(octx, \"\\\"%s\\\"\", pw->pw_name);\n \t\telse\n-\t\t\tprintf(\"%d\", uid);\n+\t\t\tnft_print_to_output_ctx(octx, \"%d\", uid);\n \t\treturn;\n \t}\n \texpr_basetype(expr)->print(expr, octx);\n@@ -261,9 +263,9 @@ static void gid_type_print(const struct expr *expr, struct output_ctx *octx)\n \n \t\tgr = getgrgid(gid);\n \t\tif (gr != NULL)\n-\t\t\tprintf(\"\\\"%s\\\"\", gr->gr_name);\n+\t\t\tnft_print_to_output_ctx(octx, \"\\\"%s\\\"\", gr->gr_name);\n \t\telse\n-\t\t\tprintf(\"%u\", gid);\n+\t\t\tnft_print_to_output_ctx(octx, \"%u\", gid);\n \t\treturn;\n \t}\n \texpr_basetype(expr)->print(expr, octx);\n@@ -446,9 +448,11 @@ static bool meta_key_is_qualified(enum nft_meta_keys key)\n static void meta_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n \tif (meta_key_is_qualified(expr->meta.key))\n-\t\tprintf(\"meta %s\", meta_templates[expr->meta.key].token);\n+\t\tnft_print_to_output_ctx(octx, \"meta %s\",\n+\t\t\t\t meta_templates[expr->meta.key].token);\n \telse\n-\t\tprintf(\"%s\", meta_templates[expr->meta.key].token);\n+\t\tnft_print_to_output_ctx(octx, \"%s\",\n+\t\t\t\t meta_templates[expr->meta.key].token);\n }\n \n static bool meta_expr_cmp(const struct expr *e1, const struct expr *e2)\n@@ -573,9 +577,11 @@ struct expr *meta_expr_alloc(const struct location *loc, enum nft_meta_keys key)\n static void meta_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n \tif (meta_key_is_qualified(stmt->meta.key))\n-\t\tprintf(\"meta %s set \", meta_templates[stmt->meta.key].token);\n+\t\tnft_print_to_output_ctx(octx, \"meta %s set \",\n+\t\t\t\t\tmeta_templates[stmt->meta.key].token);\n \telse\n-\t\tprintf(\"%s set \", meta_templates[stmt->meta.key].token);\n+\t\tnft_print_to_output_ctx(octx, \"%s set \",\n+\t\t\t\t\tmeta_templates[stmt->meta.key].token);\n \n \texpr_print(stmt->meta.expr, octx);\n }\ndiff --git a/src/numgen.c b/src/numgen.c\nindex 19a4a9c..af844fb 100644\n--- a/src/numgen.c\n+++ b/src/numgen.c\n@@ -30,10 +30,12 @@ static const char *numgen_type_str(enum nft_ng_types type)\n \n static void numgen_expr_print(const struct expr *expr, struct output_ctx *octx)\n {\n-\tprintf(\"numgen %s mod %u\", numgen_type_str(expr->numgen.type),\n-\t expr->numgen.mod);\n+\tnft_print_to_output_ctx(octx, \"numgen %s mod %u\",\n+\t\t\t\tnumgen_type_str(expr->numgen.type),\n+\t\t\t\texpr->numgen.mod);\n \tif (expr->numgen.offset)\n-\t\tprintf(\" offset %u\", expr->numgen.offset);\n+\t\tnft_print_to_output_ctx(octx, \" offset %u\",\n+\t\t\t\t expr->numgen.offset);\n }\n \n static bool numgen_expr_cmp(const struct expr *e1, const struct expr *e2)\ndiff --git a/src/payload.c b/src/payload.c\nindex 7f94ff7..be05e22 100644\n--- a/src/payload.c\n+++ b/src/payload.c\n@@ -46,11 +46,12 @@ static void payload_expr_print(const struct expr *expr, struct output_ctx *octx)\n \tdesc = expr->payload.desc;\n \ttmpl = expr->payload.tmpl;\n \tif (payload_is_known(expr))\n-\t\tprintf(\"%s %s\", desc->name, tmpl->token);\n+\t\tnft_print_to_output_ctx(octx, \"%s %s\", desc->name,\n+\t\t\t\t tmpl->token);\n \telse\n-\t\tprintf(\"payload @%s,%u,%u\",\n-\t\t proto_base_tokens[expr->payload.base],\n-\t\t expr->payload.offset, expr->len);\n+\t\tnft_print_to_output_ctx(octx, \"payload @%s,%u,%u\",\n+\t\t\t\t proto_base_tokens[expr->payload.base],\n+\t\t\t\t expr->payload.offset, expr->len);\n }\n \n static bool payload_expr_cmp(const struct expr *e1, const struct expr *e2)\ndiff --git a/src/rule.c b/src/rule.c\nindex 44d36c1..1c7ee48 100644\n--- a/src/rule.c\n+++ b/src/rule.c\n@@ -273,7 +273,8 @@ static const char *set_policy2str(uint32_t policy)\n }\n \n static void set_print_declaration(const struct set *set,\n-\t\t\t\t struct print_fmt_options *opts)\n+\t\t\t\t struct print_fmt_options *opts,\n+\t\t\t\t struct output_ctx *octx)\n {\n \tconst char *delim = \"\";\n \tconst char *type;\n@@ -286,34 +287,39 @@ static void set_print_declaration(const struct set *set,\n \telse\n \t\ttype = \"set\";\n \n-\tprintf(\"%s%s\", opts->tab, type);\n+\tnft_print_to_output_ctx(octx, \"%s%s\", opts->tab, type);\n \n \tif (opts->family != NULL)\n-\t\tprintf(\" %s\", opts->family);\n+\t\tnft_print_to_output_ctx(octx, \" %s\", opts->family);\n \n \tif (opts->table != NULL)\n-\t\tprintf(\" %s\", opts->table);\n+\t\tnft_print_to_output_ctx(octx, \" %s\", opts->table);\n \n-\tprintf(\" %s {%s\", set->handle.set, opts->nl);\n+\tnft_print_to_output_ctx(octx, \" %s {%s\", set->handle.set, opts->nl);\n \n-\tprintf(\"%s%stype %s\", opts->tab, opts->tab, set->keytype->name);\n+\tnft_print_to_output_ctx(octx, \"%s%stype %s\", opts->tab, opts->tab,\n+\t\t\t\tset->keytype->name);\n \tif (set->flags & NFT_SET_MAP)\n-\t\tprintf(\" : %s\", set->datatype->name);\n+\t\tnft_print_to_output_ctx(octx, \" : %s\", set->datatype->name);\n \telse if (set->flags & NFT_SET_OBJECT)\n-\t\tprintf(\" : %s\", obj_type_name(set->objtype));\n+\t\tnft_print_to_output_ctx(octx, \" : %s\",\n+\t\t\t\t\tobj_type_name(set->objtype));\n \n-\tprintf(\"%s\", opts->stmt_separator);\n+\tnft_print_to_output_ctx(octx, \"%s\", opts->stmt_separator);\n \n \tif (!(set->flags & (NFT_SET_CONSTANT))) {\n \t\tif (set->policy != NFT_SET_POL_PERFORMANCE) {\n-\t\t\tprintf(\"%s%spolicy %s%s\", opts->tab, opts->tab,\n-\t\t\t set_policy2str(set->policy),\n-\t\t\t opts->stmt_separator);\n+\t\t\tnft_print_to_output_ctx(octx, \"%s%spolicy %s%s\",\n+\t\t\t\t\t\topts->tab, opts->tab,\n+\t\t\t\t\t\tset_policy2str(set->policy),\n+\t\t\t\t\t\topts->stmt_separator);\n \t\t}\n \n \t\tif (set->desc.size > 0) {\n-\t\t\tprintf(\"%s%ssize %u%s\", opts->tab, opts->tab,\n-\t\t\t set->desc.size, opts->stmt_separator);\n+\t\t\tnft_print_to_output_ctx(octx, \"%s%ssize %u%s\",\n+\t\t\t\t\t\topts->tab, opts->tab,\n+\t\t\t\t\t\tset->desc.size,\n+\t\t\t\t\t\topts->stmt_separator);\n \t\t}\n \t}\n \n@@ -323,45 +329,49 @@ static void set_print_declaration(const struct set *set,\n \t\tflags &= ~NFT_SET_TIMEOUT;\n \n \tif (flags & (NFT_SET_CONSTANT | NFT_SET_INTERVAL | NFT_SET_TIMEOUT)) {\n-\t\tprintf(\"%s%sflags \", opts->tab, opts->tab);\n+\t\tnft_print_to_output_ctx(octx, \"%s%sflags \", opts->tab,\n+\t\t\t\t\topts->tab);\n \t\tif (set->flags & NFT_SET_CONSTANT) {\n-\t\t\tprintf(\"%sconstant\", delim);\n+\t\t\tnft_print_to_output_ctx(octx, \"%sconstant\", delim);\n \t\t\tdelim = \",\";\n \t\t}\n \t\tif (set->flags & NFT_SET_INTERVAL) {\n-\t\t\tprintf(\"%sinterval\", delim);\n+\t\t\tnft_print_to_output_ctx(octx, \"%sinterval\", delim);\n \t\t\tdelim = \",\";\n \t\t}\n \t\tif (set->flags & NFT_SET_TIMEOUT) {\n-\t\t\tprintf(\"%stimeout\", delim);\n+\t\t\tnft_print_to_output_ctx(octx, \"%stimeout\", delim);\n \t\t\tdelim = \",\";\n \t\t}\n-\t\tprintf(\"%s\", opts->stmt_separator);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", opts->stmt_separator);\n \t}\n \n \tif (set->timeout) {\n-\t\tprintf(\"%s%stimeout \", opts->tab, opts->tab);\n-\t\ttime_print(set->timeout / 1000);\n-\t\tprintf(\"%s\", opts->stmt_separator);\n+\t\tnft_print_to_output_ctx(octx, \"%s%stimeout \", opts->tab,\n+\t\t\t\t\topts->tab);\n+\t\ttime_print(set->timeout / 1000, octx);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", opts->stmt_separator);\n \t}\n \tif (set->gc_int) {\n-\t\tprintf(\"%s%sgc-interval \", opts->tab, opts->tab);\n-\t\ttime_print(set->gc_int / 1000);\n-\t\tprintf(\"%s\", opts->stmt_separator);\n+\t\tnft_print_to_output_ctx(octx, \"%s%sgc-interval \", opts->tab,\n+\t\t\t\t\topts->tab);\n+\t\ttime_print(set->gc_int / 1000, octx);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", opts->stmt_separator);\n \t}\n }\n \n static void do_set_print(const struct set *set, struct print_fmt_options *opts,\n \t\t\t struct output_ctx *octx)\n {\n-\tset_print_declaration(set, opts);\n+\tset_print_declaration(set, opts, octx);\n \n \tif (set->init != NULL && set->init->size > 0) {\n-\t\tprintf(\"%s%selements = \", opts->tab, opts->tab);\n+\t\tnft_print_to_output_ctx(octx, \"%s%selements = \", opts->tab,\n+\t\t\t\t\topts->tab);\n \t\texpr_print(set->init, octx);\n-\t\tprintf(\"%s\", opts->nl);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", opts->nl);\n \t}\n-\tprintf(\"%s}%s\", opts->tab, opts->nl);\n+\tnft_print_to_output_ctx(octx, \"%s}%s\", opts->tab, opts->nl);\n }\n \n void set_print(const struct set *s, struct output_ctx *octx)\n@@ -426,14 +436,16 @@ void rule_print(const struct rule *rule, struct output_ctx *octx)\n \tlist_for_each_entry(stmt, &rule->stmts, list) {\n \t\tstmt->ops->print(stmt, octx);\n \t\tif (!list_is_last(&stmt->list, &rule->stmts))\n-\t\t\tprintf(\" \");\n+\t\t\tnft_print_to_output_ctx(octx, \" \");\n \t}\n \n \tif (rule->comment)\n-\t\tprintf(\" comment \\\"%s\\\"\", rule->comment);\n+\t\tnft_print_to_output_ctx(octx, \" comment \\\"%s\\\"\",\n+\t\t\t\t rule->comment);\n \n \tif (octx->handle > 0)\n-\t\tprintf(\" # handle %\" PRIu64, rule->handle.handle.id);\n+\t\tnft_print_to_output_ctx(octx, \" # handle %\" PRIu64,\n+\t\t\t\t rule->handle.handle.id);\n }\n \n struct rule *rule_lookup(const struct chain *chain, uint64_t handle)\n@@ -663,21 +675,25 @@ static const char *chain_policy2str(uint32_t policy)\n \treturn \"unknown\";\n }\n \n-static void chain_print_declaration(const struct chain *chain)\n+static void chain_print_declaration(const struct chain *chain,\n+\t\t\t\t struct output_ctx *octx)\n {\n-\tprintf(\"\\tchain %s {\\n\", chain->handle.chain);\n+\tnft_print_to_output_ctx(octx, \"\\tchain %s {\\n\", chain->handle.chain);\n \tif (chain->flags & CHAIN_F_BASECHAIN) {\n \t\tif (chain->dev != NULL) {\n-\t\t\tprintf(\"\\t\\ttype %s hook %s device %s priority %d; policy %s;\\n\",\n-\t\t\t chain->type,\n-\t\t\t hooknum2str(chain->handle.family, chain->hooknum),\n-\t\t\t chain->dev, chain->priority,\n-\t\t\t chain_policy2str(chain->policy));\n+\t\t\tnft_print_to_output_ctx(octx,\n+\t\t\t\t\t\t\"\\t\\ttype %s hook %s device %s priority %d; policy %s;\\n\",\n+\t\t\t\t\t\tchain->type,\n+\t\t\t\t\t\thooknum2str(chain->handle.family, chain->hooknum),\n+\t\t\t\t\t\tchain->dev, chain->priority,\n+\t\t\t\t\t\tchain_policy2str(chain->policy));\n \t\t} else {\n-\t\t\tprintf(\"\\t\\ttype %s hook %s priority %d; policy %s;\\n\",\n-\t\t\t chain->type,\n-\t\t\t hooknum2str(chain->handle.family, chain->hooknum),\n-\t\t\t chain->priority, chain_policy2str(chain->policy));\n+\t\t\tnft_print_to_output_ctx(octx,\n+\t\t\t\t\t\t\"\\t\\ttype %s hook %s priority %d; policy %s;\\n\",\n+\t\t\t\t\t\tchain->type,\n+\t\t\t\t\t\thooknum2str(chain->handle.family, chain->hooknum),\n+\t\t\t\t\t\tchain->priority,\n+\t\t\t\t\t\tchain_policy2str(chain->policy));\n \t\t}\n \t}\n }\n@@ -686,14 +702,14 @@ static void chain_print(const struct chain *chain, struct output_ctx *octx)\n {\n \tstruct rule *rule;\n \n-\tchain_print_declaration(chain);\n+\tchain_print_declaration(chain, octx);\n \n \tlist_for_each_entry(rule, &chain->rules, list) {\n-\t\tprintf(\"\\t\\t\");\n+\t\tnft_print_to_output_ctx(octx, \"\\t\\t\");\n \t\trule_print(rule, octx);\n-\t\tprintf(\"\\n\");\n+\t\tnft_print_to_output_ctx(octx, \"\\n\");\n \t}\n-\tprintf(\"\\t}\\n\");\n+\tnft_print_to_output_ctx(octx, \"\\t}\\n\");\n }\n \n void chain_print_plain(const struct chain *chain)\n@@ -798,27 +814,28 @@ static void table_print(const struct table *table, struct output_ctx *octx)\n \tconst char *delim = \"\";\n \tconst char *family = family2str(table->handle.family);\n \n-\tprintf(\"table %s %s {\\n\", family, table->handle.table);\n+\tnft_print_to_output_ctx(octx, \"table %s %s {\\n\", family,\n+\t\t\t\ttable->handle.table);\n \ttable_print_options(table, &delim);\n \n \tlist_for_each_entry(obj, &table->objs, list) {\n-\t\tprintf(\"%s\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", delim);\n \t\tobj_print(obj, octx);\n \t\tdelim = \"\\n\";\n \t}\n \tlist_for_each_entry(set, &table->sets, list) {\n \t\tif (set->flags & NFT_SET_ANONYMOUS)\n \t\t\tcontinue;\n-\t\tprintf(\"%s\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", delim);\n \t\tset_print(set, octx);\n \t\tdelim = \"\\n\";\n \t}\n \tlist_for_each_entry(chain, &table->chains, list) {\n-\t\tprintf(\"%s\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%s\", delim);\n \t\tchain_print(chain, octx);\n \t\tdelim = \"\\n\";\n \t}\n-\tprintf(\"}\\n\");\n+\tnft_print_to_output_ctx(octx, \"}\\n\");\n }\n \n struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,\n@@ -1180,9 +1197,9 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)\n \t\t cmd->handle.family != table->handle.family)\n \t\t\tcontinue;\n \n-\t\tprintf(\"table %s %s {\\n\",\n-\t\t family2str(table->handle.family),\n-\t\t table->handle.table);\n+\t\tnft_print_to_output_ctx(ctx->octx, \"table %s %s {\\n\",\n+\t\t\t\t\tfamily2str(table->handle.family),\n+\t\t\t\t\ttable->handle.table);\n \n \t\tlist_for_each_entry(set, &table->sets, list) {\n \t\t\tif (cmd->obj == CMD_OBJ_SETS &&\n@@ -1195,11 +1212,12 @@ static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd)\n \t\t\tif (cmd->obj == CMD_OBJ_MAPS &&\n \t\t\t !(set->flags & NFT_SET_MAP))\n \t\t\t\tcontinue;\n-\t\t\tset_print_declaration(set, &opts);\n-\t\t\tprintf(\"%s}%s\", opts.tab, opts.nl);\n+\t\t\tset_print_declaration(set, &opts, ctx->octx);\n+\t\t\tnft_print_to_output_ctx(ctx->octx, \"%s}%s\", opts.tab,\n+\t\t\t\t\t\topts.nl);\n \t\t}\n \n-\t\tprintf(\"}\\n\");\n+\t\tnft_print_to_output_ctx(ctx->octx, \"}\\n\");\n \t}\n \treturn 0;\n }\n@@ -1264,40 +1282,46 @@ static void obj_print_data(const struct obj *obj,\n {\n \tswitch (obj->type) {\n \tcase NFT_OBJECT_COUNTER:\n-\t\tprintf(\" %s {%s%s%s\", obj->handle.obj,\n-\t\t\t\t opts->nl, opts->tab, opts->tab);\n+\t\tnft_print_to_output_ctx(octx, \" %s {%s%s%s\", obj->handle.obj,\n+\t\t\t\t\topts->nl, opts->tab, opts->tab);\n \t\tif (octx->stateless) {\n-\t\t\tprintf(\"packets 0 bytes 0\");\n+\t\t\tnft_print_to_output_ctx(octx, \"packets 0 bytes 0\");\n \t\t\tbreak;\n \t\t}\n-\t\tprintf(\"packets %\"PRIu64\" bytes %\"PRIu64\"\",\n-\t\t obj->counter.packets, obj->counter.bytes);\n+\t\tnft_print_to_output_ctx(octx,\n+\t\t\t\t\t\"packets %\" PRIu64 \" bytes %\" PRIu64 \"\",\n+\t\t\t\t\tobj->counter.packets,\n+\t\t\t\t\tobj->counter.bytes);\n \t\tbreak;\n \tcase NFT_OBJECT_QUOTA: {\n \t\tconst char *data_unit;\n \t\tuint64_t bytes;\n \n-\t\tprintf(\" %s {%s%s%s\", obj->handle.obj,\n-\t\t\t\t opts->nl, opts->tab, opts->tab);\n+\t\tnft_print_to_output_ctx(octx, \" %s {%s%s%s\", obj->handle.obj,\n+\t\t\t\t\topts->nl, opts->tab, opts->tab);\n \t\tdata_unit = get_rate(obj->quota.bytes, &bytes);\n-\t\tprintf(\"%s%\"PRIu64\" %s\",\n-\t\t obj->quota.flags & NFT_QUOTA_F_INV ? \"over \" : \"\",\n-\t\t bytes, data_unit);\n+\t\tnft_print_to_output_ctx(octx, \"%s%\" PRIu64 \" %s\",\n+\t\t\t\t\tobj->quota.flags & NFT_QUOTA_F_INV ? \"over \" : \"\",\n+\t\t\t\t\tbytes, data_unit);\n \t\tif (!octx->stateless && obj->quota.used) {\n \t\t\tdata_unit = get_rate(obj->quota.used, &bytes);\n-\t\t\tprintf(\" used %\"PRIu64\" %s\", bytes, data_unit);\n+\t\t\tnft_print_to_output_ctx(octx, \" used %\" PRIu64 \" %s\",\n+\t\t\t\t\t\tbytes, data_unit);\n \t\t}\n \t\t}\n \t\tbreak;\n \tcase NFT_OBJECT_CT_HELPER: {\n-\t\tprintf(\"ct helper %s {\\n\", obj->handle.obj);\n-\t\tprintf(\"\\t\\ttype \\\"%s\\\" protocol \", obj->ct_helper.name);\n+\t\tnft_print_to_output_ctx(octx, \"ct helper %s {\\n\",\n+\t\t\t\t\tobj->handle.obj);\n+\t\tnft_print_to_output_ctx(octx, \"\\t\\ttype \\\"%s\\\" protocol \",\n+\t\t\t\t\tobj->ct_helper.name);\n \t\tprint_proto_name_proto(obj->ct_helper.l4proto);\n-\t\tprintf(\"\\t\\tl3proto %s\", family2str(obj->ct_helper.l3proto));\n+\t\tnft_print_to_output_ctx(octx, \"\\t\\tl3proto %s\",\n+\t\t\t\t\tfamily2str(obj->ct_helper.l3proto));\n \t\tbreak;\n \t\t}\n \tdefault:\n-\t\tprintf(\"unknown {%s\", opts->nl);\n+\t\tnft_print_to_output_ctx(octx, \"unknown {%s\", opts->nl);\n \t\tbreak;\n \t}\n }\n@@ -1332,17 +1356,19 @@ static void obj_print_declaration(const struct obj *obj,\n \t\t\t\t struct print_fmt_options *opts,\n \t\t\t\t struct output_ctx *octx)\n {\n-\tprintf(\"%s%s\", opts->tab, obj_type_name(obj->type));\n+\tnft_print_to_output_ctx(octx, \"%s%s\", opts->tab,\n+\t\t\t\tobj_type_name(obj->type));\n \n \tif (opts->family != NULL)\n-\t\tprintf(\" %s\", opts->family);\n+\t\tnft_print_to_output_ctx(octx, \" %s\", opts->family);\n \n \tif (opts->table != NULL)\n-\t\tprintf(\" %s\", opts->table);\n+\t\tnft_print_to_output_ctx(octx, \" %s\", opts->table);\n \n \tobj_print_data(obj, opts, octx);\n \n-\tprintf(\"%s%s}%s\", opts->nl, opts->tab, opts->nl);\n+\tnft_print_to_output_ctx(octx, \"%s%s}%s\", opts->nl, opts->tab,\n+\t\t\t\topts->nl);\n }\n \n void obj_print(const struct obj *obj, struct output_ctx *octx)\n@@ -1383,13 +1409,13 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type)\n \t\t cmd->handle.family != table->handle.family)\n \t\t\tcontinue;\n \n-\t\tprintf(\"table %s %s {\\n\",\n-\t\t family2str(table->handle.family),\n-\t\t table->handle.table);\n+\t\tnft_print_to_output_ctx(ctx->octx, \"table %s %s {\\n\",\n+\t\t\t\t\tfamily2str(table->handle.family),\n+\t\t\t\t\ttable->handle.table);\n \n \t\tif (cmd->handle.table != NULL &&\n \t\t strcmp(cmd->handle.table, table->handle.table)) {\n-\t\t\tprintf(\"}\\n\");\n+\t\t\tnft_print_to_output_ctx(ctx->octx, \"}\\n\");\n \t\t\tcontinue;\n \t\t}\n \n@@ -1402,7 +1428,7 @@ static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type)\n \t\t\tobj_print_declaration(obj, &opts, ctx->octx);\n \t\t}\n \n-\t\tprintf(\"}\\n\");\n+\t\tnft_print_to_output_ctx(ctx->octx, \"}\\n\");\n \t}\n \treturn 0;\n }\n@@ -1438,19 +1464,20 @@ static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd)\n \t\t cmd->handle.family != table->handle.family)\n \t\t\tcontinue;\n \n-\t\tprintf(\"table %s %s\\n\",\n-\t\t family2str(table->handle.family),\n-\t\t table->handle.table);\n+\t\tnft_print_to_output_ctx(ctx->octx, \"table %s %s\\n\",\n+\t\t\t\t\tfamily2str(table->handle.family),\n+\t\t\t\t\ttable->handle.table);\n \t}\n \n \treturn 0;\n }\n \n-static void table_print_declaration(struct table *table)\n+static void table_print_declaration(struct table *table,\n+\t\t\t\t struct output_ctx *octx)\n {\n-\tprintf(\"table %s %s {\\n\",\n-\t\tfamily2str(table->handle.family),\n-\t\ttable->handle.table);\n+\tnft_print_to_output_ctx(octx, \"table %s %s {\\n\",\n+\t\t\t\tfamily2str(table->handle.family),\n+\t\t\t\ttable->handle.table);\n }\n \n static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,\n@@ -1458,7 +1485,7 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,\n {\n \tstruct chain *chain;\n \n-\ttable_print_declaration(table);\n+\ttable_print_declaration(table, ctx->octx);\n \n \tlist_for_each_entry(chain, &table->chains, list) {\n \t\tif (chain->handle.family != cmd->handle.family ||\n@@ -1468,7 +1495,7 @@ static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd,\n \t\tchain_print(chain, ctx->octx);\n \t}\n \n-\tprintf(\"}\\n\");\n+\tnft_print_to_output_ctx(ctx->octx, \"}\\n\");\n \n \treturn 0;\n }\n@@ -1483,13 +1510,13 @@ static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd)\n \t\t cmd->handle.family != table->handle.family)\n \t\t\tcontinue;\n \n-\t\ttable_print_declaration(table);\n+\t\ttable_print_declaration(table, ctx->octx);\n \n \t\tlist_for_each_entry(chain, &table->chains, list) {\n-\t\t\tchain_print_declaration(chain);\n-\t\t\tprintf(\"\\t}\\n\");\n+\t\t\tchain_print_declaration(chain, ctx->octx);\n+\t\t\tnft_print_to_output_ctx(ctx->octx, \"\\t}\\n\");\n \t\t}\n-\t\tprintf(\"}\\n\");\n+\t\tnft_print_to_output_ctx(ctx->octx, \"}\\n\");\n \t}\n \n \treturn 0;\n@@ -1504,9 +1531,9 @@ static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd,\n \tif (set == NULL)\n \t\treturn -1;\n \n-\ttable_print_declaration(table);\n+\ttable_print_declaration(table, ctx->octx);\n \tset_print(set, ctx->octx);\n-\tprintf(\"}\\n\");\n+\tnft_print_to_output_ctx(ctx->octx, \"}\\n\");\n \n \treturn 0;\n }\n@@ -1693,9 +1720,10 @@ static int do_command_monitor(struct netlink_ctx *ctx, struct cmd *cmd)\n \treturn netlink_monitor(&monhandler, ctx->nf_sock);\n }\n \n-static int do_command_describe(struct netlink_ctx *ctx, struct cmd *cmd)\n+static int do_command_describe(struct netlink_ctx *ctx, struct cmd *cmd,\n+\t\t\t struct output_ctx *octx)\n {\n-\texpr_describe(cmd->expr);\n+\texpr_describe(cmd->expr, octx);\n \treturn 0;\n }\n \n@@ -1741,7 +1769,7 @@ int do_command(struct netlink_ctx *ctx, struct cmd *cmd)\n \tcase CMD_MONITOR:\n \t\treturn do_command_monitor(ctx, cmd);\n \tcase CMD_DESCRIBE:\n-\t\treturn do_command_describe(ctx, cmd);\n+\t\treturn do_command_describe(ctx, cmd, ctx->octx);\n \tdefault:\n \t\tBUG(\"invalid command object type %u\\n\", cmd->obj);\n \t}\ndiff --git a/src/statement.c b/src/statement.c\nindex 58f8aaf..ef4f6dd 100644\n--- a/src/statement.c\n+++ b/src/statement.c\n@@ -109,20 +109,20 @@ struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)\n \n static void flow_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"flow \");\n+\tnft_print_to_output_ctx(octx, \"flow \");\n \tif (stmt->flow.set) {\n \t\texpr_print(stmt->flow.set, octx);\n-\t\tprintf(\" \");\n+\t\tnft_print_to_output_ctx(octx, \" \");\n \t}\n-\tprintf(\"{ \");\n+\tnft_print_to_output_ctx(octx, \"{ \");\n \texpr_print(stmt->flow.key, octx);\n-\tprintf(\" \");\n+\tnft_print_to_output_ctx(octx, \" \");\n \n \toctx->stateless++;\n \tstmt_print(stmt->flow.stmt, octx);\n \toctx->stateless--;\n \n-\tprintf(\"} \");\n+\tnft_print_to_output_ctx(octx, \"} \");\n \n }\n \n@@ -147,13 +147,13 @@ struct stmt *flow_stmt_alloc(const struct location *loc)\n \n static void counter_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"counter\");\n+\tnft_print_to_output_ctx(octx, \"counter\");\n \n \tif (octx->stateless)\n \t\treturn;\n \n-\tprintf(\" packets %\" PRIu64 \" bytes %\" PRIu64,\n-\t stmt->counter.packets, stmt->counter.bytes);\n+\tnft_print_to_output_ctx(octx, \" packets %\" PRIu64 \" bytes %\" PRIu64,\n+\t\t\t\tstmt->counter.packets, stmt->counter.bytes);\n }\n \n static const struct stmt_ops counter_stmt_ops = {\n@@ -189,10 +189,11 @@ static void objref_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n \tswitch (stmt->objref.type) {\n \tcase NFT_OBJECT_CT_HELPER:\n-\t\tprintf(\"ct helper set \");\n+\t\tnft_print_to_output_ctx(octx, \"ct helper set \");\n \t\tbreak;\n \tdefault:\n-\t\tprintf(\"%s name \", objref_type_name(stmt->objref.type));\n+\t\tnft_print_to_output_ctx(octx, \"%s name \",\n+\t\t\t\t\tobjref_type_name(stmt->objref.type));\n \t\tbreak;\n \t}\n \texpr_print(stmt->objref.expr, octx);\n@@ -233,39 +234,44 @@ static const char *log_level(uint32_t level)\n \n static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"log\");\n+\tnft_print_to_output_ctx(octx, \"log\");\n \tif (stmt->log.flags & STMT_LOG_PREFIX)\n-\t\tprintf(\" prefix \\\"%s\\\"\", stmt->log.prefix);\n+\t\tnft_print_to_output_ctx(octx, \" prefix \\\"%s\\\"\",\n+\t\t\t\t\tstmt->log.prefix);\n \tif (stmt->log.flags & STMT_LOG_GROUP)\n-\t\tprintf(\" group %u\", stmt->log.group);\n+\t\tnft_print_to_output_ctx(octx, \" group %u\", stmt->log.group);\n \tif (stmt->log.flags & STMT_LOG_SNAPLEN)\n-\t\tprintf(\" snaplen %u\", stmt->log.snaplen);\n+\t\tnft_print_to_output_ctx(octx, \" snaplen %u\",\n+\t\t\t\t\tstmt->log.snaplen);\n \tif (stmt->log.flags & STMT_LOG_QTHRESHOLD)\n-\t\tprintf(\" queue-threshold %u\", stmt->log.qthreshold);\n+\t\tnft_print_to_output_ctx(octx, \" queue-threshold %u\",\n+\t\t\t\t\tstmt->log.qthreshold);\n \tif ((stmt->log.flags & STMT_LOG_LEVEL) &&\n \t stmt->log.level != LOG_WARNING)\n-\t\tprintf(\" level %s\", log_level(stmt->log.level));\n+\t\tnft_print_to_output_ctx(octx, \" level %s\",\n+\t\t\t\t\tlog_level(stmt->log.level));\n \n \tif ((stmt->log.logflags & NF_LOG_MASK) == NF_LOG_MASK) {\n-\t\tprintf(\" flags all\");\n+\t\tnft_print_to_output_ctx(octx, \" flags all\");\n \t} else {\n \t\tif (stmt->log.logflags & (NF_LOG_TCPSEQ | NF_LOG_TCPOPT)) {\n \t\t\tconst char *delim = \" \";\n \n-\t\t\tprintf(\" flags tcp\");\n+\t\t\tnft_print_to_output_ctx(octx, \" flags tcp\");\n \t\t\tif (stmt->log.logflags & NF_LOG_TCPSEQ) {\n-\t\t\t\tprintf(\" sequence\");\n+\t\t\t\tnft_print_to_output_ctx(octx, \" sequence\");\n \t\t\t\tdelim = \",\";\n \t\t\t}\n \t\t\tif (stmt->log.logflags & NF_LOG_TCPOPT)\n-\t\t\t\tprintf(\"%soptions\", delim);\n+\t\t\t\tnft_print_to_output_ctx(octx, \"%soptions\",\n+\t\t\t\t\t\t\tdelim);\n \t\t}\n \t\tif (stmt->log.logflags & NF_LOG_IPOPT)\n-\t\t\tprintf(\" flags ip options\");\n+\t\t\tnft_print_to_output_ctx(octx, \" flags ip options\");\n \t\tif (stmt->log.logflags & NF_LOG_UID)\n-\t\t\tprintf(\" flags skuid\");\n+\t\t\tnft_print_to_output_ctx(octx, \" flags skuid\");\n \t\tif (stmt->log.logflags & NF_LOG_MACDECODE)\n-\t\t\tprintf(\" flags ether\");\n+\t\t\tnft_print_to_output_ctx(octx, \" flags ether\");\n \t}\n }\n \n@@ -328,23 +334,26 @@ static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n \n \tswitch (stmt->limit.type) {\n \tcase NFT_LIMIT_PKTS:\n-\t\tprintf(\"limit rate %s%\" PRIu64 \"/%s\",\n-\t\t inv ? \"over \" : \"\", stmt->limit.rate,\n-\t\t get_unit(stmt->limit.unit));\n+\t\tnft_print_to_output_ctx(octx, \"limit rate %s%\" PRIu64 \"/%s\",\n+\t\t\t\t\tinv ? \"over \" : \"\", stmt->limit.rate,\n+\t\t\t\t\tget_unit(stmt->limit.unit));\n \t\tif (stmt->limit.burst > 0)\n-\t\t\tprintf(\" burst %u packets\", stmt->limit.burst);\n+\t\t\tnft_print_to_output_ctx(octx, \" burst %u packets\",\n+\t\t\t\t\t\tstmt->limit.burst);\n \t\tbreak;\n \tcase NFT_LIMIT_PKT_BYTES:\n \t\tdata_unit = get_rate(stmt->limit.rate, &rate);\n \n-\t\tprintf(\"limit rate %s%\" PRIu64 \" %s/%s\",\n-\t\t inv ? \"over \" : \"\", rate, data_unit,\n-\t\t get_unit(stmt->limit.unit));\n+\t\tnft_print_to_output_ctx(octx,\n+\t\t\t\t\t\"limit rate %s%\" PRIu64 \" %s/%s\",\n+\t\t\t\t\tinv ? \"over \" : \"\", rate, data_unit,\n+\t\t\t\t\tget_unit(stmt->limit.unit));\n \t\tif (stmt->limit.burst > 0) {\n \t\t\tuint64_t burst;\n \n \t\t\tdata_unit = get_rate(stmt->limit.burst, &burst);\n-\t\t\tprintf(\" burst %\"PRIu64\" %s\", burst, data_unit);\n+\t\t\tnft_print_to_output_ctx(octx, \" burst %\" PRIu64 \" %s\",\n+\t\t\t\t\t\tburst, data_unit);\n \t\t}\n \t\tbreak;\n \t}\n@@ -369,17 +378,17 @@ static void queue_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n \tconst char *delim = \" \";\n \n-\tprintf(\"queue\");\n+\tnft_print_to_output_ctx(octx, \"queue\");\n \tif (stmt->queue.queue != NULL) {\n-\t\tprintf(\" num \");\n+\t\tnft_print_to_output_ctx(octx, \" num \");\n \t\texpr_print(stmt->queue.queue, octx);\n \t}\n \tif (stmt->queue.flags & NFT_QUEUE_FLAG_BYPASS) {\n-\t\tprintf(\"%sbypass\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%sbypass\", delim);\n \t\tdelim = \",\";\n \t}\n \tif (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT)\n-\t\tprintf(\"%sfanout\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%sfanout\", delim);\n \n }\n \n@@ -401,12 +410,13 @@ static void quota_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n \tuint64_t bytes, used;\n \n \tdata_unit = get_rate(stmt->quota.bytes, &bytes);\n-\tprintf(\"quota %s%\"PRIu64\" %s\",\n-\t inv ? \"over \" : \"\", bytes, data_unit);\n+\tnft_print_to_output_ctx(octx, \"quota %s%\" PRIu64 \" %s\",\n+\t\t\t\tinv ? \"over \" : \"\", bytes, data_unit);\n \n \tif (!octx->stateless && stmt->quota.used) {\n \t\tdata_unit = get_rate(stmt->quota.used, &used);\n-\t\tprintf(\" used %\"PRIu64\" %s\", used, data_unit);\n+\t\tnft_print_to_output_ctx(octx, \" used %\" PRIu64 \" %s\", used,\n+\t\t\t\t\tdata_unit);\n \t}\n }\n \n@@ -427,15 +437,15 @@ struct stmt *quota_stmt_alloc(const struct location *loc)\n \n static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"reject\");\n+\tnft_print_to_output_ctx(octx, \"reject\");\n \tswitch (stmt->reject.type) {\n \tcase NFT_REJECT_TCP_RST:\n-\t\tprintf(\" with tcp reset\");\n+\t\tnft_print_to_output_ctx(octx, \" with tcp reset\");\n \t\tbreak;\n \tcase NFT_REJECT_ICMPX_UNREACH:\n \t\tif (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH)\n \t\t\tbreak;\n-\t\tprintf(\" with icmpx type \");\n+\t\tnft_print_to_output_ctx(octx, \" with icmpx type \");\n \t\texpr_print(stmt->reject.expr, octx);\n \t\tbreak;\n \tcase NFT_REJECT_ICMP_UNREACH:\n@@ -443,13 +453,13 @@ static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n \t\tcase NFPROTO_IPV4:\n \t\t\tif (stmt->reject.icmp_code == ICMP_PORT_UNREACH)\n \t\t\t\tbreak;\n-\t\t\tprintf(\" with icmp type \");\n+\t\t\tnft_print_to_output_ctx(octx, \" with icmp type \");\n \t\t\texpr_print(stmt->reject.expr, octx);\n \t\t\tbreak;\n \t\tcase NFPROTO_IPV6:\n \t\t\tif (stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT)\n \t\t\t\tbreak;\n-\t\t\tprintf(\" with icmpv6 type \");\n+\t\t\tnft_print_to_output_ctx(octx, \" with icmpv6 type \");\n \t\t\texpr_print(stmt->reject.expr, octx);\n \t\t\tbreak;\n \t\t}\n@@ -468,7 +478,7 @@ struct stmt *reject_stmt_alloc(const struct location *loc)\n \treturn stmt_alloc(loc, &reject_stmt_ops);\n }\n \n-static void print_nf_nat_flags(uint32_t flags)\n+static void print_nf_nat_flags(uint32_t flags, struct output_ctx *octx)\n {\n \tconst char *delim = \" \";\n \n@@ -476,17 +486,17 @@ static void print_nf_nat_flags(uint32_t flags)\n \t\treturn;\n \n \tif (flags & NF_NAT_RANGE_PROTO_RANDOM) {\n-\t\tprintf(\"%srandom\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%srandom\", delim);\n \t\tdelim = \",\";\n \t}\n \n \tif (flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {\n-\t\tprintf(\"%sfully-random\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%sfully-random\", delim);\n \t\tdelim = \",\";\n \t}\n \n \tif (flags & NF_NAT_RANGE_PERSISTENT)\n-\t\tprintf(\"%spersistent\", delim);\n+\t\tnft_print_to_output_ctx(octx, \"%spersistent\", delim);\n }\n \n static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n@@ -496,21 +506,21 @@ static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n \t\t[NFT_NAT_DNAT]\t= \"dnat\",\n \t};\n \n-\tprintf(\"%s to \", nat_types[stmt->nat.type]);\n+\tnft_print_to_output_ctx(octx, \"%s to \", nat_types[stmt->nat.type]);\n \tif (stmt->nat.addr) {\n \t\tif (stmt->nat.proto) {\n \t\t\tif (stmt->nat.addr->ops->type == EXPR_VALUE &&\n \t\t\t stmt->nat.addr->dtype->type == TYPE_IP6ADDR) {\n-\t\t\t\tprintf(\"[\");\n+\t\t\t\tnft_print_to_output_ctx(octx, \"[\");\n \t\t\t\texpr_print(stmt->nat.addr, octx);\n-\t\t\t\tprintf(\"]\");\n+\t\t\t\tnft_print_to_output_ctx(octx, \"]\");\n \t\t\t} else if (stmt->nat.addr->ops->type == EXPR_RANGE &&\n \t\t\t\t stmt->nat.addr->left->dtype->type == TYPE_IP6ADDR) {\n-\t\t\t\tprintf(\"[\");\n+\t\t\t\tnft_print_to_output_ctx(octx, \"[\");\n \t\t\t\texpr_print(stmt->nat.addr->left, octx);\n-\t\t\t\tprintf(\"]-[\");\n+\t\t\t\tnft_print_to_output_ctx(octx, \"]-[\");\n \t\t\t\texpr_print(stmt->nat.addr->right, octx);\n-\t\t\t\tprintf(\"]\");\n+\t\t\t\tnft_print_to_output_ctx(octx, \"]\");\n \t\t\t} else {\n \t\t\t\texpr_print(stmt->nat.addr, octx);\n \t\t\t}\n@@ -520,11 +530,11 @@ static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n \t}\n \n \tif (stmt->nat.proto) {\n-\t\tprintf(\":\");\n+\t\tnft_print_to_output_ctx(octx, \":\");\n \t\texpr_print(stmt->nat.proto, octx);\n \t}\n \n-\tprint_nf_nat_flags(stmt->nat.flags);\n+\tprint_nf_nat_flags(stmt->nat.flags, octx);\n }\n \n static void nat_stmt_destroy(struct stmt *stmt)\n@@ -547,14 +557,14 @@ struct stmt *nat_stmt_alloc(const struct location *loc)\n \n static void masq_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"masquerade\");\n+\tnft_print_to_output_ctx(octx, \"masquerade\");\n \n \tif (stmt->masq.proto) {\n-\t\tprintf(\" to :\");\n+\t\tnft_print_to_output_ctx(octx, \" to :\");\n \t\texpr_print(stmt->masq.proto, octx);\n \t}\n \n-\tprint_nf_nat_flags(stmt->masq.flags);\n+\tprint_nf_nat_flags(stmt->masq.flags, octx);\n }\n \n static void masq_stmt_destroy(struct stmt *stmt)\n@@ -576,14 +586,14 @@ struct stmt *masq_stmt_alloc(const struct location *loc)\n \n static void redir_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"redirect\");\n+\tnft_print_to_output_ctx(octx, \"redirect\");\n \n \tif (stmt->redir.proto) {\n-\t\tprintf(\" to :\");\n+\t\tnft_print_to_output_ctx(octx, \" to :\");\n \t\texpr_print(stmt->redir.proto, octx);\n \t}\n \n-\tprint_nf_nat_flags(stmt->redir.flags);\n+\tprint_nf_nat_flags(stmt->redir.flags, octx);\n }\n \n static void redir_stmt_destroy(struct stmt *stmt)\n@@ -610,9 +620,10 @@ static const char * const set_stmt_op_names[] = {\n \n static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"set %s \", set_stmt_op_names[stmt->set.op]);\n+\tnft_print_to_output_ctx(octx, \"set %s \",\n+\t\t\t\tset_stmt_op_names[stmt->set.op]);\n \texpr_print(stmt->set.key, octx);\n-\tprintf(\" \");\n+\tnft_print_to_output_ctx(octx, \" \");\n \texpr_print(stmt->set.set, octx);\n }\n \n@@ -636,13 +647,13 @@ struct stmt *set_stmt_alloc(const struct location *loc)\n \n static void dup_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"dup\");\n+\tnft_print_to_output_ctx(octx, \"dup\");\n \tif (stmt->dup.to != NULL) {\n-\t\tprintf(\" to \");\n+\t\tnft_print_to_output_ctx(octx, \" to \");\n \t\texpr_print(stmt->dup.to, octx);\n \n \t\tif (stmt->dup.dev != NULL) {\n-\t\t\tprintf(\" device \");\n+\t\t\tnft_print_to_output_ctx(octx, \" device \");\n \t\t\texpr_print(stmt->dup.dev, octx);\n \t\t}\n \t}\n@@ -668,7 +679,7 @@ struct stmt *dup_stmt_alloc(const struct location *loc)\n \n static void fwd_stmt_print(const struct stmt *stmt, struct output_ctx *octx)\n {\n-\tprintf(\"fwd to \");\n+\tnft_print_to_output_ctx(octx, \"fwd to \");\n \texpr_print(stmt->fwd.to, octx);\n }\n \n", "prefixes": [ "nft", "2/2" ] }