/* version.c - Version checking * Copyright (C) 2001, 2002, 2012, 2013, 2014 g10 Code GmbH * * This file is part of libgpg-error. * * libgpg-error is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * libgpg-error is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #define digitp(a) ((a) >= '0' && (a) <= '9') /* This is actually a dummy function to make sure that is module is not empty. Some compilers barf on empty modules. */ static const char * cright_blurb (void) { static const char blurb[] = "\n\n" "This is Libgpg-error " PACKAGE_VERSION " - A runtime library\n" "Copyright 2001-2022 g10 Code GmbH\n" "\n" "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n" "\n\n"; return blurb; } /* This function parses the first portion of the version number S and * stores it at NUMBER. On success, this function returns a pointer * into S starting with the first character, which is not part of the * initial number portion; on failure, NULL is returned. */ static const char* parse_version_number (const char *s, int *number) { int val = 0; if (*s == '0' && digitp (s[1])) return NULL; /* Leading zeros are not allowed. */ for (; digitp (*s); s++) { val *= 10; val += *s - '0'; } *number = val; return val < 0 ? NULL : s; } /* This function breaks up the complete string-representation of the * version number S, which is of the following struture: ... The major, * minor and micro number components will be stored in *MAJOR, *MINOR * and *MICRO. If MINOR or MICRO is NULL the version number is * assumed to have just 1 respective 2 parts. * * On success, the last component, the patch level, will be returned; * in failure, NULL will be returned. */ static const char * parse_version_string (const char *s, int *major, int *minor, int *micro) { s = parse_version_number (s, major); if (!s) return NULL; if (!minor) { if (*s == '.') s++; } else { if (*s != '.') return NULL; s++; s = parse_version_number (s, minor); if (!s) return NULL; if (!micro) { if (*s == '.') s++; } else { if (*s != '.') return NULL; s++; s = parse_version_number (s, micro); if (!s) return NULL; } } return s; /* patchlevel */ } /* Helper for _gpgrt_cmp_version. */ static int do_cmp_version (const char *a, const char *b, int level) { int a_major, a_minor, a_micro; int b_major, b_minor, b_micro; const char *a_plvl, *b_plvl; int r; int ignore_plvl; int positive, negative; if (level < 0) { positive = -1; negative = 1; level = 0 - level; } else { positive = 1; negative = -1; } if ((ignore_plvl = (level > 9))) level %= 10; a_major = a_minor = a_micro = 0; a_plvl = parse_version_string (a, &a_major, level > 1? &a_minor : NULL, level > 2? &a_micro : NULL); if (!a_plvl) a_major = a_minor = a_micro = 0; /* Error. */ b_major = b_minor = b_micro = 0; b_plvl = parse_version_string (b, &b_major, level > 1? &b_minor : NULL, level > 2? &b_micro : NULL); if (!b_plvl) b_major = b_minor = b_micro = 0; if (!ignore_plvl) { if (!a_plvl && !b_plvl) return negative; /* Put invalid strings at the end. */ if (a_plvl && !b_plvl) return positive; if (!a_plvl && b_plvl) return negative; } if (a_major > b_major) return positive; if (a_major < b_major) return negative; if (a_minor > b_minor) return positive; if (a_minor < b_minor) return negative; if (a_micro > b_micro) return positive; if (a_micro < b_micro) return negative; if (ignore_plvl) return 0; for (; *a_plvl && *b_plvl; a_plvl++, b_plvl++) { if (*a_plvl == '.' && *b_plvl == '.') { r = strcmp (a_plvl, b_plvl); if (!r) return 0; else if ( r > 0 ) return positive; else return negative; } else if (*a_plvl == '.') return negative; /* B is larger. */ else if (*b_plvl == '.') return positive; /* A is larger. */ else if (*a_plvl != *b_plvl) break; } if (*a_plvl == *b_plvl) return 0; else if ((*(signed char *)a_plvl - *(signed char *)b_plvl) > 0) return positive; else return negative; } /* Compare function for version strings. The return value is * like strcmp(). LEVEL may be * 0 - reserved * 1 - format is "". * 2 - format is ".". * 3 - format is "..". * To ignore the patchlevel in the comparison add 10 to LEVEL. To get * a reverse sorting order use a negative number. */ int _gpgrt_cmp_version (const char *a, const char *b, int level) { return do_cmp_version (a, b, level); } /* * Check that the the version of the library is at minimum REQ_VERSION * and return the actual version string; return NULL if the condition * is not met. If NULL is passed to this function, no check is done * and the version string is simply returned. */ const char * _gpg_error_check_version (const char *req_version) { const char *my_version = PACKAGE_VERSION; if (req_version && req_version[0] == 1 && req_version[1] == 1) return cright_blurb (); if (!req_version) return my_version; return _gpgrt_cmp_version (my_version, req_version, 12) >= 0 ? my_version : NULL; }