From 369437f159c04cf83a94dde634c4fc9a57a42335 Mon Sep 17 00:00:00 2001
From: Danny Auble <da@schedmd.com>
Date: Tue, 26 Jun 2012 14:54:48 -0700
Subject: [PATCH] Added logic for a Natural Sort (via code from Martin Pool
 <mbp sourcefrog net>) so we can get a correct alphanumeric sort of hostnames.

---
 src/common/Makefile.am      |   7 +-
 src/common/Makefile.in      |  60 ++++++------
 src/common/strnatcmp.c      | 178 ++++++++++++++++++++++++++++++++++++
 src/common/strnatcmp.h      |  31 +++++++
 src/slurmctld/read_config.c |   5 +-
 5 files changed, 247 insertions(+), 34 deletions(-)
 create mode 100644 src/common/strnatcmp.c
 create mode 100644 src/common/strnatcmp.h

diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 8df61a75d14..0e400b7be9a 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -32,14 +32,15 @@ noinst_LTLIBRARIES = 			\
 	libspank.la
 
 libcommon_la_SOURCES = 			\
- 	xcgroup_read_config.c xcgroup_read_config.h		\
- 	xcgroup.c xcgroup.h 					\
- 	xcpuinfo.c xcpuinfo.h 					\
+	xcgroup_read_config.c xcgroup_read_config.h		\
+	xcgroup.c xcgroup.h 					\
+	xcpuinfo.c xcpuinfo.h 					\
 	assoc_mgr.c assoc_mgr.h 	\
 	xmalloc.c xmalloc.h 		\
 	xassert.c xassert.h		\
 	xstring.c xstring.h		\
 	xsignal.c xsignal.h		\
+	strnatcmp.c strnatcmp.h		\
 	forward.c forward.h     	\
 	strlcpy.c strlcpy.h		\
 	list.c list.h 			\
diff --git a/src/common/Makefile.in b/src/common/Makefile.in
index e61fdec02ea..f8fc223791f 100644
--- a/src/common/Makefile.in
+++ b/src/common/Makefile.in
@@ -102,14 +102,14 @@ am__libcommon_la_SOURCES_DIST = xcgroup_read_config.c \
 	xcgroup_read_config.h xcgroup.c xcgroup.h xcpuinfo.c \
 	xcpuinfo.h assoc_mgr.c assoc_mgr.h xmalloc.c xmalloc.h \
 	xassert.c xassert.h xstring.c xstring.h xsignal.c xsignal.h \
-	forward.c forward.h strlcpy.c strlcpy.h list.c list.h net.c \
-	net.h log.c log.h cbuf.c cbuf.h safeopen.c safeopen.h \
-	bitstring.c bitstring.h mpi.c mpi.h pack.c pack.h \
-	parse_config.c parse_config.h parse_spec.c parse_spec.h \
-	plugin.c plugin.h plugrack.c plugrack.h print_fields.c \
-	print_fields.h read_config.c read_config.h node_select.c \
-	node_select.h env.c env.h fd.c fd.h slurm_cred.h slurm_cred.c \
-	slurm_errno.c slurm_priority.c slurm_priority.h \
+	strnatcmp.c strnatcmp.h forward.c forward.h strlcpy.c \
+	strlcpy.h list.c list.h net.c net.h log.c log.h cbuf.c cbuf.h \
+	safeopen.c safeopen.h bitstring.c bitstring.h mpi.c mpi.h \
+	pack.c pack.h parse_config.c parse_config.h parse_spec.c \
+	parse_spec.h plugin.c plugin.h plugrack.c plugrack.h \
+	print_fields.c print_fields.h read_config.c read_config.h \
+	node_select.c node_select.h env.c env.h fd.c fd.h slurm_cred.h \
+	slurm_cred.c slurm_errno.c slurm_priority.c slurm_priority.h \
 	slurm_protocol_api.c slurm_protocol_api.h \
 	slurm_protocol_pack.c slurm_protocol_pack.h \
 	slurm_protocol_util.c slurm_protocol_util.h \
@@ -139,24 +139,24 @@ am__libcommon_la_SOURCES_DIST = xcgroup_read_config.c \
 @HAVE_UNSETENV_FALSE@am__objects_1 = unsetenv.lo
 am_libcommon_la_OBJECTS = xcgroup_read_config.lo xcgroup.lo \
 	xcpuinfo.lo assoc_mgr.lo xmalloc.lo xassert.lo xstring.lo \
