text: /* Jody Bruchon's sorting code library
   *
   * Copyright (C) 2014-2020 by Jody Bruchon <jody@jodybruchon.com>
   * Released under The MIT License
   */
  
  #include <stdlib.h>
  #include "jody_sort.h"
  
  #define IS_NUM(a) (((a >= '0') && (a <= '9')) ? 1 : 0)
  #define IS_LOWER(a) (((a >= 'a') && (a <= 'z')) ? 1 : 0)
  
  extern int numeric_sort(const char * restrict c1,
                  const char * restrict c2, int sort_direction)
  {
    int len1 = 0, len2 = 0;
    int precompare = 0;
  
    if (c1 == NULL || c2 == NULL) return -99;
  
    /* Numerically correct sort */
    while (*c1 != '\0' && *c2 != '\0') {
      /* Reset string length counters */
      len1 = 0; len2 = 0;
  
      /* Skip all sequences of zeroes */
      while (*c1 == '0') {
        len1++;
        c1++;
      }
      while (*c2 == '0') {
        len2++;
        c2++;
      }
  
      /* If both chars are numeric, do a numeric comparison */
      if (IS_NUM(*c1) && IS_NUM(*c2)) {
        precompare = 0;
  
        /* Scan numbers and get preliminary results */
        while (IS_NUM(*c1) && IS_NUM(*c2)) {
          if (*c1 < *c2) precompare = -sort_direction;
          if (*c1 > *c2) precompare = sort_direction;
          len1++; len2++;
          c1++; c2++;
  
          /* Skip remaining digit pairs after any
           * difference is found */
          if (precompare != 0) {
            while (IS_NUM(*c1) && IS_NUM(*c2)) {
              len1++; len2++;
              c1++; c2++;
            }
            break;
          }
        }
  
        /* One numeric and one non-numeric means the
         * numeric one is larger and sorts later */
        if (IS_NUM(*c1) ^ IS_NUM(*c2)) {
          if (IS_NUM(*c1)) return sort_direction;
          else return -sort_direction;
        }
  
        /* If the last test fell through, numbers are
         * of equal length. Use the precompare result
         * as the result for this number comparison. */
        if (precompare != 0) return precompare;
      }
  
      /* Do normal comparison */
      if (*c1 == *c2 && *c1 != '\0' && *c2 != '\0') {
        c1++; c2++;
        len1++; len2++;
      /* Put symbols and spaces after everything else */
      } else if (*c2 < '.' && *c1 >= '.') return -sort_direction;
      else if (*c1 < '.' && *c2 >= '.') return sort_direction;
      /* Normal strcasecmp() style compare */
      else {
        char s1 = *c1, s2 = *c2;
        /* Convert lowercase into uppercase */
        if (IS_LOWER(s1)) s1 = (char)(s1 - 32);
        if (IS_LOWER(s2)) s2 = (char)(s2 - 32);
        if (s1 > s2) return sort_direction;
        else return -sort_direction;
      }
    }
  
    /* Longer strings generally sort later */
    if (len1 < len2) return -sort_direction;
    if (len1 > len2) return sort_direction;
  
    /* Normal comparison - FIXME? length check should already handle these */
    if (*c1 == '\0' && *c2 != '\0') return -sort_direction;
    if (*c1 != '\0' && *c2 == '\0') return sort_direction;
  
    /* Fall through: the strings are equal */
    return 0;
  }
  