blob: e9dc9fb938358a1773285c552a2c83c2a352925b [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/util/version_util.h"
#include <regex.h>
#include <mutex>
#include <ostream>
#include <string>
#include <utility>
#include <glog/logging.h>
#include "kudu/gutil/macros.h"
#include "kudu/gutil/strings/numbers.h"
#include "kudu/gutil/strings/strip.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/status.h"
using std::ostream;
using std::string;
using strings::Substitute;
namespace kudu {
bool Version::operator==(const Version& other) const {
return this->major == other.major &&
this->minor == other.minor &&
this->maintenance == other.maintenance &&
this->extra_delimiter == other.extra_delimiter &&
this->extra == other.extra;
}
string Version::ToString() const {
return extra.empty()
? Substitute("$0.$1.$2", major, minor, maintenance)
: Substitute("$0.$1.$2$3$4", major, minor, maintenance, *extra_delimiter, extra);
}
ostream& operator<<(ostream& os, const Version& v) {
return os << v.ToString();
}
Status ParseVersion(const string& version_str,
Version* v) {
static regex_t re;
static std::once_flag once;
static const char* kVersionPattern =
"^([[:digit:]]+)\\." // <major>.
"([[:digit:]]+)\\." // <minor>.
"([[:digit:]]+)" // <maintenance>
"([.-].*)?$"; // [<delimiter><extra>]
std::call_once(once, []{
CHECK_EQ(0, regcomp(&re, kVersionPattern, REG_EXTENDED));
});
DCHECK(v);
const Status invalid_ver_err =
Status::InvalidArgument("invalid version string", version_str);
auto v_str = version_str;
StripWhiteSpace(&v_str);
regmatch_t matches[5];
if (regexec(&re, v_str.c_str(), arraysize(matches), matches, 0) != 0) {
return invalid_ver_err;
}
#define PARSE_REQUIRED_COMPONENT(idx, lhs) \
{ \
int i = (idx); \
if (matches[i].rm_so == -1 || \
!SimpleAtoi(v_str.substr(matches[i].rm_so, \
matches[i].rm_eo - matches[i].rm_so), \
(lhs))) { \
return invalid_ver_err; \
} \
}
Version temp_v;
PARSE_REQUIRED_COMPONENT(1, &temp_v.major);
PARSE_REQUIRED_COMPONENT(2, &temp_v.minor);
PARSE_REQUIRED_COMPONENT(3, &temp_v.maintenance);
#undef PARSE_REQUIRED_COMPONENT
if (matches[4].rm_so != -1) {
int extra_comp_off = matches[4].rm_so + 1; // skip the delimiter
int extra_comp_len = matches[4].rm_eo - extra_comp_off;
if (extra_comp_len > 0) {
temp_v.extra_delimiter = v_str[extra_comp_off - 1];
temp_v.extra = v_str.substr(extra_comp_off, extra_comp_len);
}
}
temp_v.raw_version = version_str;
*v = std::move(temp_v);
return Status::OK();
}
} // namespace kudu