An open API service indexing awesome lists of open source software.

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

Awesome Lists containing this project

README

          

# libversion


libversion packaging status

![CI](https://github.com/repology/libversion/workflows/CI/badge.svg)
[![Github commits (since latest release)](https://img.shields.io/github/commits-since/repology/libversion/latest.svg)](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)