1251875Speter/* -*- mode: c; c-file-style: "k&r" -*-
2251875Speter
3251875Speter  strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
4251875Speter  Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
5251875Speter
6251875Speter  This software is provided 'as-is', without any express or implied
7251875Speter  warranty.  In no event will the authors be held liable for any damages
8251875Speter  arising from the use of this software.
9251875Speter
10251875Speter  Permission is granted to anyone to use this software for any purpose,
11251875Speter  including commercial applications, and to alter it and redistribute it
12251875Speter  freely, subject to the following restrictions:
13251875Speter
14251875Speter  1. The origin of this software must not be misrepresented; you must not
15251875Speter     claim that you wrote the original software. If you use this software
16251875Speter     in a product, an acknowledgment in the product documentation would be
17251875Speter     appreciated but is not required.
18251875Speter  2. Altered source versions must be plainly marked as such, and must not be
19251875Speter     misrepresented as being the original software.
20251875Speter  3. This notice may not be removed or altered from any source distribution.
21251875Speter*/
22251875Speter
23251875Speter#include <ctype.h>
24251875Speter#include <string.h>
25251875Speter#include "apr_strings.h"
26251875Speter#include "apr_lib.h"            /* for apr_is*() */
27251875Speter
28251875Speter#if defined(__GNUC__)
29251875Speter#  define UNUSED __attribute__((__unused__))
30251875Speter#else
31251875Speter#  define UNUSED
32251875Speter#endif
33251875Speter
34251875Speter/* based on "strnatcmp.c,v 1.6 2000/04/20 07:30:11 mbp Exp $" */
35251875Speter
36251875Speterstatic int
37251875Spetercompare_right(char const *a, char const *b)
38251875Speter{
39251875Speter     int bias = 0;
40251875Speter
41251875Speter     /* The longest run of digits wins.  That aside, the greatest
42251875Speter	value wins, but we can't know that it will until we've scanned
43251875Speter	both numbers to know that they have the same magnitude, so we
44251875Speter	remember it in BIAS. */
45251875Speter     for (;; a++, b++) {
46251875Speter	  if (!apr_isdigit(*a)  &&  !apr_isdigit(*b))
47251875Speter	       break;
48251875Speter	  else if (!apr_isdigit(*a))
49251875Speter	       return -1;
50251875Speter	  else if (!apr_isdigit(*b))
51251875Speter	       return +1;
52251875Speter	  else if (*a < *b) {
53251875Speter	       if (!bias)
54251875Speter		    bias = -1;
55251875Speter	  } else if (*a > *b) {
56251875Speter	       if (!bias)
57251875Speter		    bias = +1;
58251875Speter	  } else if (!*a  &&  !*b)
59251875Speter	       break;
60251875Speter     }
61251875Speter
62251875Speter     return bias;
63251875Speter}
64251875Speter
65251875Speter
66251875Speterstatic int
67251875Spetercompare_left(char const *a, char const *b)
68251875Speter{
69251875Speter     /* Compare two left-aligned numbers: the first to have a
70251875Speter        different value wins. */
71251875Speter     for (;; a++, b++) {
72251875Speter	  if (!apr_isdigit(*a)  &&  !apr_isdigit(*b))
73251875Speter	       break;
74251875Speter	  else if (!apr_isdigit(*a))
75251875Speter	       return -1;
76251875Speter	  else if (!apr_isdigit(*b))
77251875Speter	       return +1;
78251875Speter	  else if (*a < *b)
79251875Speter	       return -1;
80251875Speter	  else if (*a > *b)
81251875Speter	       return +1;
82251875Speter     }
83251875Speter
84251875Speter     return 0;
85251875Speter}
86251875Speter
87251875Speter
88251875Speterstatic int strnatcmp0(char const *a, char const *b, int fold_case)
89251875Speter{
90251875Speter     int ai, bi;
91251875Speter     char ca, cb;
92251875Speter     int fractional, result;
93251875Speter     ai = bi = 0;
94251875Speter     while (1) {
95251875Speter	  ca = a[ai]; cb = b[bi];
96251875Speter
97251875Speter	  /* skip over leading spaces or zeros */
98251875Speter	  while (apr_isspace(ca))
99251875Speter	       ca = a[++ai];
100251875Speter
101251875Speter	  while (apr_isspace(cb))
102251875Speter	       cb = b[++bi];
103251875Speter
104251875Speter	  /* process run of digits */
105251875Speter	  if (apr_isdigit(ca)  &&  apr_isdigit(cb)) {
106251875Speter	       fractional = (ca == '0' || cb == '0');
107251875Speter
108251875Speter	       if (fractional) {
109251875Speter		    if ((result = compare_left(a+ai, b+bi)) != 0)
110251875Speter			 return result;
111251875Speter	       } else {
112251875Speter		    if ((result = compare_right(a+ai, b+bi)) != 0)
113251875Speter			 return result;
114251875Speter	       }
115251875Speter	  }
116251875Speter
117251875Speter	  if (!ca && !cb) {
118251875Speter	       /* The strings compare the same.  Perhaps the caller
119251875Speter                  will want to call strcmp to break the tie. */
120251875Speter	       return 0;
121251875Speter	  }
122251875Speter
123251875Speter	  if (fold_case) {
124251875Speter	       ca = apr_toupper(ca);
125251875Speter	       cb = apr_toupper(cb);
126251875Speter	  }
127251875Speter
128251875Speter	  if (ca < cb)
129251875Speter	       return -1;
130251875Speter	  else if (ca > cb)
131251875Speter	       return +1;
132251875Speter
133251875Speter	  ++ai; ++bi;
134251875Speter     }
135251875Speter}
136251875Speter
137251875Speter
138251875Speter
139251875SpeterAPR_DECLARE(int) apr_strnatcmp(char const *a, char const *b)
140251875Speter{
141251875Speter     return strnatcmp0(a, b, 0);
142251875Speter}
143251875Speter
144251875Speter
145251875Speter/* Compare, recognizing numeric string and ignoring case. */
146251875SpeterAPR_DECLARE(int) apr_strnatcasecmp(char const *a, char const *b)
147251875Speter{
148251875Speter     return strnatcmp0(a, b, 1);
149251875Speter}
150