diff --git a/require-ess/src/require.c b/require-ess/src/require.c
index 43ba75a78a431d4c04319750d5e843e2ec5a60db..c725f37d6e3cdce1d939ce1abac6d0672a0f8d34 100644
--- a/require-ess/src/require.c
+++ b/require-ess/src/require.c
@@ -38,6 +38,7 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd);
 #include <osiFileName.h>
 #include <epicsExport.h>
 
+#include "version.h"
 #include "require.h"
 
 int requireDebug;
@@ -827,12 +828,9 @@ static int compareDigit(int found, int requested, char *name)
  */
 static int compareVersions(const char *found, const char *request, int already_matched)
 {
-    int found_major = 0, found_minor = 0, found_patch = 0, found_parts = 0, found_build = 0;
-    int req_major = 0, req_minor = 0, req_patch = 0, req_parts = 0, req_build = 0;
+    semver_t sv_found, sv_request;
     int match;
 
-    const char *version_string = "%d.%d.%d-%d";
-
     debug("require: compareVersions(found=%s, request=%s)\n", found, request);
 
     if (request == NULL || request[0] == 0)
@@ -846,12 +844,14 @@ static int compareVersions(const char *found, const char *request, int already_m
         return MISMATCH;
     }
 
-    found_parts = sscanf(found, version_string, &found_major, &found_minor, &found_patch, &found_build);
+    /*sv_found = (semver_t *)calloc(1, sizeof(semver_t));
+    sv_request = (semver_t *)calloc(1, sizeof(semver_t));*/
 
-    req_parts = sscanf(request, version_string, &req_major, &req_minor, &req_patch, &req_build);
+    parse_semver(found, &sv_found);
+    parse_semver(request, &sv_request);
 
     // test version, look for exact.
-    if (req_parts == 0 || req_parts == 1 || req_parts == 2)
+    if (strlen(sv_request.test_str) > 0)
     {
         if (strcmp(found, request) == 0)
         {
@@ -859,7 +859,7 @@ static int compareVersions(const char *found, const char *request, int already_m
             return EXACT;
         }
 
-        if (found_parts == 0 || found_parts == 1 || found_parts == 2)
+        if (strlen(sv_found.test_str) > 0)
         {
             debug("require: compareVersions: Test versions requested and found, no match\n");
             return MISMATCH;
@@ -869,24 +869,25 @@ static int compareVersions(const char *found, const char *request, int already_m
         return HIGHER;
     }
 
-    if (found_parts == 0 || found_parts == 1 || found_parts == 2)
+    if (strlen(sv_found.test_str) > 0)
     {
         debug("require: compareVersions: Numeric version requested, test version found\n");
         return MISMATCH;
     }
 
     // At least three digits specifed
-    match = compareDigit(found_major, req_major, "major");
+    match = compareDigit(sv_found.major, sv_request.major, "major");
     if (match != MATCH)
         return match;
-    match = compareDigit(found_minor, req_minor, "minor");
+    match = compareDigit(sv_found.minor, sv_request.minor, "minor");
     if (match != MATCH)
         return match;
-    match = compareDigit(found_patch, req_patch, "patch");
+    match = compareDigit(sv_found.patch, sv_request.patch, "patch");
     if (match != MATCH)
         return match;
 
-    if (req_parts == 3)
+    // build number not specified
+    if (sv_request.build == -1)
     {
         if (already_matched)
         {
@@ -899,7 +900,7 @@ static int compareVersions(const char *found, const char *request, int already_m
             return MATCH;
         }
     }
-    return compareDigit(found_build, req_build, "build");
+    return compareDigit(sv_found.build, sv_request.build, "build");
 }
 
 /* require (module)
diff --git a/require-ess/src/version.c b/require-ess/src/version.c
new file mode 100644
index 0000000000000000000000000000000000000000..c239fbdf4ecebe3a638cf46dc964b60fd3b32331
--- /dev/null
+++ b/require-ess/src/version.c
@@ -0,0 +1,70 @@
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "version.h"
+
+void cleanup_semver(semver_t *s) {
+    free(s->version_str);
+    free(s);
+}
+
+void init_semver(const char *version, semver_t *s) {
+    s->version_str = calloc(strlen(version) + 1, sizeof(char));
+    strcpy(s->version_str, version);
+    s->test_str = "";
+
+    s->major = 0;
+    s->minor = 0;
+    s->patch = 0;
+    s->build = -1;
+}
+
+void fetch_part(char *version_str, regmatch_t *groups, unsigned int ix, int *part) {
+    char tmp;
+
+    tmp = version_str[groups[ix].rm_eo];
+    version_str[groups[ix].rm_eo] = 0;
+    *part = atoi(version_str + groups[ix].rm_so);
+    version_str[groups[ix].rm_eo] = tmp;
+}
+
+int parse_semver(const char *version, semver_t *s) {
+    regex_t compiled;
+    regmatch_t groups[MAX_REGEX_GROUPS];
+
+    init_semver(version, s);
+
+    if (s->version_str == NULL || s->version_str[0] == 0) {
+        return 1;
+    }
+
+    if (regcomp(&compiled, VERSION_REGEX, REG_EXTENDED)) {
+        return 1;
+    }
+
+    if (regexec(&compiled, s->version_str, MAX_REGEX_GROUPS, groups, 0) == 0) {
+        fetch_part(s->version_str, groups, MAJ_IX, &s->major);
+        fetch_part(s->version_str, groups, MIN_IX, &s->minor);
+        fetch_part(s->version_str, groups, PATCH_IX, &s->patch);
+        if (groups[BUILD_IX].rm_so != (size_t)-1) {
+            fetch_part(s->version_str, groups, BUILD_IX, &s->build);
+        }
+        s->test_str = s->version_str + groups[TEST_IX].rm_so;
+    }
+    return 0;
+}
+
+
+/*int main(int argc, char **argv) {
+    if (argc < 1) return 0;
+    if (strlen(argv[1]) == 0) return 0;
+
+    semver_t *s = (semver_t *)calloc(1,sizeof(semver_t));
+
+    parse_semver(argv[1], s);
+    ps("main", s);
+
+    cleanup_semver(s);
+    return 0;
+}*/
\ No newline at end of file
diff --git a/require-ess/src/version.h b/require-ess/src/version.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd251116b9aec1f9e67f9bee50bbaa63f4ad813c
--- /dev/null
+++ b/require-ess/src/version.h
@@ -0,0 +1,23 @@
+#define VERSION_REGEX "^(([0-9]+)\\.([0-9]+)\\.([0-9]+)(\\-([0-9]+))?)?(.*)$"
+#define MAX_REGEX_GROUPS (7+1)
+
+#define MAJ_IX 2
+#define MIN_IX 3
+#define PATCH_IX 4
+#define BUILD_IX 6
+#define TEST_IX 7
+
+typedef struct semver_t {
+    char *version_str;
+    int major;
+    int minor;
+    int patch;
+    int build; // can be negative; implies that build has not been specified
+    char *test_str;
+} semver_t;
+
+void cleanup_semver(semver_t *s);
+
+void init_semver(const char *version, semver_t *s);
+
+int parse_semver(const char *version, semver_t *s);
\ No newline at end of file
diff --git a/require.Makefile b/require.Makefile
index a1acaf6e63f00b635920647bd1ccdf503b9ff172..d07ccfc6f8ede6bcd2435cae8bbb4334af16f8d9 100644
--- a/require.Makefile
+++ b/require.Makefile
@@ -41,6 +41,7 @@ APPSRC := $(APP)/src
 APPDB := $(APP)/Db
 
 SOURCES += $(APPSRC)/require.c
+SOURCES += $(APPSRC)/version.c
 DBDS    += $(APPSRC)/require.dbd
 SOURCES += $(APPSRC)/runScript.c
 DBDS    += $(APPSRC)/runScript.dbd