1 /*
2 * version.c: library version number and utilities
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25
26 #include "svn_error.h"
27 #include "svn_version.h"
28
29 #include "sysinfo.h"
30 #include "svn_private_config.h"
31 #include "private/svn_subr_private.h"
32
33 const svn_version_t *
svn_subr_version(void)34 svn_subr_version(void)
35 {
36 SVN_VERSION_BODY;
37 }
38
39
svn_ver_compatible(const svn_version_t * my_version,const svn_version_t * lib_version)40 svn_boolean_t svn_ver_compatible(const svn_version_t *my_version,
41 const svn_version_t *lib_version)
42 {
43 /* With normal development builds the matching rules are stricter
44 that for release builds, to avoid inadvertantly using the wrong
45 libraries. For backward compatibility testing of development
46 builds one can use --disable-full-version-match to cause a
47 development build to use the release build rules. This allows
48 the libraries from the newer development build to be used by an
49 older development build. */
50
51 #ifndef SVN_DISABLE_FULL_VERSION_MATCH
52 if (lib_version->tag[0] != '\0')
53 /* Development library; require exact match. */
54 return svn_ver_equal(my_version, lib_version);
55 else if (my_version->tag[0] != '\0')
56 /* Development client; must be newer than the library
57 and have the same major and minor version. */
58 return (my_version->major == lib_version->major
59 && my_version->minor == lib_version->minor
60 && my_version->patch > lib_version->patch);
61 #endif
62
63 /* General compatibility rules for released versions. */
64 return (my_version->major == lib_version->major
65 && my_version->minor <= lib_version->minor);
66 }
67
68
svn_ver_equal(const svn_version_t * my_version,const svn_version_t * lib_version)69 svn_boolean_t svn_ver_equal(const svn_version_t *my_version,
70 const svn_version_t *lib_version)
71 {
72 return (my_version->major == lib_version->major
73 && my_version->minor == lib_version->minor
74 && my_version->patch == lib_version->patch
75 && 0 == strcmp(my_version->tag, lib_version->tag));
76 }
77
78
79 svn_error_t *
svn_ver_check_list2(const svn_version_t * my_version,const svn_version_checklist_t * checklist,svn_boolean_t (* comparator)(const svn_version_t *,const svn_version_t *))80 svn_ver_check_list2(const svn_version_t *my_version,
81 const svn_version_checklist_t *checklist,
82 svn_boolean_t (*comparator)(const svn_version_t *,
83 const svn_version_t *))
84 {
85 svn_error_t *err = SVN_NO_ERROR;
86 int i;
87
88 #ifdef SVN_DISABLE_FULL_VERSION_MATCH
89 /* Force more relaxed check for --disable-full-version-match. */
90 comparator = svn_ver_compatible;
91 #endif
92
93 for (i = 0; checklist[i].label != NULL; ++i)
94 {
95 const svn_version_t *lib_version = checklist[i].version_query();
96 if (!comparator(my_version, lib_version))
97 err = svn_error_createf(SVN_ERR_VERSION_MISMATCH, err,
98 _("Version mismatch in '%s'%s:"
99 " found %d.%d.%d%s,"
100 " expected %d.%d.%d%s"),
101 checklist[i].label,
102 comparator == svn_ver_equal
103 ? _(" (expecting equality)")
104 : comparator == svn_ver_compatible
105 ? _(" (expecting compatibility)")
106 : "",
107 lib_version->major, lib_version->minor,
108 lib_version->patch, lib_version->tag,
109 my_version->major, my_version->minor,
110 my_version->patch, my_version->tag);
111 }
112
113 return err;
114 }
115
116
117 struct svn_version_extended_t
118 {
119 const char *build_date; /* Compilation date */
120 const char *build_time; /* Compilation time */
121 const char *build_host; /* Build canonical host name */
122 const char *copyright; /* Copyright notice (localized) */
123 const char *runtime_host; /* Runtime canonical host name */
124 const char *runtime_osname; /* Running OS release name */
125
126 /* Array of svn_version_ext_linked_lib_t describing dependent
127 libraries. */
128 const apr_array_header_t *linked_libs;
129
130 /* Array of svn_version_ext_loaded_lib_t describing loaded shared
131 libraries. */
132 const apr_array_header_t *loaded_libs;
133 };
134
135
136 const svn_version_extended_t *
svn_version_extended(svn_boolean_t verbose,apr_pool_t * pool)137 svn_version_extended(svn_boolean_t verbose,
138 apr_pool_t *pool)
139 {
140 svn_version_extended_t *info = apr_pcalloc(pool, sizeof(*info));
141
142 info->build_date = NULL;
143 info->build_time = NULL;
144 info->build_host = SVN_BUILD_HOST;
145 info->copyright = apr_pstrdup
146 (pool, _("Copyright (C) 2021 The Apache Software Foundation.\n"
147 "This software consists of contributions made by many people;\n"
148 "see the NOTICE file for more information.\n"
149 "Subversion is open source software, see "
150 "http://subversion.apache.org/\n"));
151
152 if (verbose)
153 {
154 info->runtime_host = svn_sysinfo__canonical_host(pool);
155 info->runtime_osname = svn_sysinfo__release_name(pool);
156 info->linked_libs = svn_sysinfo__linked_libs(pool);
157 info->loaded_libs = svn_sysinfo__loaded_libs(pool);
158 }
159
160 return info;
161 }
162
163
164 const char *
svn_version_ext_build_date(const svn_version_extended_t * ext_info)165 svn_version_ext_build_date(const svn_version_extended_t *ext_info)
166 {
167 return ext_info->build_date;
168 }
169
170 const char *
svn_version_ext_build_time(const svn_version_extended_t * ext_info)171 svn_version_ext_build_time(const svn_version_extended_t *ext_info)
172 {
173 return ext_info->build_time;
174 }
175
176 const char *
svn_version_ext_build_host(const svn_version_extended_t * ext_info)177 svn_version_ext_build_host(const svn_version_extended_t *ext_info)
178 {
179 return ext_info->build_host;
180 }
181
182 const char *
svn_version_ext_copyright(const svn_version_extended_t * ext_info)183 svn_version_ext_copyright(const svn_version_extended_t *ext_info)
184 {
185 return ext_info->copyright;
186 }
187
188 const char *
svn_version_ext_runtime_host(const svn_version_extended_t * ext_info)189 svn_version_ext_runtime_host(const svn_version_extended_t *ext_info)
190 {
191 return ext_info->runtime_host;
192 }
193
194 const char *
svn_version_ext_runtime_osname(const svn_version_extended_t * ext_info)195 svn_version_ext_runtime_osname(const svn_version_extended_t *ext_info)
196 {
197 return ext_info->runtime_osname;
198 }
199
200 const apr_array_header_t *
svn_version_ext_linked_libs(const svn_version_extended_t * ext_info)201 svn_version_ext_linked_libs(const svn_version_extended_t *ext_info)
202 {
203 return ext_info->linked_libs;
204 }
205
206 const apr_array_header_t *
svn_version_ext_loaded_libs(const svn_version_extended_t * ext_info)207 svn_version_ext_loaded_libs(const svn_version_extended_t *ext_info)
208 {
209 return ext_info->loaded_libs;
210 }
211
212 svn_error_t *
svn_version__parse_version_string(svn_version_t ** version_p,const char * version_string,apr_pool_t * result_pool)213 svn_version__parse_version_string(svn_version_t **version_p,
214 const char *version_string,
215 apr_pool_t *result_pool)
216 {
217 svn_error_t *err;
218 svn_version_t *version;
219 apr_array_header_t *pieces =
220 svn_cstring_split(version_string, ".", FALSE, result_pool);
221
222 if ((pieces->nelts < 2) || (pieces->nelts > 3))
223 return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, NULL,
224 _("Failed to parse version number string '%s'"),
225 version_string);
226
227 version = apr_pcalloc(result_pool, sizeof(*version));
228 version->tag = "";
229
230 /* Parse the major and minor integers strictly. */
231 err = svn_cstring_atoi(&(version->major),
232 APR_ARRAY_IDX(pieces, 0, const char *));
233 if (err)
234 return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
235 _("Failed to parse version number string '%s'"),
236 version_string);
237 err = svn_cstring_atoi(&(version->minor),
238 APR_ARRAY_IDX(pieces, 1, const char *));
239 if (err)
240 return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
241 _("Failed to parse version number string '%s'"),
242 version_string);
243
244 /* If there's a third component, we'll parse it, too. But we don't
245 require that it be present. */
246 if (pieces->nelts == 3)
247 {
248 const char *piece = APR_ARRAY_IDX(pieces, 2, const char *);
249 char *hyphen = strchr(piece, '-');
250 if (hyphen)
251 {
252 version->tag = apr_pstrdup(result_pool, hyphen + 1);
253 *hyphen = '\0';
254 }
255 err = svn_cstring_atoi(&(version->patch), piece);
256 if (err)
257 return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
258 _("Failed to parse version number string '%s'"
259 ),
260 version_string);
261 }
262
263 if (version->major < 0 || version->minor < 0 || version->patch < 0)
264 return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
265 _("Failed to parse version number string '%s'"),
266 version_string);
267
268 *version_p = version;
269 return SVN_NO_ERROR;
270 }
271
272
273 svn_boolean_t
svn_version__at_least(const svn_version_t * version,int major,int minor,int patch)274 svn_version__at_least(const svn_version_t *version,
275 int major,
276 int minor,
277 int patch)
278 {
279 /* Compare major versions. */
280 if (version->major < major)
281 return FALSE;
282 if (version->major > major)
283 return TRUE;
284
285 /* Major versions are the same. Compare minor versions. */
286 if (version->minor < minor)
287 return FALSE;
288 if (version->minor > minor)
289 return TRUE;
290
291 /* Major and minor versions are the same. Compare patch
292 versions. */
293 if (version->patch < patch)
294 return FALSE;
295 if (version->patch > patch)
296 return TRUE;
297
298 /* Major, minor, and patch versions are identical matches. But tags
299 in our schema are always used for versions not yet quite at the
300 given patch level. */
301 if (version->tag && version->tag[0])
302 return FALSE;
303
304 return TRUE;
305 }
306