-	xsignal.lo forward.lo strlcpy.lo list.lo net.lo log.lo cbuf.lo \
-	safeopen.lo bitstring.lo mpi.lo pack.lo parse_config.lo \
-	parse_spec.lo plugin.lo plugrack.lo print_fields.lo \
-	read_config.lo node_select.lo env.lo fd.lo slurm_cred.lo \
-	slurm_errno.lo slurm_priority.lo slurm_protocol_api.lo \
-	slurm_protocol_pack.lo slurm_protocol_util.lo \
-	slurm_protocol_socket_implementation.lo slurm_protocol_defs.lo \
-	slurm_rlimits_info.lo slurmdb_defs.lo slurmdb_pack.lo \
-	slurmdbd_defs.lo working_cluster.lo uid.lo util-net.lo \
-	slurm_auth.lo jobacct_common.lo slurm_accounting_storage.lo \
-	slurm_jobacct_gather.lo slurm_jobcomp.lo slurm_topology.lo \
-	switch.lo arg_desc.lo malloc.lo getopt.lo getopt1.lo \
-	$(am__objects_1) slurm_selecttype_info.lo \
-	slurm_resource_info.lo hostlist.lo slurm_step_layout.lo \
-	checkpoint.lo job_resources.lo parse_time.lo job_options.lo \
-	global_defaults.lo timers.lo stepd_api.lo \
-	write_labelled_message.lo proc_args.lo slurm_strcasestr.lo \
-	node_conf.lo gres.lo
+	xsignal.lo strnatcmp.lo forward.lo strlcpy.lo list.lo net.lo \
+	log.lo cbuf.lo safeopen.lo bitstring.lo mpi.lo pack.lo \
+	parse_config.lo parse_spec.lo plugin.lo plugrack.lo \
+	print_fields.lo read_config.lo node_select.lo env.lo fd.lo \
+	slurm_cred.lo slurm_errno.lo slurm_priority.lo \
+	slurm_protocol_api.lo slurm_protocol_pack.lo \
+	slurm_protocol_util.lo slurm_protocol_socket_implementation.lo \
+	slurm_protocol_defs.lo slurm_rlimits_info.lo slurmdb_defs.lo \
+	slurmdb_pack.lo slurmdbd_defs.lo working_cluster.lo uid.lo \
+	util-net.lo slurm_auth.lo jobacct_common.lo \
+	slurm_accounting_storage.lo slurm_jobacct_gather.lo \
+	slurm_jobcomp.lo slurm_topology.lo switch.lo arg_desc.lo \
+	malloc.lo getopt.lo getopt1.lo $(am__objects_1) \
+	slurm_selecttype_info.lo slurm_resource_info.lo hostlist.lo \
+	slurm_step_layout.lo checkpoint.lo job_resources.lo \
+	parse_time.lo job_options.lo global_defaults.lo timers.lo \
+	stepd_api.lo write_labelled_message.lo proc_args.lo \
+	slurm_strcasestr.lo node_conf.lo gres.lo
 am__EXTRA_libcommon_la_SOURCES_DIST = unsetenv.c unsetenv.h
 libcommon_la_OBJECTS = $(am_libcommon_la_OBJECTS)
 libcommon_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
@@ -422,14 +422,15 @@ noinst_LTLIBRARIES = \
 	libspank.la
 
 libcommon_la_SOURCES = \
- 	xcgroup_read_config.c xcgroup_read_config.h		\
- 	xcgroup.c xcgroup.h 					\
- 	xcpuinfo.c xcpuinfo.h 					\
+	xcgroup_read_config.c xcgroup_read_config.h		\
+	xcgroup.c xcgroup.h 					\
+	xcpuinfo.c xcpuinfo.h 					\
 	assoc_mgr.c assoc_mgr.h 	\
 	xmalloc.c xmalloc.h 		\
 	xassert.c xassert.h		\
 	xstring.c xstring.h		\
 	xsignal.c xsignal.h		\
+	strnatcmp.c strnatcmp.h		\
 	forward.c forward.h     	\
 	strlcpy.c strlcpy.h		\
 	list.c list.h 			\
