14Srgrimes/*
24Srgrimes * version.c:  library version number and utilities
34Srgrimes *
44Srgrimes * ====================================================================
54Srgrimes *    Licensed to the Apache Software Foundation (ASF) under one
64Srgrimes *    or more contributor license agreements.  See the NOTICE file
74Srgrimes *    distributed with this work for additional information
84Srgrimes *    regarding copyright ownership.  The ASF licenses this file
94Srgrimes *    to you under the Apache License, Version 2.0 (the
104Srgrimes *    "License"); you may not use this file except in compliance
114Srgrimes *    with the License.  You may obtain a copy of the License at
124Srgrimes *
134Srgrimes *      http://www.apache.org/licenses/LICENSE-2.0
144Srgrimes *
154Srgrimes *    Unless required by applicable law or agreed to in writing,
164Srgrimes *    software distributed under the License is distributed on an
174Srgrimes *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
184Srgrimes *    KIND, either express or implied.  See the License for the
194Srgrimes *    specific language governing permissions and limitations
204Srgrimes *    under the License.
214Srgrimes * ====================================================================
224Srgrimes */
234Srgrimes
244Srgrimes
254Srgrimes
264Srgrimes#include "svn_error.h"
274Srgrimes#include "svn_version.h"
284Srgrimes
294Srgrimes#include "sysinfo.h"
304Srgrimes#include "svn_private_config.h"
314Srgrimes#include "private/svn_subr_private.h"
32551Srgrimes
3350477Speterconst svn_version_t *
344Srgrimessvn_subr_version(void)
354Srgrimes{
36196994Sphk  SVN_VERSION_BODY;
37196994Sphk}
38196994Sphk
39196994Sphk
40249722Stijlsvn_boolean_t svn_ver_compatible(const svn_version_t *my_version,
41249722Stijl                                 const svn_version_t *lib_version)
424Srgrimes{
434Srgrimes  /* With normal development builds the matching rules are strict, to
444Srgrimes     avoid inadvertantly using the wrong libraries.  For backward
4568498Sasmodai     compatibility testing use --disable-full-version-match to
4668498Sasmodai     configure 1.7 and then the libraries that get built can be used
47154128Simp     to replace those in 1.6 or earlier builds.  */
48223440Sjhb
49154128Simp#ifndef SVN_DISABLE_FULL_VERSION_MATCH
50154128Simp  if (lib_version->tag[0] != '\0')
5144364Simp    /* Development library; require exact match. */
52551Srgrimes    return svn_ver_equal(my_version, lib_version);
5344364Simp  else if (my_version->tag[0] != '\0')
5444364Simp    /* Development client; must be newer than the library
5538673Skato       and have the same major and minor version. */
5644364Simp    return (my_version->major == lib_version->major
5736977Sbde            && my_version->minor == lib_version->minor
5836977Sbde            && my_version->patch > lib_version->patch);
59177661Sjb#endif
60224207Sattilio
61183525Sjhb  /* General compatibility rules for released versions. */
62224207Sattilio  return (my_version->major == lib_version->major
6328354Sfsmp          && my_version->minor <= lib_version->minor);
6466296Sps}
65177661Sjb
664Srgrimes
67250338Sattiliosvn_boolean_t svn_ver_equal(const svn_version_t *my_version,
68250338Sattilio                            const svn_version_t *lib_version)
69250338Sattilio{
70250338Sattilio  return (my_version->major == lib_version->major
7168498Sasmodai          && my_version->minor == lib_version->minor
7268498Sasmodai          && my_version->patch == lib_version->patch
73195376Ssam          && 0 == strcmp(my_version->tag, lib_version->tag));
74195376Ssam}
75195376Ssam
76195376Ssam
77195376Ssamsvn_error_t *
78195376Ssamsvn_ver__check_list2(const svn_version_t *my_version,
79195376Ssam                     const svn_version_checklist_t *checklist,
804Srgrimes                     svn_boolean_t (*comparator)(const svn_version_t *,
81191278Srwatson                                                 const svn_version_t *))
82191278Srwatson{
83191278Srwatson  svn_error_t *err = SVN_NO_ERROR;
84191278Srwatson  int i;
85192331Sjhb
86191276Srwatson  for (i = 0; checklist[i].label != NULL; ++i)
87191276Srwatson    {
8815543Sphk      const svn_version_t *lib_version = checklist[i].version_query();
8915565Sphk      if (!comparator(my_version, lib_version))
901045Sdg        err = svn_error_createf(SVN_ERR_VERSION_MISMATCH, err,
9115543Sphk                                _("Version mismatch in '%s'%s:"
9215565Sphk                                  " found %d.%d.%d%s,"
93282065Skib                                  " expected %d.%d.%d%s"),
94112841Sjake                                checklist[i].label,
95112841Sjake                                comparator == svn_ver_equal
96183343Skmacy                                ? _(" (expecting equality)")
97112841Sjake                                : comparator == svn_ver_compatible
98111363Sjake                                ? _(" (expecting compatibility)")
99111363Sjake                                : "",
100183343Skmacy                                lib_version->major, lib_version->minor,
101112841Sjake                                lib_version->patch, lib_version->tag,
102111363Sjake                                my_version->major, my_version->minor,
103111363Sjake                                my_version->patch, my_version->tag);
104111363Sjake    }
10515543Sphk
10615565Sphk  return err;
10727950Sdyson}
1084Srgrimes
109197316Salc
110197316Salcstruct svn_version_extended_t
11127993Sdyson{
11282309Speter  const char *build_date;       /* Compilation date */
11383366Sjulian  const char *build_time;       /* Compilation time */
11483366Sjulian  const char *build_host;       /* Build canonical host name */
11582309Speter  const char *copyright;        /* Copyright notice (localized) */
116116355Salc  const char *runtime_host;     /* Runtime canonical host name */
117286878Skib  const char *runtime_osname;   /* Running OS release name */
118286878Skib
119286878Skib  /* Array of svn_version_ext_linked_lib_t describing dependent
120286878Skib     libraries. */
121286878Skib  const apr_array_header_t *linked_libs;
1224Srgrimes
1234Srgrimes  /* Array of svn_version_ext_loaded_lib_t describing loaded shared
124239730Sdes     libraries. */
125239730Sdes  const apr_array_header_t *loaded_libs;
126239730Sdes};
127239730Sdes
128239730Sdes
129239730Sdesconst svn_version_extended_t *
130239730Sdessvn_version_extended(svn_boolean_t verbose,
131239730Sdes                     apr_pool_t *pool)
132239730Sdes{
133239730Sdes  svn_version_extended_t *info = apr_pcalloc(pool, sizeof(*info));
134239730Sdes
135239730Sdes  info->build_date = NULL;
136239730Sdes  info->build_time = NULL;
137239730Sdes  info->build_host = SVN_BUILD_HOST;
13881933Sdillon  info->copyright = apr_pstrdup
139102738Sdillon    (pool, _("Copyright (C) 2015 The Apache Software Foundation.\n"
140102738Sdillon             "This software consists of contributions made by many people;\n"
141248789Skib             "see the NOTICE file for more information.\n"
142248789Skib             "Subversion is open source software, see "
143248789Skib             "http://subversion.apache.org/\n"));
14481933Sdillon
14581933Sdillon  if (verbose)
146248789Skib    {
14781933Sdillon      info->runtime_host = svn_sysinfo__canonical_host(pool);
14881933Sdillon      info->runtime_osname = svn_sysinfo__release_name(pool);
14981933Sdillon      info->linked_libs = svn_sysinfo__linked_libs(pool);
1504Srgrimes      info->loaded_libs = svn_sysinfo__loaded_libs(pool);
1514Srgrimes    }
15240286Sdg
15340286Sdg  return info;
154112569Sjake}
155112569Sjake
1561549Srgrimes
157112569Sjakeconst char *
158112569Sjakesvn_version_ext_build_date(const svn_version_extended_t *ext_info)
1591045Sdg{
160112569Sjake  return ext_info->build_date;
161112569Sjake}
162584Srgrimes
16344157Sluoqiconst char *
16444157Sluoqisvn_version_ext_build_time(const svn_version_extended_t *ext_info)
165286305Skib{
166286305Skib  return ext_info->build_time;
167286305Skib}
168196994Sphk
169const char *
170svn_version_ext_build_host(const svn_version_extended_t *ext_info)
171{
172  return ext_info->build_host;
173}
174
175const char *
176svn_version_ext_copyright(const svn_version_extended_t *ext_info)
177{
178  return ext_info->copyright;
179}
180
181const char *
182svn_version_ext_runtime_host(const svn_version_extended_t *ext_info)
183{
184  return ext_info->runtime_host;
185}
186
187const char *
188svn_version_ext_runtime_osname(const svn_version_extended_t *ext_info)
189{
190  return ext_info->runtime_osname;
191}
192
193const apr_array_header_t *
194svn_version_ext_linked_libs(const svn_version_extended_t *ext_info)
195{
196  return ext_info->linked_libs;
197}
198
199const apr_array_header_t *
200svn_version_ext_loaded_libs(const svn_version_extended_t *ext_info)
201{
202  return ext_info->loaded_libs;
203}
204
205svn_error_t *
206svn_version__parse_version_string(svn_version_t **version_p,
207                                  const char *version_string,
208                                  apr_pool_t *result_pool)
209{
210  svn_error_t *err;
211  svn_version_t *version;
212  apr_array_header_t *pieces =
213    svn_cstring_split(version_string, ".", FALSE, result_pool);
214
215  if ((pieces->nelts < 2) || (pieces->nelts > 3))
216    return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, NULL,
217                             _("Failed to parse version number string '%s'"),
218                             version_string);
219
220  version = apr_pcalloc(result_pool, sizeof(*version));
221  version->tag = "";
222
223  /* Parse the major and minor integers strictly. */
224  err = svn_cstring_atoi(&(version->major),
225                         APR_ARRAY_IDX(pieces, 0, const char *));
226  if (err)
227    return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
228                             _("Failed to parse version number string '%s'"),
229                             version_string);
230  err = svn_cstring_atoi(&(version->minor),
231                         APR_ARRAY_IDX(pieces, 1, const char *));
232  if (err)
233    return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
234                             _("Failed to parse version number string '%s'"),
235                             version_string);
236
237  /* If there's a third component, we'll parse it, too.  But we don't
238     require that it be present. */
239  if (pieces->nelts == 3)
240    {
241      const char *piece = APR_ARRAY_IDX(pieces, 2, const char *);
242      char *hyphen = strchr(piece, '-');
243      if (hyphen)
244        {
245          version->tag = apr_pstrdup(result_pool, hyphen + 1);
246          *hyphen = '\0';
247        }
248      err = svn_cstring_atoi(&(version->patch), piece);
249      if (err)
250        return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
251                                 _("Failed to parse version number string '%s'"
252                                  ),
253                                 version_string);
254    }
255
256  if (version->major < 0 || version->minor < 0 || version->patch < 0)
257    return svn_error_createf(SVN_ERR_MALFORMED_VERSION_STRING, err,
258                             _("Failed to parse version number string '%s'"),
259                             version_string);
260
261  *version_p = version;
262  return SVN_NO_ERROR;
263}
264
265
266svn_boolean_t
267svn_version__at_least(svn_version_t *version,
268                      int major,
269                      int minor,
270                      int patch)
271{
272  /* Compare major versions. */
273  if (version->major < major)
274    return FALSE;
275  if (version->major > major)
276    return TRUE;
277
278  /* Major versions are the same.  Compare minor versions. */
279  if (version->minor < minor)
280    return FALSE;
281  if (version->minor > minor)
282    return TRUE;
283
284  /* Major and minor versions are the same.  Compare patch
285     versions. */
286  if (version->patch < patch)
287    return FALSE;
288  if (version->patch > patch)
289    return TRUE;
290
291  /* Major, minor, and patch versions are identical matches.  But tags
292     in our schema are always used for versions not yet quite at the
293     given patch level. */
294  if (version->tag && version->tag[0])
295    return FALSE;
296
297  return TRUE;
298}
299