~ubuntu-branches/ubuntu/lucid/coreutils/lucid-updates

« back to all changes in this revision

Viewing changes to lib/filevercmp.c

  • Committer: Bazaar Package Importer
  • Author(s): Michael Stone
  • Date: 2009-02-22 12:36:29 UTC
  • mto: (1.2.1 upstream) (5.1.3 sid)
  • mto: This revision was merged to the branch mainline in revision 6.
  • Revision ID: james.westby@ubuntu.com-20090222123629-nqyzcikzk591uza2
Tags: upstream-7.1
ImportĀ upstreamĀ versionĀ 7.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
 
3
   Copyright (C) 2001 Anthony Towns <aj@azure.humbug.org.au>
 
4
   Copyright (C) 2008 Free Software Foundation, Inc.
 
5
 
 
6
   This program is free software: you can redistribute it and/or modify
 
7
   it under the terms of the GNU General Public License as published by
 
8
   the Free Software Foundation, either version 3 of the License, or
 
9
   (at your option) any later version.
 
10
 
 
11
   This program is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
   GNU General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU General Public License
 
17
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
18
 
 
19
#include <config.h>
 
20
#include "filevercmp.h"
 
21
 
 
22
#include <sys/types.h>
 
23
#include <stdlib.h>
 
24
#include <stdbool.h>
 
25
#include <string.h>
 
26
#include <c-ctype.h>
 
27
#include <limits.h>
 
28
 
 
29
/* Match a file suffix defined by this regular expression:
 
30
   /(\.[A-Za-z][A-Za-z0-9]*)*$/
 
31
   Scan the string *STR and return a pointer to the matching suffix, or
 
32
   NULL if not found.  Upon return, *STR points to terminating NUL.  */
 
33
static const char *
 
34
match_suffix (const char **str)
 
35
{
 
36
  const char *match = NULL;
 
37
  bool read_alpha = false;
 
38
  while (**str)
 
39
    {
 
40
      if (read_alpha)
 
41
        {
 
42
          read_alpha = false;
 
43
          if (!c_isalpha (**str))
 
44
            match = NULL;
 
45
        }
 
46
      else if ('.' == **str)
 
47
        {
 
48
          read_alpha = true;
 
49
          if (!match)
 
50
            match = *str;
 
51
        }
 
52
      else if (!c_isalnum (**str))
 
53
        match = NULL;
 
54
      (*str)++;
 
55
    }
 
56
  return match;
 
57
}
 
58
 
 
59
/* verrevcmp helper function */
 
60
static inline int
 
61
order (unsigned char c)
 
62
{
 
63
  if (c_isdigit (c))
 
64
    return 0;
 
65
  else if (c_isalpha (c))
 
66
    return c;
 
67
  else if (c == '~')
 
68
    return -1;
 
69
  else
 
70
    return (int) c + UCHAR_MAX + 1;
 
71
}
 
72
 
 
73
/* slightly modified verrevcmp function from dpkg
 
74
   S1, S2 - compared string
 
75
   S1_LEN, S2_LEN - length of strings to be scanned
 
76
 
 
77
   This implements the algorithm for comparison of version strings
 
78
   specified by Debian and now widely adopted.  The detailed
 
79
   specification can be found in the Debian Policy Manual in the
 
80
   section on the `Version' control field.  This version of the code
 
81
   implements that from s5.6.12 of Debian Policy v3.8.0.1
 
82
   http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version */
 
83
static int
 
84
verrevcmp (const char *s1, size_t s1_len, const char *s2, size_t s2_len)
 
85
{
 
86
  size_t s1_pos = 0;
 
87
  size_t s2_pos = 0;
 
88
  while (s1_pos < s1_len || s2_pos < s2_len)
 
89
    {
 
90
      int first_diff = 0;
 
91
      while ((s1_pos < s1_len && !c_isdigit (s1[s1_pos]))
 
92
             || (s2_pos < s2_len && !c_isdigit (s2[s2_pos])))
 
93
        {
 
94
          int s1_c = (s1_pos == s1_len) ? 0 : order (s1[s1_pos]);
 
95
          int s2_c = (s2_pos == s2_len) ? 0 : order (s2[s2_pos]);
 
96
          if (s1_c != s2_c)
 
97
            return s1_c - s2_c;
 
98
          s1_pos++;
 
99
          s2_pos++;
 
100
        }
 
101
      while (s1[s1_pos] == '0')
 
102
        s1_pos++;
 
103
      while (s2[s2_pos] == '0')
 
104
        s2_pos++;
 
105
      while (c_isdigit (s1[s1_pos]) && c_isdigit (s2[s2_pos]))
 
106
        {
 
107
          if (!first_diff)
 
108
            first_diff = s1[s1_pos] - s2[s2_pos];
 
109
          s1_pos++;
 
110
          s2_pos++;
 
111
        }
 
112
      if (c_isdigit (s1[s1_pos]))
 
113
        return 1;
 
114
      if (c_isdigit (s2[s2_pos]))
 
115
        return -1;
 
116
      if (first_diff)
 
117
        return first_diff;
 
118
    }
 
119
  return 0;
 
120
}
 
121
 
 
122
/* Compare version strings S1 and S2.
 
123
   See filevercmp.h for function description.  */
 
124
int
 
125
filevercmp (const char *s1, const char *s2)
 
126
{
 
127
  const char *s1_pos = s1;
 
128
  const char *s2_pos = s2;
 
129
  const char *s1_suffix, *s2_suffix;
 
130
  size_t s1_len, s2_len;
 
131
  int result;
 
132
 
 
133
  /* easy comparison to see if strings are identical */
 
134
  int simple_cmp = strcmp (s1, s2);
 
135
  if (simple_cmp == 0)
 
136
    return 0;
 
137
 
 
138
  /* "cut" file suffixes */
 
139
  s1_suffix = match_suffix (&s1_pos);
 
140
  s2_suffix = match_suffix (&s2_pos);
 
141
  s1_len = (s1_suffix ? s1_suffix : s1_pos) - s1;
 
142
  s2_len = (s2_suffix ? s2_suffix : s2_pos) - s2;
 
143
 
 
144
  /* restore file suffixes if strings are identical after "cut" */
 
145
  if ((s1_suffix || s2_suffix) && (s1_len == s2_len)
 
146
      && 0 == strncmp (s1, s2, s1_len))
 
147
    {
 
148
      s1_len = s1_pos - s1;
 
149
      s2_len = s2_pos - s2;
 
150
    }
 
151
 
 
152
  result = verrevcmp (s1, s1_len, s2, s2_len);
 
153
  return result == 0 ? simple_cmp : result;
 
154
}