@@ -664,6 +665,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slurmdbd_defs.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stepd_api.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strnatcmp.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/switch.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timers.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uid.Plo@am__quote@
diff --git a/src/common/strnatcmp.c b/src/common/strnatcmp.c
new file mode 100644
index 00000000000..c91d87fc00c
--- /dev/null
+++ b/src/common/strnatcmp.c
@@ -0,0 +1,178 @@
+/* -*- mode: c; c-file-style: "k&r" -*-
+
+   strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
+   Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
+
+   This software is provided 'as-is', without any express or implied
+   warranty.  In no event will the authors be held liable for any damages
+   arising from the use of this software.
+
+   Permission is granted to anyone to use this software for any purpose,
+   including commercial applications, and to alter it and redistribute it
+   freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+   2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+   3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+/* partial change history:
+ *
+ * 2004-10-10 mbp: Lift out character type dependencies into macros.
+ *
+ * Eric Sosman pointed out that ctype functions take a parameter whose
+ * value must be that of an unsigned int, even on platforms that have
+ * negative chars in their default char type.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "strnatcmp.h"
+
+
+/* These are defined as macros to make it easier to adapt this code to
+ * different characters types or comparison functions. */
+static inline int
+nat_isdigit(nat_char a)
+{
+	return isdigit((unsigned char) a);
+}
+
+
+static inline int
+nat_isspace(nat_char a)
+{
+	return isspace((unsigned char) a);
+}
+
+
+static inline nat_char
+nat_toupper(nat_char a)
+{
+	return toupper((unsigned char) a);
+}
+
+
+
+static int
+compare_right(nat_char const *a, nat_char const *b)
+{
+	int bias = 0;
+
+	/* The longest run of digits wins.  That aside, the greatest
+	   value wins, but we can't know that it will until we've scanned
+	   both numbers to know that they have the same magnitude, so we
+	   remember it in BIAS. */
+	for (;; a++, b++) {
+		if (!nat_isdigit(*a)  &&  !nat_isdigit(*b))
+			return bias;
+		else if (!nat_isdigit(*a))
+			return -1;
+		else if (!nat_isdigit(*b))
+			return +1;
+		else if (*a < *b) {
+			if (!bias)
+				bias = -1;
+		} else if (*a > *b) {
+			if (!bias)
+				bias = +1;
+		} else if (!*a  &&  !*b)
+			return bias;
+	}
+
+	return 0;
+}
+
+
+static int
+compare_left(nat_char const *a, nat_char const *b)
+{
+	/* Compare two left-aligned numbers: the first to have a
+	   different value wins. */
+	for (;; a++, b++) {
+		if (!nat_isdigit(*a)  &&  !nat_isdigit(*b))
+			return 0;
+		else if (!nat_isdigit(*a))
+			return -1;
+		else if (!nat_isdigit(*b))
+			return +1;
+		else if (*a < *b)
+			return -1;
+		else if (*a > *b)
+			return +1;
+	}
+
+	return 0;
+}
+
+
+static int strnatcmp0(nat_char const *a, nat_char const *b, int fold_case)
+{
+	int ai, bi;
+	nat_char ca, cb;
+	int fractional, result;
+
+	assert(a && b);
+	ai = bi = 0;
+	while (1) {
+		ca = a[ai]; cb = b[bi];
+
+		/* skip over leading spaces or zeros */
+		while (nat_isspace(ca))
+			ca = a[++ai];
+
+		while (nat_isspace(cb))
+			cb = b[++bi];
+
+		/* process run of digits */
+		if (nat_isdigit(ca)  &&  nat_isdigit(cb)) {
+			fractional = (ca == '0' || cb == '0');
+
+			if (fractional) {
+				if ((result = compare_left(a+ai, b+bi)) != 0)
+					return result;
+			} else {
+				if ((result = compare_right(a+ai, b+bi)) != 0)
+					return result;
+			}
+		}
+
+		if (!ca && !cb) {
+			/* The strings compare the same.  Perhaps the caller
+			   will want to call strcmp to break the tie. */
+			return 0;
+		}
+
+		if (fold_case) {
+			ca = nat_toupper(ca);
+			cb = nat_toupper(cb);
+		}
+
+		if (ca < cb)
+			return -1;
+		else if (ca > cb)
+			return +1;
+
+		++ai; ++bi;
+	}
+}
+
+
+
+int strnatcmp(nat_char const *a, nat_char const *b) {
+	return strnatcmp0(a, b, 0);
+}
+
+
+/* Compare, recognizing numeric string and ignoring case. */
+int strnatcasecmp(nat_char const *a, nat_char const *b) {
+	return strnatcmp0(a, b, 1);
+}
diff --git a/src/common/strnatcmp.h b/src/common/strnatcmp.h
new file mode 100644
index 00000000000..51a3c4e8cb6
--- /dev/null
+++ b/src/common/strnatcmp.h
@@ -0,0 +1,31 @@
+/* -*- mode: c; c-file-style: "k&r" -*-
+
+  strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
+  Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+/* CUSTOMIZATION SECTION
+ *
+ * You can change this typedef, but must then also change the inline
+ * functions in strnatcmp.c */
+typedef char nat_char;
+
+int strnatcmp(nat_char const *a, nat_char const *b);
+int strnatcasecmp(nat_char const *a, nat_char const *b);
diff --git a/src/slurmctld/read_config.c b/src/slurmctld/read_config.c
index 5c99d8e41c0..1301b03f5ee 100644
--- a/src/slurmctld/read_config.c
+++ b/src/slurmctld/read_config.c
@@ -71,6 +71,7 @@
 #include "src/common/slurm_rlimits_info.h"
 #include "src/common/switch.h"
 #include "src/common/xstring.h"
+#include "src/common/strnatcmp.h"
 
 #include "src/slurmctld/acct_policy.h"
 #include "src/slurmctld/front_end.h"
@@ -132,8 +133,8 @@ static void _reorder_nodes_by_name(void)
 	for (i = 0; i < node_record_count; i++) {
 		min_inx = i;
 		for (j = i + 1; j < node_record_count; j++) {
-			if (strcmp(node_record_table_ptr[j].name,
-				   node_record_table_ptr[min_inx].name) < 0)
+			if (strnatcmp(node_record_table_ptr[j].name,
+				      node_record_table_ptr[min_inx].name) < 0)
 				min_inx = j;
 		}
 
-- 
GitLab