MINOR: stats: introduce stats field ctx

Add a new value in stats ctx: field.
Implement field support in line dumping parent functions
stats_print_proxy_field_json() and stats_dump_proxy_to_buffer().

This will allow child dumping functions to support partial line dumping
when needed. ie: when dumping buffer is exhausted: do a partial send and
wait for a new buffer to finish the dump. Thanks to field ctx, the function
can start dumping where it left off on previous (unterminated) invokation.
diff --git a/include/haproxy/stats-t.h b/include/haproxy/stats-t.h
index 4dd65f4..9cc1d33 100644
--- a/include/haproxy/stats-t.h
+++ b/include/haproxy/stats-t.h
@@ -540,6 +540,7 @@
 	uint32_t domain;        /* set the stats to used, for now only proxy stats are supported */
 	int scope_str;		/* limit scope to a frontend/backend substring */
 	int scope_len;		/* length of the string above in the buffer */
+	int field;              /* current field iterator when stat line is dumped through returning function */
 	int px_st;		/* STAT_PX_ST* */
 	unsigned int flags;	/* STAT_* from stats-t.h */
 	int iid, type, sid;	/* proxy id, type and service id if bounding of stats is enabled */
diff --git a/src/stats.c b/src/stats.c
index 0d9e4d7..1a9f263 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -3045,8 +3045,11 @@
 	struct channel *rep = sc_ic(sc);
 	struct server *sv, *svs;	/* server and server-state, server-state=server or server->track */
 	struct listener *l;
+	int current_field;
 
 	chunk_reset(&trash);
+more:
+	current_field = ctx->field;
 
 	switch (ctx->px_st) {
 	case STAT_PX_ST_INIT:
@@ -3108,8 +3111,11 @@
 		if (stats_dump_fe_stats(sc, px)) {
 			if (!stats_putchk(rep, htx, &trash))
 				goto full;
+			if (ctx->field)
+				goto more;
 		}
 
+		current_field = 0;
 		ctx->obj2 = px->conf.listeners.n;
 		ctx->px_st = STAT_PX_ST_LI;
 		__fallthrough;
@@ -3142,9 +3148,12 @@
 			if (stats_dump_li_stats(sc, px, l)) {
 				if (!stats_putchk(rep, htx, &trash))
 					goto full;
+				if (ctx->field)
+					goto more;
 			}
 		}
 
+		current_field = 0;
 		ctx->obj2 = px->srv; /* may be NULL */
 		ctx->px_st = STAT_PX_ST_SV;
 		__fallthrough;
@@ -3215,8 +3224,11 @@
 		if (stats_dump_be_stats(sc, px)) {
 			if (!stats_putchk(rep, htx, &trash))
 				goto full;
+			if (ctx->field)
+				goto more;
 		}
 
+		current_field = 0;
 		ctx->px_st = STAT_PX_ST_END;
 		__fallthrough;
 
@@ -3240,6 +3252,8 @@
 
   full:
 	sc_need_room(sc);
+	/* restore previous field */
+	ctx->field = current_field;
 	return 0;
 }
 
@@ -3729,6 +3743,7 @@
 
 		ctx->obj1 = px->next;
 		ctx->px_st = STAT_PX_ST_INIT;
+		ctx->field = 0;
 	}
 
 	return 1;
@@ -3791,6 +3806,7 @@
 			ctx->obj1 = proxies_list;
 
 		ctx->px_st = STAT_PX_ST_INIT;
+		ctx->field = 0;
 		ctx->state = STAT_STATE_LIST;
 		__fallthrough;
 
@@ -4579,22 +4595,33 @@
 {
 	struct appctx *appctx = __sc_appctx(sc);
 	struct show_stat_ctx *ctx = appctx->svcctx;
+	int ret;
+	int current_field;
 
 	if (!stats_fill_info(info, INF_TOTAL_FIELDS, ctx->flags))
 		return 0;
 
 	chunk_reset(&trash);
+more:
+	current_field = ctx->field;
 
 	if (ctx->flags & STAT_FMT_TYPED)
-		stats_dump_typed_info_fields(&trash, info, ctx);
+		ret = stats_dump_typed_info_fields(&trash, info, ctx);
 	else if (ctx->flags & STAT_FMT_JSON)
-		stats_dump_json_info_fields(&trash, info, ctx);
+		ret = stats_dump_json_info_fields(&trash, info, ctx);
 	else
-		stats_dump_info_fields(&trash, info, ctx);
+		ret = stats_dump_info_fields(&trash, info, ctx);
 
-	if (applet_putchk(appctx, &trash) == -1)
+	if (applet_putchk(appctx, &trash) == -1) {
+		/* restore previous field */
+		ctx->field = current_field;
 		return 0;
-
+	}
+	if (ret && ctx->field) {
+		/* partial dump */
+		goto more;
+	}
+	ctx->field = 0;
 	return 1;
 }
 
@@ -4947,6 +4974,7 @@
 	ctx->scope_str = 0;
 	ctx->scope_len = 0;
 	ctx->flags = 0;
+	ctx->field = 0; /* explicit default value */
 
 	while (*args[arg]) {
 		if (strcmp(args[arg], "typed") == 0)