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