https://github.com/repology/libversion
Advanced version string comparison library
https://github.com/repology/libversion
c library version versioning
Last synced: 2 months ago
JSON representation
Advanced version string comparison library
- Host: GitHub
- URL: https://github.com/repology/libversion
- Owner: repology
- License: mit
- Created: 2017-03-16T10:55:38.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2025-10-06T20:28:11.000Z (3 months ago)
- Last Synced: 2025-10-21T04:01:54.343Z (2 months ago)
- Topics: c, library, version, versioning
- Language: C
- Homepage:
- Size: 169 KB
- Stars: 118
- Watchers: 8
- Forks: 16
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGES.md
- License: COPYING
Awesome Lists containing this project
README
# libversion

[](https://github.com/repology/libversion)
Advanced version string comparison library.
Need to compare software, package or whatever versions? Comparing
`1.0` and `1.1` could be easy, but are you ready for more
complex cases like `1.2-x.3~alpha4`? **libversion** is, which
is proven by using the library in [Repology](https://repology.org/)
project which relies on comparing software version strings, even
if they are written in different formats.
## Features
A short list of version features libversion handles for you:
* Simple versions, obviously: `0.9 < 1.0 < 1.1`
* Omitting insignificant components: `1.0 == 1.0.0`
* Leading zeroes: `1.001 == 1.1`
* Unusual separators: `1_2~3 == 1.2.3`
* Letter suffixes: `1.2 < 1.2a < 1.2b < 1.3`
* Alphanumeric prerelease components:
* `1.0alpha1 == 1.0.alpha1 == 1.0a1 == 1.0.a1`
* `1.0alpha1 < 1.0alpha2 < 1.0beta1 < 1.0rc1 < 1.0`
* Awareness of prerelease keywords: while `1.0 < 1.0a-1` (_a_ treated
as version addendum), but `1.0alpha-1 < 1.0` (_alpha_ is treated
as prerelease marker)
* Awareness of _patch_, _post_ and _pl_ keywords: while `1.0alpha1 < 1.0`
(_alpha_ is pre-release), but `1.0 < 1.0patch1 < 1.1` (_patch_ is post-release)
* Customizable handling of _p_ keyword (it may mean either _patch_ or _pre_,
and since libversion cannot guess, this is controlled with an external flag)
See [doc/ALGORITHM.md](doc/ALGORITHM.md) for more elaborate description
of inner logic.
## API
### Version comparison
```
int version_compare2(const char* v1, const char* v2);
int version_compare4(const char* v1, const char* v2, int v1_flags, int v2_flags);
```
Compares version strings `v1` and `v2`.
Returns **-1** if `v1` is lower than `v2`, **0** if `v1` is equal to `v2` and **1** if `v1` is higher than `v2`.
Thread safe, does not produce errors, does not allocate dynamic memory,
O(N) computational complexity, O(1) stack memory requirements.
4-argument form allows specifying flags for each version argument to
tune comparison behavior in specific cases. Currently supported `flags`
values are:
* `VERSIONFLAG_P_IS_PATCH` _p_ letter is treated as _patch_
(post-release) instead of _pre_ (pre-release).
* `VERSIONFLAG_ANY_IS_PATCH` any letter sequence is treated as
post-release (useful for handling patchsets as in
`1.2foopatchset3.barpatchset4`).
* `VERSIONFLAG_LOWER_BOUND` derive lowest possible version with
the given prefix. For example, lower bound for `1.0` is such
imaginary version `?` that it's higher than any release before
`1.0` and lower than any prerelease of `1.0`.
E.g. `0.999` < lower bound(`1.0`) < `1.0alpha0`.
* `VERSIONFLAG_UPPER_BOUND` derive highest possible version with
the given prefix. Opposite of `VERSIONFLAG_LOWER_BOUND`.
If both `flags` are zero, `version_compare4` acts exactly the same
as `version_compare2`.
## Example
```c
#include
#include
int main() {
/* 0.99 < 1.11 */
assert(version_compare2("0.99", "1.11") == -1);
/* 1.0 == 1.0.0 */
assert(version_compare2("1.0", "1.0.0") == 0);
/* 1.0alpha1 < 1.0.rc1 */
assert(version_compare2("1.0alpha1", "1.0.rc1") == -1);
/* 1.0 > 1.0.rc1 */
assert(version_compare2("1.0", "1.0-rc1") == 1);
/* 1.2.3alpha4 is the same as 1.2.3~a4 */
assert(version_compare2("1.2.3alpha4", "1.2.3~a4") == 0);
/* by default, `p' is treated as `pre'... */
assert(version_compare2("1.0p1", "1.0pre1") == 0);
assert(version_compare2("1.0p1", "1.0post1") == -1);
assert(version_compare2("1.0p1", "1.0patch1") == -1);
/* ...but this is tunable: here it's handled as `patch` */
assert(version_compare4("1.0p1", "1.0pre1", VERSIONFLAG_P_IS_PATCH, 0) == 1);
assert(version_compare4("1.0p1", "1.0post1", VERSIONFLAG_P_IS_PATCH, 0) == 0);
assert(version_compare4("1.0p1", "1.0patch1", VERSIONFLAG_P_IS_PATCH, 0) == 0);
/* a way to check that the version belongs to a given release */
assert(
(version_compare4("1.0alpha1", "1.0", 0, VERSIONFLAG_LOWER_BOUND) == 1) &&
(version_compare4("1.0alpha1", "1.0", 0, VERSIONFLAG_UPPER_BOUND) == -1) &&
(version_compare4("1.0.1", "1.0", 0, VERSIONFLAG_LOWER_BOUND) == 1) &&
(version_compare4("1.0.1", "1.0", 0, VERSIONFLAG_UPPER_BOUND) == -1) &&
/* 1.0alpha1 and 1.0.1 belong to 1.0 release, e.g. they lie between
(lowest possible version in 1.0) and (highest possible version in 1.0) */
);
}
```
```sh
gcc my_code.c `pkg-config --cflags --libs libversion`
# or (for static linking)
gcc my_code.c --static `pkg-config --static --cflags --libs libversion`
```
Using libversion in CMake is very simple (note that this handles
include paths and compiler flags as well):
```cmake
find_package(libversion)
target_link_libraries(my_target libversion::libversion)
# or (for static linking)
target_link_libraries(my_target libversion::libversion_static)
```
## Building
libversion uses [CMake](https://cmake.org/) build system.
To build the library, run `cmake . && cmake --build .`.
To run test suite, run `ctest` after building.
To install the library systemwide, run `make install`.
The project installs library, headers, pkg-config file, CMake import
files and a demo utility, `version_compare`, which may be used to
compare versions from command line:
```
$ ./version_compare
Usage: ./version_compare version1 version2
$ ./version_compare 0.99 1.0
<
```
## Bindings and compatible implementations
* Python: [py-libversion](https://github.com/repology/py-libversion) by @AMDmi3
* Go: [golibversion](https://github.com/saenai255/golibversion) by @saenai255
* Rust: [libversion-rs](https://github.com/repology/libversion-rs) by @AMDmi3 (not really a binding, but pure Rust implementation)
* Ruby: [ruby-libversion](https://github.com/Zopolis4/ruby-libversion) by @Zopolis4
* Perl: [Version::libversion::XS](https://github.com/giterlizzi/perl-Version-libversion-XS) by @giterlizzi
* Raku: [Version::Repology](https://raku.land/zef:lizmat/Version::Repology) by @lizmat (a pure Raku implementation)
## Author
* [Dmitry Marakasov](https://github.com/AMDmi3)
## License
* [MIT](COPYING)