.
[gnulib.git] / lib / memcoll.c
1 /* Locale-specific memory comparison.
2    Copyright 1999 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Contributed by Paul Eggert <eggert@twinsun.com>.  */
19
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #ifndef __GNUC__
25 # ifdef HAVE_ALLOCA_H
26 #  include <alloca.h>
27 # else
28 #  ifdef _AIX
29  #  pragma alloca
30 #  else
31 #   ifdef _WIN32
32 #    include <malloc.h>
33 #    include <io.h>
34 #   else
35 #    ifndef alloca
36 char *alloca ();
37 #    endif
38 #   endif
39 #  endif
40 # endif
41 #endif
42
43 #if HAVE_STRING_H
44 # include <string.h>
45 #endif
46
47 /* Compare S1 (with length S1LEN) and S2 (with length S2LEN) according
48    to the LC_COLLATE locale.  S1 and S2 do not overlap, but may be
49    adjacent.  Temporarily modify the bytes after S1 and S2, but
50    restore their original contents before returning.  */
51 int
52 memcoll (char *s1, size_t s1len, char *s2, size_t s2len)
53 {
54   int diff;
55   char n1;
56   char n2;
57
58   /* We will temporarily set the bytes after S1 and S2 to zero, so if
59      S1 and S2 are adjacent, compare to a temporary copy of the
60      earlier, to avoid temporarily stomping on the later.  */
61
62   if (s1 + s1len == s2)
63     {
64       char *s2copy = alloca (s2len + 1);
65       memcpy (s2copy, s2, s2len);
66       s2 = s2copy;
67     }
68
69   if (s2 + s2len == s1)
70     {
71       char *s1copy = alloca (s1len + 1);
72       memcpy (s1copy, s1, s1len);
73       s1 = s1copy;
74     }
75
76   n1 = s1[s1len];  s1[s1len++] = '\0';
77   n2 = s2[s2len];  s2[s2len++] = '\0';
78
79   while (! (diff = strcoll (s1, s2)))
80     {
81       /* strcoll found no difference, but perhaps it was fooled by NUL
82          characters in the data.  Work around this problem by advancing
83          past the NUL chars.  */
84       size_t size1 = strlen (s1) + 1;
85       size_t size2 = strlen (s2) + 1;
86       s1 += size1;
87       s2 += size2;
88       s1len -= size1;
89       s2len -= size2;
90
91       if (s1len == 0)
92         {
93           if (s2len != 0)
94             diff = -1;
95           break;
96         }
97       else if (s2len == 0)
98         {
99           diff = 1;
100           break;
101         }
102     }
103
104   s1[s1len - 1] = n1;
105   s2[s2len - 1] = n2;
106
107   return diff;
108 }