All of lore.kernel.org
 help / color / mirror / Atom feed
From: Justin Mitchell <jumitche@redhat.com>
To: Linux NFS Mailing list <linux-nfs@vger.kernel.org>
Subject: [PATCH 5/7] nfs-utils: tidy up output of conf_report
Date: Mon, 23 Apr 2018 16:27:51 +0100	[thread overview]
Message-ID: <1524497271.7418.8.camel@redhat.com> (raw)
In-Reply-To: <1524496788.7418.2.camel@redhat.com>

Previously unused conf_report() function is modified to output
an alphabetically sorted version of the current configuration
to the nominated FILE handle.

Signed-off-by: Justin Mitchell <jumitche@redhat.com>
---
 support/include/conffile.h |   3 +-
 support/nfs/conffile.c     | 237 ++++++++++++++++++++++++++++-----------------
 2 files changed, 151 insertions(+), 89 deletions(-)

diff --git a/support/include/conffile.h b/support/include/conffile.h
index 6baaf9a..bc2d61f 100644
--- a/support/include/conffile.h
+++ b/support/include/conffile.h
@@ -37,6 +37,7 @@
 #include <ctype.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <stdio.h>
 
 struct conf_list_node {
 	TAILQ_ENTRY(conf_list_node) link;
@@ -65,7 +66,7 @@ extern void     conf_cleanup(void);
 extern int      conf_match_num(const char *, const char *, int);
 extern int      conf_remove(int, const char *, const char *);
 extern int      conf_remove_section(int, const char *);
-extern void     conf_report(void);
+extern void     conf_report(FILE *);
 
 /*
  * Convert letter from upper case to lower case
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index 7ed5646..9148557 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -1055,117 +1055,178 @@ conf_end(int transaction, int commit)
  * Configuration is "stored in reverse order", so reverse it again.
  */
 struct dumper {
-	char *s, *v;
+	char *section;
+	char *arg;
+	char *tag;
+	char *value;
 	struct dumper *next;
 };
 
+static int dumper_section_compare(const struct dumper *nodea, const struct dumper *nodeb)
+{
+	int ret;
+
+	// missing node, shouldnt happen
+	if (!nodea || !nodeb) return -1;
+	// if only one has a section name, the blank one goes first
+	if (!nodea->section &&  nodeb->section) return -1;
+	if ( nodea->section && !nodeb->section) return  1;
+	// no section names at all
+	if (!nodea->section && !nodeb->section) return  0;
+	// two section names, do they match
+	ret = strcmp(nodea->section, nodeb->section);
+	// section names differ, that was easy
+	if (ret != 0) return ret;
+	// sections matched, but how about sub-sections, again blank goes first
+	if (!nodea->arg &&  nodeb->arg) return -1;
+	if ( nodea->arg && !nodeb->arg) return  1;
+
+	// both have sub-section args and they differ
+	if (nodea->arg && nodeb->arg && (ret=strcmp(nodea->arg, nodeb->arg))!=0)
+		return ret;
+
+	return 0;
+}
+
+static bool 
+should_escape(const char * text)
+{
+	int len;
+	if (!text) return false;
+	if (isspace(text[0])) return true;
+	len = strlen(text);
+	if (isspace(text[len-1])) return true;
+	return false;
+}
+
 static void
-conf_report_dump(struct dumper *node)
+conf_report_dump_text(struct dumper *head, FILE *ff)
 {
-	/* Recursive, cleanup when we're done.  */
-	if (node->next)
-		conf_report_dump(node->next);
-
-	if (node->v)
-		xlog(LOG_INFO, "%s=\t%s", node->s, node->v);
-	else if (node->s) {
-		xlog(LOG_INFO, "%s", node->s);
-		if (strlen(node->s) > 0)
-			free(node->s);
+	const struct dumper *node = head;
+	const struct dumper *last = NULL;
+
+	for (node=head; node!=NULL; node=node->next) {
+		if (dumper_section_compare(last, node)!=0) {
+			if (node != head) fprintf(ff, "\n");
+			if (node->arg)
+				fprintf(ff, "[%s \"%s\"]\n", node->section, node->arg);
+			else
+				fprintf(ff, "[%s]\n", node->section);
+		}
+		fprintf(ff, " %s", node->tag);
+		if (node->value) {
+			if (should_escape(node->value))
+				fprintf(ff, " = \"%s\"", node->value);
+			else
+				fprintf(ff, " = %s", node->value);
+		}
+		fprintf(ff, "\n");
+
+		last = node;
 	}
+}
 
-	free (node);
+/* compare two dumper nodes for sorting */
+static int dumper_compare(const void * a, const void * b)
+{
+	const struct dumper *nodea = *(struct dumper **)a;
+	const struct dumper *nodeb = *(struct dumper **)b;
+	int ret;
+
+	// missing node, shouldnt happen
+	if (!nodea || !nodeb) return -1;
+
+	ret = dumper_section_compare(nodea, nodeb);
+	if (ret != 0) return ret;
+
+	// sub-sections match (both blank, or both same)
+	// so we compare the tag names
+	
+	// blank tags shouldnt happen, but paranoia
+	if (!nodea->tag &&  nodeb->tag) return -1;
+	if ( nodea->tag && !nodeb->tag) return  1;
+	if (!nodea->tag && !nodeb->tag) return  0;
+
+	// last test, compare the tags directly
+	ret = strcmp(nodea->tag, nodeb->tag);
+	return ret;
 }
 
-void
-conf_report (void)
+static struct dumper *conf_report_sort(struct dumper *start)
 {
-	struct conf_binding *cb, *last = 0;
-	unsigned int i, len, diff_arg = 0;
-	char *current_section = (char *)0;
-	char *current_arg = (char *)0;
-	struct dumper *dumper, *dnode;
+	struct dumper **list;
+	struct dumper *node;
+	unsigned int count = 0;
 
-	dumper = dnode = (struct dumper *)calloc(1, sizeof *dumper);
-	if (!dumper)
-		goto mem_fail;
+	// how long is this list
+	for (node=start; node!=NULL; node=node->next) count++;
+
+	// no need to sort a list of 1 or less
+	if (count < 2) return start;
+
+	// make an array of all the nodes
+	list = calloc(count, sizeof(struct dumper *));
+	if (!list) goto mem_err;
+
+	unsigned int i=0;
+	for (node=start; node!=NULL; node=node->next) {
+		list[i++] = node;
+	}
+
+	// sort the array
+	qsort(list, count, sizeof(struct dumper *), dumper_compare);
+
+	// turn sorted array back into a linked list
+	for (i=0; i<count-1; i++) {
+		list[i]->next = list[i+1];
+	}
+	list[count-1]->next = NULL;
+
+	// remember the new head of list and cleanup
+	node = list[0];
+	free(list);
+
+	// return the new head of list
+	return node;
+
+mem_err:
+	free(list);
+	return NULL;
+}
+
+void
+conf_report (FILE *outfile)
+{
+	struct conf_binding *cb = NULL;
+	unsigned int i;
+	struct dumper *dumper = NULL, *dnode = NULL;
 
 	xlog(LOG_INFO, "conf_report: dumping running configuration");
 
-	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
+	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) {
 		for (cb = LIST_FIRST(&conf_bindings[i]); cb; cb = LIST_NEXT(cb, link)) {
-			if (!cb->is_default) {
-				/* Make sure the Section arugment is the same */
-				if (current_arg && current_section && cb->arg) {
-					if (strcmp(cb->section, current_section) == 0 &&
-						strcmp(cb->arg, current_arg) != 0)
-					diff_arg = 1;
-				}
-				/* Dump this entry.  */
-				if (!current_section || strcmp(cb->section, current_section) 
-							|| diff_arg) {
-					if (current_section || diff_arg) {
-						len = strlen (current_section) + 3;
-						if (current_arg)
-							len += strlen(current_arg) + 3;
-						dnode->s = malloc(len);
-						if (!dnode->s)
-							goto mem_fail;
-
-						if (current_arg)
-							snprintf(dnode->s, len, "[%s \"%s\"]", 
-								current_section, current_arg);
-						else
-							snprintf(dnode->s, len, "[%s]", current_section);
-
-						dnode->next = 
-							(struct dumper *)calloc(1, sizeof (struct dumper));
-						dnode = dnode->next;
-						if (!dnode)
-							goto mem_fail;
-
-						dnode->s = "";
-						dnode->next = 
-							(struct dumper *)calloc(1, sizeof (struct dumper));
-						dnode = dnode->next;
-						if (!dnode)
-						goto mem_fail;
-					}
-					current_section = cb->section;
-					current_arg = cb->arg;
-					diff_arg = 0;
-				}
-				dnode->s = cb->tag;
-				dnode->v = cb->value;
-				dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
-				dnode = dnode->next;
-				if (!dnode)
-					goto mem_fail;
-				last = cb;
+			struct dumper *newnode = calloc(1, sizeof (struct dumper));
+			if (!newnode) goto mem_fail;
+
+			newnode->next = dumper;
+			dumper = newnode;
+
+			newnode->section = cb->section;
+			newnode->arg = cb->arg;
+			newnode->tag = cb->tag;
+			newnode->value = cb->value;
 		}
 	}
 
-	if (last) {
-		len = strlen(last->section) + 3;
-		if (last->arg)
-			len += strlen(last->arg) + 3;
-		dnode->s = malloc(len);
-		if (!dnode->s)
-			goto mem_fail;
-		if (last->arg)
-			snprintf(dnode->s, len, "[%s \"%s\"]", last->section, last->arg);
-		else
-			snprintf(dnode->s, len, "[%s]", last->section);
-	}
-	conf_report_dump(dumper);
-	return;
+	dumper = conf_report_sort(dumper);
+	conf_report_dump_text(dumper, outfile);
+	goto cleanup;
 
 mem_fail:
 	xlog_warn("conf_report: malloc/calloc failed");
+cleanup:
 	while ((dnode = dumper) != 0) {
 		dumper = dumper->next;
-		if (dnode->s)
-			free(dnode->s);
 		free(dnode);
 	}
 	return;
-- 
1.8.3.1




  parent reply	other threads:[~2018-04-23 15:27 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-04-23 15:19 [PATCH 0/7] nfs-utils: nfsconf cli tool and code tests Justin Mitchell
2018-04-23 15:24 ` [PATCH 1/7] nfs-utils: Fix minor memory leaks Justin Mitchell
2018-04-23 15:25 ` [PATCH 2/7] nfs-utils: Make config includes relative to current config Justin Mitchell
2018-05-02 18:21   ` Steve Dickson
2018-05-03 10:11     ` Justin Mitchell
2018-04-23 15:26 ` [PATCH 3/7] nfs-utils: Use config file name in error messages Justin Mitchell
2018-04-23 15:26 ` [PATCH 4/7] nfs-utils: Indicate if config file was missing Justin Mitchell
2018-04-23 15:27 ` Justin Mitchell [this message]
2018-05-02 18:24   ` [PATCH 5/7] nfs-utils: tidy up output of conf_report Steve Dickson
2018-04-23 15:34 ` [PATCH 6/7] nfs-utils: Add nfsconftool cli Justin Mitchell
2018-05-02 18:25   ` Steve Dickson
2018-04-23 15:35 ` [PATCH 7/7] nfs-utils: use nfsconftool cli to test library function Justin Mitchell

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=1524497271.7418.8.camel@redhat.com \
    --to=jumitche@redhat.com \
    --cc=linux-nfs@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.