1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 The PHP Group | |
6 | +----------------------------------------------------------------------+ |
7 | | This source file is subject to version 3.01 of the PHP license, | |
8 | | that is bundled with this package in the file LICENSE, and is | |
9 | | available through the world-wide-web at the following url: | |
10 | | http://www.php.net/license/3_01.txt | |
11 | | If you did not receive a copy of the PHP license and are unable to | |
12 | | obtain it through the world-wide-web, please send a note to | |
13 | | license@php.net so we can mail you a copy immediately. | |
14 | +----------------------------------------------------------------------+ |
15 | | Author: Stig Sæther Bakken <ssb@php.net> | |
16 | +----------------------------------------------------------------------+ |
17 | */ |
18 | |
19 | /* $Id$ */ |
20 | |
21 | #include <stdio.h> |
22 | #include <sys/types.h> |
23 | #include <ctype.h> |
24 | #include <stdlib.h> |
25 | #include <string.h> |
26 | #include "php.h" |
27 | #include "php_versioning.h" |
28 | |
29 | #define sign(n) ((n)<0?-1:((n)>0?1:0)) |
30 | |
31 | /* {{{ php_canonicalize_version() */ |
32 | |
33 | PHPAPI char * |
34 | php_canonicalize_version(const char *version) |
35 | { |
36 | int len = strlen(version); |
37 | char *buf = safe_emalloc(len, 2, 1), *q, lp, lq; |
38 | const char *p; |
39 | |
40 | if (len == 0) { |
41 | *buf = '\0'; |
42 | return buf; |
43 | } |
44 | |
45 | p = version; |
46 | q = buf; |
47 | *q++ = lp = *p++; |
48 | |
49 | while (*p) { |
50 | /* s/[-_+]/./g; |
51 | * s/([^\d\.])([^\D\.])/$1.$2/g; |
52 | * s/([^\D\.])([^\d\.])/$1.$2/g; |
53 | */ |
54 | #define isdig(x) (isdigit(x)&&(x)!='.') |
55 | #define isndig(x) (!isdigit(x)&&(x)!='.') |
56 | #define isspecialver(x) ((x)=='-'||(x)=='_'||(x)=='+') |
57 | |
58 | lq = *(q - 1); |
59 | if (isspecialver(*p)) { |
60 | if (lq != '.') { |
61 | *q++ = '.'; |
62 | } |
63 | } else if ((isndig(lp) && isdig(*p)) || (isdig(lp) && isndig(*p))) { |
64 | if (lq != '.') { |
65 | *q++ = '.'; |
66 | } |
67 | *q++ = *p; |
68 | } else if (!isalnum(*p)) { |
69 | if (lq != '.') { |
70 | *q++ = '.'; |
71 | } |
72 | } else { |
73 | *q++ = *p; |
74 | } |
75 | lp = *p++; |
76 | } |
77 | *q++ = '\0'; |
78 | return buf; |
79 | } |
80 | |
81 | /* }}} */ |
82 | /* {{{ compare_special_version_forms() */ |
83 | |
84 | typedef struct { |
85 | const char *name; |
86 | int order; |
87 | } special_forms_t; |
88 | |
89 | static int |
90 | compare_special_version_forms(char *form1, char *form2) |
91 | { |
92 | int found1 = -1, found2 = -1; |
93 | special_forms_t special_forms[11] = { |
94 | {"dev" , 0}, |
95 | {"alpha" , 1}, |
96 | {"a" , 1}, |
97 | {"beta" , 2}, |
98 | {"b" , 2}, |
99 | {"RC" , 3}, |
100 | {"rc" , 3}, |
101 | {"#" , 4}, |
102 | {"pl" , 5}, |
103 | {"p" , 5}, |
104 | {NULL, 0}, |
105 | }; |
106 | special_forms_t *pp; |
107 | |
108 | for (pp = special_forms; pp && pp->name; pp++) { |
109 | if (strncmp(form1, pp->name, strlen(pp->name)) == 0) { |
110 | found1 = pp->order; |
111 | break; |
112 | } |
113 | } |
114 | for (pp = special_forms; pp && pp->name; pp++) { |
115 | if (strncmp(form2, pp->name, strlen(pp->name)) == 0) { |
116 | found2 = pp->order; |
117 | break; |
118 | } |
119 | } |
120 | return sign(found1 - found2); |
121 | } |
122 | |
123 | /* }}} */ |
124 | /* {{{ php_version_compare() */ |
125 | |
126 | PHPAPI int |
127 | php_version_compare(const char *orig_ver1, const char *orig_ver2) |
128 | { |
129 | char *ver1; |
130 | char *ver2; |
131 | char *p1, *p2, *n1, *n2; |
132 | long l1, l2; |
133 | int compare = 0; |
134 | |
135 | if (!*orig_ver1 || !*orig_ver2) { |
136 | if (!*orig_ver1 && !*orig_ver2) { |
137 | return 0; |
138 | } else { |
139 | return *orig_ver1 ? 1 : -1; |
140 | } |
141 | } |
142 | if (orig_ver1[0] == '#') { |
143 | ver1 = estrdup(orig_ver1); |
144 | } else { |
145 | ver1 = php_canonicalize_version(orig_ver1); |
146 | } |
147 | if (orig_ver2[0] == '#') { |
148 | ver2 = estrdup(orig_ver2); |
149 | } else { |
150 | ver2 = php_canonicalize_version(orig_ver2); |
151 | } |
152 | p1 = n1 = ver1; |
153 | p2 = n2 = ver2; |
154 | while (*p1 && *p2 && n1 && n2) { |
155 | if ((n1 = strchr(p1, '.')) != NULL) { |
156 | *n1 = '\0'; |
157 | } |
158 | if ((n2 = strchr(p2, '.')) != NULL) { |
159 | *n2 = '\0'; |
160 | } |
161 | if (isdigit(*p1) && isdigit(*p2)) { |
162 | /* compare element numerically */ |
163 | l1 = strtol(p1, NULL, 10); |
164 | l2 = strtol(p2, NULL, 10); |
165 | compare = sign(l1 - l2); |
166 | } else if (!isdigit(*p1) && !isdigit(*p2)) { |
167 | /* compare element names */ |
168 | compare = compare_special_version_forms(p1, p2); |
169 | } else { |
170 | /* mix of names and numbers */ |
171 | if (isdigit(*p1)) { |
172 | compare = compare_special_version_forms("#N#" , p2); |
173 | } else { |
174 | compare = compare_special_version_forms(p1, "#N#" ); |
175 | } |
176 | } |
177 | if (compare != 0) { |
178 | break; |
179 | } |
180 | if (n1 != NULL) { |
181 | p1 = n1 + 1; |
182 | } |
183 | if (n2 != NULL) { |
184 | p2 = n2 + 1; |
185 | } |
186 | } |
187 | if (compare == 0) { |
188 | if (n1 != NULL) { |
189 | if (isdigit(*p1)) { |
190 | compare = 1; |
191 | } else { |
192 | compare = php_version_compare(p1, "#N#" ); |
193 | } |
194 | } else if (n2 != NULL) { |
195 | if (isdigit(*p2)) { |
196 | compare = -1; |
197 | } else { |
198 | compare = php_version_compare("#N#" , p2); |
199 | } |
200 | } |
201 | } |
202 | efree(ver1); |
203 | efree(ver2); |
204 | return compare; |
205 | } |
206 | |
207 | /* }}} */ |
208 | /* {{{ proto int version_compare(string ver1, string ver2 [, string oper]) |
209 | Compares two "PHP-standardized" version number strings */ |
210 | |
211 | PHP_FUNCTION(version_compare) |
212 | { |
213 | char *v1, *v2, *op = NULL; |
214 | int v1_len, v2_len, op_len = 0; |
215 | int compare, argc; |
216 | |
217 | argc = ZEND_NUM_ARGS(); |
218 | if (zend_parse_parameters(argc TSRMLS_CC, "ss|s" , &v1, &v1_len, &v2, |
219 | &v2_len, &op, &op_len) == FAILURE) { |
220 | return; |
221 | } |
222 | compare = php_version_compare(v1, v2); |
223 | if (argc == 2) { |
224 | RETURN_LONG(compare); |
225 | } |
226 | if (!strncmp(op, "<" , op_len) || !strncmp(op, "lt" , op_len)) { |
227 | RETURN_BOOL(compare == -1); |
228 | } |
229 | if (!strncmp(op, "<=" , op_len) || !strncmp(op, "le" , op_len)) { |
230 | RETURN_BOOL(compare != 1); |
231 | } |
232 | if (!strncmp(op, ">" , op_len) || !strncmp(op, "gt" , op_len)) { |
233 | RETURN_BOOL(compare == 1); |
234 | } |
235 | if (!strncmp(op, ">=" , op_len) || !strncmp(op, "ge" , op_len)) { |
236 | RETURN_BOOL(compare != -1); |
237 | } |
238 | if (!strncmp(op, "==" , op_len) || !strncmp(op, "=" , op_len) || !strncmp(op, "eq" , op_len)) { |
239 | RETURN_BOOL(compare == 0); |
240 | } |
241 | if (!strncmp(op, "!=" , op_len) || !strncmp(op, "<>" , op_len) || !strncmp(op, "ne" , op_len)) { |
242 | RETURN_BOOL(compare != 0); |
243 | } |
244 | RETURN_NULL(); |
245 | } |
246 | |
247 | /* }}} */ |
248 | |
249 | /* |
250 | * Local variables: |
251 | * tab-width: 4 |
252 | * c-basic-offset: 4 |
253 | * indent-tabs-mode: t |
254 | * End: |
255 | */ |
256 | |