cryptutils

Cryptographic Software Utilities
git clone http://git.omkov.net/cryptutils
Log | Tree | Refs | README | LICENCE | Download

AuthorJamozed <[email protected]>
Date2022-02-05 06:04:03
Commitc01a723a048b012ff1903cad9bbbac032f37ef5a
Parent08f264bf78304406c1a9af2d93f2dc8121c9760a

util: Replace "lib" with libutil

Diffstat

M CMakeLists.txt | 5 +++--
M src/alder32.c | 4 ++--
M src/crc32.c | 4 +++-
M src/fnv1a64.c | 3 +--
R src/lib/base32.c -> src/util/base32.c | 4 ++--
R src/lib/base32.h -> src/util/base32.h | 10 +++++-----
R src/lib/error.c -> src/util/error.c | 17 ++++++++++++-----
R src/lib/error.h -> src/util/rc2.h | 24 +++++++++++-------------
R src/lib/optget.c -> src/util/optget.c | 3 ++-
R src/lib/optget.h -> src/util/optget.h | 10 +++++-----
R src/lib/strconv.c -> src/util/strtou.c | 38 ++++++++++++++++++++------------------
R src/lib/strconv.h -> src/util/strconv.h | 25 ++++++++++++++++---------
M src/otp.c | 7 +++----
A src/util/crypt.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
A src/util/endian.h | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/util/error.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/util/rc2.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/util/strtos.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A src/util/util.h | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

19 files changed, 786 insertions, 69 deletions

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6de026e..759df8a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,11 +7,11 @@ SET(CMAKE_STATIC_LIBRARY_PREFIX "")
 
 FIND_PACKAGE(OpenSSL REQUIRED)
 
-FILE(GLOB LIB ${PROJECT_SOURCE_DIR}/src/lib/*)
+FILE(GLOB SRC_UTIL ${PROJECT_SOURCE_DIR}/src/util/*)
 
-ADD_LIBRARY(lib STATIC ${LIB})
+ADD_LIBRARY(libutil STATIC ${SRC_UTIL})
 
-LINK_LIBRARIES(lib)
+LINK_LIBRARIES(libutil)
 
 ADD_EXECUTABLE(alder32 ${PROJECT_SOURCE_DIR}/src/alder32.c)
 ADD_EXECUTABLE(crc32   ${PROJECT_SOURCE_DIR}/src/crc32.c)
diff --git a/src/alder32.c b/src/alder32.c
index 55ad113..ac1bb6f 100644
--- a/src/alder32.c
+++ b/src/alder32.c
@@ -30,8 +30,8 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#include "lib/error.c"
-#include "lib/optget.h"
+#include "util/error.c"
+#include "util/optget.h"
 
 #include <stdbool.h>
 #include <stdint.h>
diff --git a/src/crc32.c b/src/crc32.c
index 75f9c95..93c5fa8 100644
--- a/src/crc32.c
+++ b/src/crc32.c
@@ -30,8 +30,8 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#include "lib/error.c"
-#include "lib/optget.h"
+#include "util/error.c"
+#include "util/optget.h"
 
 #include <stdbool.h>
 #include <stdint.h>
diff --git a/src/fnv1a64.c b/src/fnv1a64.c
index 5ae0845..565ba71 100644
--- a/src/fnv1a64.c
+++ b/src/fnv1a64.c
@@ -30,8 +30,8 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#include "lib/error.c"
-#include "lib/optget.h"
+#include "util/error.c"
+#include "util/optget.h"
 
 #include <stdbool.h>
 #include <stdint.h>
diff --git a/src/otp.c b/src/otp.c
index f4b5fc5..62c1e49 100644
--- a/src/otp.c
+++ b/src/otp.c
@@ -30,10 +30,10 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#include "lib/base32.h"
-#include "lib/error.c"
-#include "lib/optget.h"
-#include "lib/strconv.h"
+#include "util/base32.h"
+#include "util/error.c"
+#include "util/optget.h"
+#include "util/strconv.h"
 
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
diff --git a/src/lib/base32.c b/src/util/base32.c
similarity index 98%
rename from src/lib/base32.c
rename to src/util/base32.c
index 76ed533..09af30c 100644
--- a/src/lib/base32.c
+++ b/src/util/base32.c
@@ -1,5 +1,5 @@
-// base32.c, version 1.0.2
-// Base32 source file for OMKOV lib
+// util/base32.c, version 1.0.3
+// Base32 source file from libutil
 // Copyright (C) 2021, Jakob Wakeling
 // All rights reserved.
 
diff --git a/src/lib/base32.h b/src/util/base32.h
similarity index 91%
rename from src/lib/base32.h
rename to src/util/base32.h
index 3cc2f08..98fc671 100644
--- a/src/lib/base32.h
+++ b/src/util/base32.h
@@ -1,5 +1,5 @@
-// base32.h, version 1.0.2
-// Base32 header file for OMKOV lib
+// util/base32.h, version 1.0.3
+// Base32 header file from libutil
 // Copyright (C) 2021, Jakob Wakeling
 // All rights reserved.
 
@@ -30,8 +30,8 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#ifndef OMKOV_LIB_BASE32_H_2PSMZTB0
-#define OMKOV_LIB_BASE32_H_2PSMZTB0
+#ifndef UTIL_BASE32_H_2PSMZTB0
+#define UTIL_BASE32_H_2PSMZTB0
 
 #include <stdint.h>
 #include <stdlib.h>
@@ -42,4 +42,4 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 extern size_t b32encode(uint8_t *dst, uint8_t *src, size_t len);
 extern size_t b32decode(uint8_t *dst, uint8_t *src, size_t len);
 
-#endif // OMKOV_LIB_BASE32_H_2PSMZTB0
+#endif // UTIL_BASE32_H_2PSMZTB0
diff --git a/src/util/crypt.h b/src/util/crypt.h
new file mode 100644
index 0000000..29a2cff
--- /dev/null
+++ b/src/util/crypt.h
@@ -0,0 +1,50 @@
+// util/crypt.h, version 0.1.1
+// Crypt header file from libutil
+// Copyright (C) 2021, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef UTIL_CRYPT_H_RDY6J5JV
+#define UTIL_CRYPT_H_RDY6J5JV
+
+#include <stdint.h>
+
+/* Circular shift left */
+#define ROL8(x, n)   (uint8_t)(((x) << (n)) | ((x) >>  (8 - (n))))
+#define ROL16(x, n) (uint16_t)(((x) << (n)) | ((x) >> (16 - (n))))
+#define ROL32(x, n) (uint32_t)(((x) << (n)) | ((x) >> (32 - (n))))
+#define ROL64(x, n) (uint64_t)(((x) << (n)) | ((x) >> (64 - (n))))
+
+/* Circular shift right */
+#define ROR8(x, n)   (uint8_t)(((x) >> (n)) | ((x) <<  (8 - (n))))
+#define ROR16(x, n) (uint16_t)(((x) >> (n)) | ((x) << (16 - (n))))
+#define ROR32(x, n) (uint32_t)(((x) >> (n)) | ((x) << (32 - (n))))
+#define ROR64(x, n) (uint64_t)(((x) >> (n)) | ((x) << (64 - (n))))
+
+#endif // UTIL_CRYPT_H_RDY6J5JV
diff --git a/src/util/endian.h b/src/util/endian.h
new file mode 100644
index 0000000..96b90b3
--- /dev/null
+++ b/src/util/endian.h
@@ -0,0 +1,153 @@
+// util/endian.h, version 1.0.1
+// Endian header file from libutil
+// Copyright (C) 2021, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef UTIL_ENDIAN_H_G7AID2RQ
+#define UTIL_ENDIAN_H_G7AID2RQ
+
+#include <stdint.h>
+
+/* Byte swap a 16-bit integer */
+#define BSWAP16(x) ( \
+	(((uint16_t)(x) & 0xFF00u) >> 8) | \
+	(((uint16_t)(x) & 0x00FFu) << 8))
+
+/* Byte swap a 32-bit integer */
+#define BSWAP32(x) ( \
+	(((uint32_t)(x) & 0xFF000000uL) >> 24) | \
+	(((uint32_t)(x) & 0x00FF0000uL) >>  8) | \
+	(((uint32_t)(x) & 0x0000FF00uL) <<  8) | \
+	(((uint32_t)(x) & 0x000000FFuL) << 24))
+
+/* Byte swap a 64-bit integer */
+#define BSWAP64(x) ( \
+	(((uint64_t)(x) & 0xFF00000000000000uLL) >> 56) | \
+	(((uint64_t)(x) & 0x00FF000000000000uLL) >> 40) | \
+	(((uint64_t)(x) & 0x0000FF0000000000uLL) >> 24) | \
+	(((uint64_t)(x) & 0x000000FF00000000uLL) >>  8) | \
+	(((uint64_t)(x) & 0x00000000FF000000uLL) <<  8) | \
+	(((uint64_t)(x) & 0x0000000000FF0000uLL) << 24) | \
+	(((uint64_t)(x) & 0x000000000000FF00uLL) << 40) | \
+	(((uint64_t)(x) & 0x00000000000000FFuLL) << 56))
+
+/* Load a 16-bit little endian integer */
+#define LD16LE(p) ( \
+	((uint16_t)(((uint8_t *)(p))[0]) << 0) | \
+	((uint16_t)(((uint8_t *)(p))[1]) << 8))
+
+/* Load a 16-bit big endian integer */
+#define LD16BE(p) ( \
+	((uint16_t)(((uint8_t *)(p))[0]) << 8) | \
+	((uint16_t)(((uint8_t *)(p))[1]) << 0))
+
+/* Load a 32-bit little endian integer */
+#define LD32LE(p) ( \
+	((uint32_t)(((uint8_t *)(p))[0]) <<  0) | \
+	((uint32_t)(((uint8_t *)(p))[1]) <<  8) | \
+	((uint32_t)(((uint8_t *)(p))[2]) << 16) | \
+	((uint32_t)(((uint8_t *)(p))[3]) << 24))
+
+/* Load a 32-bit big endian integer */
+#define LD32BE(p) ( \
+	((uint32_t)(((uint8_t *)(p))[0]) << 24) | \
+	((uint32_t)(((uint8_t *)(p))[1]) << 16) | \
+	((uint32_t)(((uint8_t *)(p))[2]) <<  8) | \
+	((uint32_t)(((uint8_t *)(p))[3]) <<  0))
+
+/* Load a 64-bit little endian integer */
+#define LD64LE(p) ( \
+	((uint64_t)(((uint8_t *)(p))[0]) <<  0) | \
+	((uint64_t)(((uint8_t *)(p))[1]) <<  8) | \
+	((uint64_t)(((uint8_t *)(p))[2]) << 16) | \
+	((uint64_t)(((uint8_t *)(p))[3]) << 24) | \
+	((uint64_t)(((uint8_t *)(p))[4]) << 32) | \
+	((uint64_t)(((uint8_t *)(p))[5]) << 40) | \
+	((uint64_t)(((uint8_t *)(p))[6]) << 48) | \
+	((uint64_t)(((uint8_t *)(p))[7]) << 56))
+
+/* Load a 64-bit big endian integer */
+#define LD64BE(p) ( \
+	((uint64_t)(((uint8_t *)(p))[0]) << 56) | \
+	((uint64_t)(((uint8_t *)(p))[1]) << 48) | \
+	((uint64_t)(((uint8_t *)(p))[2]) << 40) | \
+	((uint64_t)(((uint8_t *)(p))[3]) << 32) | \
+	((uint64_t)(((uint8_t *)(p))[4]) << 24) | \
+	((uint64_t)(((uint8_t *)(p))[5]) << 16) | \
+	((uint64_t)(((uint8_t *)(p))[6]) <<  8) | \
+	((uint64_t)(((uint8_t *)(p))[7]) <<  0))
+
+/* Store a 16-bit little endian integer */
+#define ST16LE(p, x) \
+	((uint8_t *)(p))[0] = ((uint16_t)(x) >> 0) & 0xFFu; \
+	((uint8_t *)(p))[1] = ((uint16_t)(x) >> 8) & 0xFFu
+
+/* Store a 16-bit big endian integer */
+#define ST16BE(p, x) \
+	((uint8_t *)(p))[0] = ((uint16_t)(x) >> 8) & 0xFFu; \
+	((uint8_t *)(p))[1] = ((uint16_t)(x) >> 0) & 0xFFu
+
+/* Store a 32-bit little endian integer */
+#define ST32LE(p, x) \
+	((uint8_t *)(p))[0] = ((uint32_t)(x) >>  0) & 0xFFu; \
+	((uint8_t *)(p))[1] = ((uint32_t)(x) >>  8) & 0xFFu; \
+	((uint8_t *)(p))[2] = ((uint32_t)(x) >> 16) & 0xFFu; \
+	((uint8_t *)(p))[3] = ((uint32_t)(x) >> 24) & 0xFFu
+
+/* Store a 32-bit big endian integer */
+#define ST32BE(p, x) \
+	((uint8_t *)(p))[0] = ((uint32_t)(x) >> 24) & 0xFFu; \
+	((uint8_t *)(p))[1] = ((uint32_t)(x) >> 16) & 0xFFu; \
+	((uint8_t *)(p))[2] = ((uint32_t)(x) >>  8) & 0xFFu; \
+	((uint8_t *)(p))[3] = ((uint32_t)(x) >>  0) & 0xFFu
+
+/* Store a 64-bit little endian integer */
+#define ST64LE(p, x) \
+	((uint8_t *)(p))[0] = ((uint64_t)(x) >>  0) & 0xFFu; \
+	((uint8_t *)(p))[1] = ((uint64_t)(x) >>  8) & 0xFFu; \
+	((uint8_t *)(p))[2] = ((uint64_t)(x) >> 16) & 0xFFu; \
+	((uint8_t *)(p))[3] = ((uint64_t)(x) >> 24) & 0xFFu; \
+	((uint8_t *)(p))[4] = ((uint64_t)(x) >> 32) & 0xFFu; \
+	((uint8_t *)(p))[5] = ((uint64_t)(x) >> 40) & 0xFFu; \
+	((uint8_t *)(p))[6] = ((uint64_t)(x) >> 48) & 0xFFu; \
+	((uint8_t *)(p))[7] = ((uint64_t)(x) >> 56) & 0xFFu
+
+/* Store a 64-bit big endian integer */
+#define ST64BE(p, x) \
+	((uint8_t *)(p))[0] = ((uint64_t)(x) >> 56) & 0xFFu; \
+	((uint8_t *)(p))[1] = ((uint64_t)(x) >> 48) & 0xFFu; \
+	((uint8_t *)(p))[2] = ((uint64_t)(x) >> 40) & 0xFFu; \
+	((uint8_t *)(p))[3] = ((uint64_t)(x) >> 32) & 0xFFu; \
+	((uint8_t *)(p))[4] = ((uint64_t)(x) >> 24) & 0xFFu; \
+	((uint8_t *)(p))[5] = ((uint64_t)(x) >> 16) & 0xFFu; \
+	((uint8_t *)(p))[6] = ((uint64_t)(x) >>  8) & 0xFFu; \
+	((uint8_t *)(p))[7] = ((uint64_t)(x) >>  0) & 0xFFu
+
+#endif // UTIL_ENDIAN_H_G7AID2RQ
diff --git a/src/lib/error.c b/src/util/error.c
similarity index 81%
rename from src/lib/error.c
rename to src/util/error.c
index e0c80d3..0e306e9 100644
--- a/src/lib/error.c
+++ b/src/util/error.c
@@ -1,5 +1,5 @@
-// error.c, version 1.0.2
-// Error source file for OMKOV lib
+// util/error.h, version 1.1.1
+// Error source file from libutil
 // Copyright (C) 2020, Jakob Wakeling
 // All rights reserved.
 
@@ -43,19 +43,26 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 char *A0 = NULL;
 bool warned = false;
 
-/* Print an error message and exit */
+/* Print an error message and exit. */
 noreturn void error(int status, const char *format, ...) {
 	fflush(stdout); if (A0) { fputs(A0, stderr); fputs(": ", stderr); }
 	va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap);
 	fputc('\n', stderr); exit(status);
 }
 
-/* Print a warning message and set the warned flag */
+/* Print a warning message and set the warned flag. */
 void warn(const char *format, ...) {
 	fflush(stdout); if (A0) { fputs(A0, stderr); fputs(": ", stderr); }
 	va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap);
 	fputc('\n', stderr); warned = true; return;
 }
 
-/* Shorthand for strerror(errno) */
+/* Print a warning message but do not set the warned flag. */
+void alert(const char *format, ...) {
+	fflush(stdout); if (A0) { fputs(A0, stderr); fputs(": ", stderr); }
+	va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap);
+	fputc('\n', stderr); return;
+}
+
+/* Shorthand for strerror(errno). DEPRECIATED, use the SERR macro. */
 char *serr(void) { return strerror(errno); }
diff --git a/src/util/error.h b/src/util/error.h
new file mode 100644
index 0000000..794e5c3
--- /dev/null
+++ b/src/util/error.h
@@ -0,0 +1,87 @@
+// util/error.h, version 1.1.1
+// Error header file from libutil
+// Copyright (C) 2020, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef UTIL_ERROR_H_38W06M3W
+#define UTIL_ERROR_H_38W06M3W
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdnoreturn.h>
+#include <string.h>
+
+/* Warn and then return status */
+#define WARN_R(status, format, ...) do { \
+	warn(format, __VA_ARGS__); return status; \
+} while (0)
+
+/* Warn and then reset errno */
+#define WARN_E(format, ...) do { \
+	warn(format, __VA_ARGS__); errno = 0; \
+} while (0)
+
+/* Warn, reset errno, and then return status */
+#define WARN_RE(status, format, ...) do { \
+	warn(format, __VA_ARGS__); errno = 0; return status; \
+} while (0)
+
+/* Alert and then return status */
+#define ALERT_R(status, format, ...) do { \
+	alert(format, __VA_ARGS__); return status; \
+} while (0)
+
+/* Alert and then reset errno */
+#define ALERT_E(format, ...) do { \
+	alert(format, __VA_ARGS__); errno = 0; \
+} while (0)
+
+/* Alert, reset errno, and then return status */
+#define ALERT_RE(status, format, ...) do { \
+	alert(format, __VA_ARGS__); errno = 0; return status; \
+} while (0)
+
+/* Shorthand for strerror(serrno). */
+#define SERR (strerror(errno))
+
+extern char *A0;
+extern bool warned;
+
+/* Print an error message and exit. */
+extern noreturn void error(int status, const char *format, ...);
+/* Print a warning message and set the warned flag. */
+extern void warn(const char *format, ...);
+/* Print a warning message but do not set the warned flag. */
+extern void alert(const char *format, ...);
+
+/* Shorthand for strerror(errno). DEPRECIATED, use the SERR macro. */
+extern char *serr(void);
+
+#endif // UTIL_ERROR_H_38W06M3W
diff --git a/src/lib/optget.c b/src/util/optget.c
similarity index 98%
rename from src/lib/optget.c
rename to src/util/optget.c
index da61ebd..24d334d 100644
--- a/src/lib/optget.c
+++ b/src/util/optget.c
@@ -1,5 +1,5 @@
-// optget.h, version 1.6.0
-// optget source file for OMKOV lib
+// util/optget.h, version 1.6.1
+// optget source file from libutil
 // Copyright (C) 2020, Jakob Wakeling
 // All rights reserved.
 
diff --git a/src/lib/optget.h b/src/util/optget.h
similarity index 91%
rename from src/lib/optget.h
rename to src/util/optget.h
index f83d132..e3065fb 100644
--- a/src/lib/optget.h
+++ b/src/util/optget.h
@@ -1,5 +1,5 @@
-// optget.h, version 1.6.0
-// optget header file for OMKOV lib
+// util/optget.h, version 1.6.1
+// optget header file from libutil
 // Copyright (C) 2020, Jakob Wakeling
 // All rights reserved.
 
@@ -30,8 +30,8 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#ifndef OMKOV_LIB_OPTGET_H_W3LIZK1S
-#define OMKOV_LIB_OPTGET_H_W3LIZK1S
+#ifndef UTIL_OPTGET_H_W3LIZK1S
+#define UTIL_OPTGET_H_W3LIZK1S
 
 #define ARG_NUL 0
 #define ARG_REQ 1
@@ -52,4 +52,4 @@ extern const struct opt OPTGET_INIT;
 
 extern int optget(struct opt *opt, char *av[], int flags);
 
-#endif // OMKOV_LIB_OPTGET_H_W3LIZK1S
+#endif // UTIL_OPTGET_H_W3LIZK1S
diff --git a/src/util/rc2.c b/src/util/rc2.c
new file mode 100644
index 0000000..104b8c9
--- /dev/null
+++ b/src/util/rc2.c
@@ -0,0 +1,154 @@
+// util/rc2.c, version 1.0.0
+// RC2 source file from libutil
+// Copyright (C) 2021, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "rc2.h"
+
+#include "crypt.h"
+#include "endian.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static inline bool isle() { uint16_t e = 0x00FF; return *(uint8_t *)&e; }
+static inline bool isbe() { uint16_t e = 0xFF00; return *(uint8_t *)&e; }
+
+static const uint8_t RC2[];
+
+/* Expand a user key to a 128-byte RC2 key. */
+void rc2expand(struct rc2 *ctx, const uint8_t *key, int len, int ekl) {
+	assert(len >= 1 && len <= 128); assert(ekl <= 1024);
+	memcpy(ctx->l, key, len);
+	
+	int t8 = (ekl + 7) / 8, tm = 0xFF >> (8 * t8 - ekl);
+	
+	// First key expansion loop
+	for (int i = len; i != 128; ++i) {
+		ctx->l[i] = RC2[(ctx->l[i - 1] + ctx->l[i - len]) & 0xFF];
+	}
+	
+	// Intermediate step
+	ctx->l[128 - t8] = RC2[ctx->l[128 - t8] & tm];
+	
+	// Second key expansion loop
+	for (int i = 128 - t8; i != 0; --i) {
+		ctx->l[i - 1] = RC2[ctx->l[i] ^ ctx->l[i + t8 - 1]];
+	}
+	
+	// If CPU is big-endian, then byte swap key
+	if (isbe()) for (int i = 0; i != 64; ++i) {
+		ctx->k[i] = BSWAP16(ctx->k[i]);
+	}
+	
+	return;
+}
+
+/* Encrypt an 8-byte block using RC2 */
+void rc2encrypt(struct rc2 *ctx, uint8_t *in, uint8_t *out) {
+	uint16_t r0 = LD16LE(in);
+	uint16_t r1 = LD16LE(in + 2);
+	uint16_t r2 = LD16LE(in + 4);
+	uint16_t r3 = LD16LE(in + 6);
+	
+	for (int i = 0; i != 16; ++i) {
+		// Perform mixing round
+		r0 += ctx->k[i * 4]     + (r3 & r2) + (~r3 & r1); r0 = ROL16(r0, 1);
+		r1 += ctx->k[i * 4 + 1] + (r0 & r3) + (~r0 & r2); r1 = ROL16(r1, 2);
+		r2 += ctx->k[i * 4 + 2] + (r1 & r0) + (~r1 & r3); r2 = ROL16(r2, 3);
+		r3 += ctx->k[i * 4 + 3] + (r2 & r1) + (~r2 & r0); r3 = ROL16(r3, 5);
+		
+		// Perform mashing round for rounds 5 and 11
+		if (i == 4 || i == 10) {
+			r0 += ctx->k[r3 & 63]; r1 += ctx->k[r0 & 63];
+			r2 += ctx->k[r1 & 63]; r3 += ctx->k[r2 & 63];
+		}
+	}
+	
+	ST16LE(out, r0);
+	ST16LE(out + 2, r1);
+	ST16LE(out + 4, r2);
+	ST16LE(out + 6, r3);
+}
+
+/* Decrypt an 8-byte block using RC2 */
+void rc2decrypt(struct rc2 *ctx, uint8_t *in, uint8_t *out) {
+	uint16_t r0 = LD16LE(in);
+	uint16_t r1 = LD16LE(in + 2);
+	uint16_t r2 = LD16LE(in + 4);
+	uint16_t r3 = LD16LE(in + 6);
+	
+	for (int i = 15; i >= 0; --i) {
+		// Perform r-mixing round
+		r3 = ROR16(r3, 5); r3 -= ctx->k[i * 4 + 3] + (r2 & r1) + (~r2 & r0);
+		r2 = ROR16(r2, 3); r2 -= ctx->k[i * 4 + 2] + (r1 & r0) + (~r1 & r3);
+		r1 = ROR16(r1, 2); r1 -= ctx->k[i * 4 + 1] + (r0 & r3) + (~r0 & r2);
+		r0 = ROR16(r0, 1); r0 -= ctx->k[i * 4]     + (r3 & r2) + (~r3 & r1);
+		
+		// Perform r-mashing round for rounds 5 and 11
+		if (i == 5 || i == 11) {
+			r3 -= ctx->k[r2 & 63]; r2 -= ctx->k[r1 & 63];
+			r1 -= ctx->k[r0 & 63]; r0 -= ctx->k[r3 & 63];
+		}
+	}
+	
+	ST16LE(out, r0);
+	ST16LE(out + 2, r1);
+	ST16LE(out + 4, r2);
+	ST16LE(out + 6, r3);
+}
+
+static const uint8_t RC2[] = {
+	0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, 0x28, 0xE9, 0xFD, 0x79,
+	0x4A, 0xA0, 0xD8, 0x9D, 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
+	0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, 0x17, 0x9A, 0x59, 0xF5,
+	0x87, 0xB3, 0x4F, 0x13, 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
+	0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, 0xF0, 0x95, 0x21, 0x22,
+	0x5C, 0x6B, 0x4E, 0x82, 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
+	0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, 0x12, 0x75, 0xCA, 0x1F,
+	0x3B, 0xBE, 0xE4, 0xD1, 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
+	0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, 0x27, 0xF2, 0x1D, 0x9B,
+	0xBC, 0x94, 0x43, 0x03, 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
+	0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, 0x08, 0xE8, 0xEA, 0xDE,
+	0x80, 0x52, 0xEE, 0xF7, 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
+	0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, 0x4B, 0x9F, 0xD0, 0x5E,
+	0x04, 0x18, 0xA4, 0xEC, 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
+	0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, 0x99, 0x7C, 0x3A, 0x85,
+	0x23, 0xB8, 0xB4, 0x7A, 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
+	0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, 0x05, 0xDF, 0x29, 0x10,
+	0x67, 0x6C, 0xBA, 0xC9, 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
+	0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, 0x0D, 0x38, 0x34, 0x1B,
+	0xAB, 0x33, 0xFF, 0xB0, 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
+	0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, 0x0A, 0xA6, 0x20, 0x68,
+	0xFE, 0x7F, 0xC1, 0xAD
+};
diff --git a/src/lib/error.h b/src/util/rc2.h
similarity index 75%
rename from src/lib/error.h
rename to src/util/rc2.h
index 16db9cf..ee6ef1e 100644
--- a/src/lib/error.h
+++ b/src/util/rc2.h
@@ -1,6 +1,6 @@
-// error.h, version 1.0.2
-// Error header file for OMKOV lib
-// Copyright (C) 2020, Jakob Wakeling
+// util/rc2.h, version 1.0.0
+// RC2 header file from libutil
+// Copyright (C) 2021, Jakob Wakeling
 // All rights reserved.
 
 /*
@@ -30,18 +30,16 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#ifndef OMKOV_LIB_ERROR_H_38W06M3W
-#define OMKOV_LIB_ERROR_H_38W06M3W
+#ifndef UTIL_RC2_H_PMXL29JH
+#define UTIL_RC2_H_PMXL29JH
 
-#include <stdbool.h>
-#include <stdnoreturn.h>
+#include <stdint.h>
+#include <stdlib.h>
 
-extern char *A0;
-extern bool warned;
+struct rc2 { union { uint16_t k[64]; uint8_t l[128]; }; };
 
-extern noreturn void error(int status, const char *format, ...);
-extern void warn(const char *format, ...);
+extern void rc2expand(struct rc2 *ctx, const uint8_t *key, int len, int ekl);
+extern void rc2encrypt(struct rc2 *ctx, uint8_t *in, uint8_t *out);
+extern void rc2decrypt(struct rc2 *ctx, uint8_t *in, uint8_t *out);
 
-extern char *serr(void);
-
-#endif // OMKOV_LIB_ERROR_H_38W06M3W
+#endif // UTIL_RC2_H_PMXL29JH
diff --git a/src/lib/strconv.h b/src/util/strconv.h
similarity index 66%
rename from src/lib/strconv.h
rename to src/util/strconv.h
index 182e322..5901fcb 100644
--- a/src/lib/strconv.h
+++ b/src/util/strconv.h
@@ -1,6 +1,6 @@
-// strconv.h, version 1.0.0
-// String conversion header file for OMKOV lib
-// Copyright (C) 2020, Jakob Wakeling
+// util/strconv.h, version 1.1.2
+// String conversion header file from libutil
+// Copyright (C) 2021, Jakob Wakeling
 // All rights reserved.
 
 /*
@@ -30,14 +30,19 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-#ifndef OMKOV_LIB_STRING_H_3EQMSZZK
-#define OMKOV_LIB_STRING_H_3EQMSZZK
+#ifndef UTIL_STRCONV_H_3EQMSZZK
+#define UTIL_STRCONV_H_3EQMSZZK
 
-#include <stdint.h>
+#include "util.h"
 
-uint8_t strtou8(const char *nptr, char **endptr, register int base);
-uint16_t strtou16(const char *nptr, char **endptr, register int base);
-uint32_t strtou32(const char *nptr, char **endptr, register int base);
-uint64_t strtou64(const char *nptr, char **endptr, register int base);
+s8 strtos8(const char *nptr, char **endptr, register int base);
+s16 strtos16(const char *nptr, char **endptr, register int base);
+s32 strtos32(const char *nptr, char **endptr, register int base);
+s64 strtos64(const char *nptr, char **endptr, register int base);
 
-#endif // OMKOV_LIB_STRING_H_3EQMSZZK
+u8 strtou8(const char *nptr, char **endptr, register int base);
+u16 strtou16(const char *nptr, char **endptr, register int base);
+u32 strtou32(const char *nptr, char **endptr, register int base);
+u64 strtou64(const char *nptr, char **endptr, register int base);
+
+#endif // UTIL_STRCONV_H_3EQMSZZK
diff --git a/src/util/strtos.c b/src/util/strtos.c
new file mode 100644
index 0000000..c09c2e3
--- /dev/null
+++ b/src/util/strtos.c
@@ -0,0 +1,165 @@
+// util/strtos.c, version 1.1.2
+// String conversion source file from libutil
+// Copyright (C) 2021, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#include "util.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+
+/* Convert a string to a signed 8-bit integer */
+s8 strtos8(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u8 i = 0, c; bool neg = false;
+	
+	for (; isspace(*s); ++s);
+	
+	if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; }
+	
+	if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+		s += 2; base = 16;
+	}
+	else if (base == 0) { base = s[0] == '0' ? 8 : 10; }
+	else if (base < 2 || base > 36) { errno = EINVAL; goto end; }
+	
+	for (;; ++s) {
+		if (*s >= '0' && *s <= '9') { c = *s - '0'; }
+		else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); }
+		else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); }
+		else { break; }
+		
+		if (c >= base) { break; }
+		if (i > (neg ? -(S8_MIN + 1) : S8_MAX - c) / base) {
+			errno = ERANGE; i = neg ? S8_MIN : S8_MAX; goto end;
+		}
+		
+		i = i * base + c;
+	}
+	
+end:;
+	if (endptr) { *endptr = (char *)s; } return neg ? -i : i;
+}
+
+/* Convert a string to a signed 16-bit integer */
+s16 strtos16(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u16 i = 0, c; bool neg = false;
+	
+	for (; isspace(*s); ++s);
+	
+	if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; }
+	
+	if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+		s += 2; base = 16;
+	}
+	else if (base == 0) { base = s[0] == '0' ? 8 : 10; }
+	else if (base < 2 || base > 36) { errno = EINVAL; goto end; }
+	
+	for (;; ++s) {
+		if (*s >= '0' && *s <= '9') { c = *s - '0'; }
+		else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); }
+		else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); }
+		else { break; }
+		
+		if (c >= base) { break; }
+		if (i > (neg ? -(S16_MIN + 1) : S16_MAX - c) / base) {
+			errno = ERANGE; i = neg ? S16_MIN : S16_MAX; goto end;
+		}
+		
+		i = i * base + c;
+	}
+	
+end:;
+	if (endptr) { *endptr = (char *)s; } return neg ? -i : i;
+}
+
+/* Convert a string to a signed 32-bit integer */
+s32 strtos32(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u32 i = 0, c; bool neg = false;
+	
+	for (; isspace(*s); ++s);
+	
+	if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; }
+	
+	if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+		s += 2; base = 16;
+	}
+	else if (base == 0) { base = s[0] == '0' ? 8 : 10; }
+	else if (base < 2 || base > 36) { errno = EINVAL; goto end; }
+	
+	for (;; ++s) {
+		if (*s >= '0' && *s <= '9') { c = *s - '0'; }
+		else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); }
+		else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); }
+		else { break; }
+		
+		if (c >= base) { break; }
+		if (i > (neg ? -(S32_MIN + 1) : S32_MAX - c) / base) {
+			errno = ERANGE; i = neg ? S32_MIN : S32_MAX; goto end;
+		}
+		
+		i = i * base + c;
+	}
+	
+end:;
+	if (endptr) { *endptr = (char *)s; } return neg ? -i : i;
+}
+
+/* Convert a string to a signed 64-bit integer */
+s64 strtos64(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u64 i = 0, c; bool neg = false;
+	
+	for (; isspace(*s); ++s);
+	
+	if (*s == '+') { ++s; } else if (*s == '-') { ++s; neg = true; }
+	
+	if ((!base || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+		s += 2; base = 16;
+	}
+	else if (base == 0) { base = s[0] == '0' ? 8 : 10; }
+	else if (base < 2 || base > 36) { errno = EINVAL; goto end; }
+	
+	for (;; ++s) {
+		if (*s >= '0' && *s <= '9') { c = *s - '0'; }
+		else if (*s >= 'A' && *s <= 'Z') { c = *s - ('A' - 10); }
+		else if (*s >= 'a' && *s <= 'z') { c = *s - ('a' - 10); }
+		else { break; }
+		
+		if (c >= base) { break; }
+		if (i > (neg ? -(S64_MIN + 1) : S64_MAX - c) / base) {
+			errno = ERANGE; i = neg ? S64_MIN : S64_MAX; goto end;
+		}
+		
+		i = i * base + c;
+	}
+	
+end:;
+	if (endptr) { *endptr = (char *)s; } return neg ? -i : i;
+}
diff --git a/src/lib/strconv.c b/src/util/strtou.c
similarity index 82%
rename from src/lib/strconv.c
rename to src/util/strtou.c
index 7e9afa8..e865866 100644
--- a/src/lib/strconv.c
+++ b/src/util/strtou.c
@@ -1,5 +1,5 @@
-// strconv.c, version 1.0.0
-// String conversion source file for OMKOV lib
+// util/strtou.c, version 1.1.2
+// String conversion source file from libutil
 // Copyright (C) 2021, Jakob Wakeling
 // All rights reserved.
 
@@ -30,13 +30,14 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
+#include "util.h"
+
 #include <ctype.h>
 #include <errno.h>
-#include <stdint.h>
 
 /* Convert a string to an unsigned 8-bit integer */
-uint8_t strtou8(const char *nptr, char **endptr, register int base) {
-	register const char *s = nptr; register uint8_t i = 0, c;
+u8 strtou8(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u8 i = 0, c;
 
 	for (; isspace(*s); ++s);
 
@@ -55,8 +56,8 @@ uint8_t strtou8(const char *nptr, char **endptr, register int base) {
 		else { break; }
 
 		if (c >= base) { break; }
-		if (i > (UINT8_MAX - c) / base) {
-			errno = ERANGE; i = UINT8_MAX; goto end;
+		if (i > (U8_MAX - c) / base) {
+			errno = ERANGE; i = U8_MAX; goto end;
 		}
 
 		i = i * base + c;
@@ -67,8 +68,8 @@ end:;
 }
 
 /* Convert a string to an unsigned 16-bit integer */
-uint16_t strtou16(const char *nptr, char **endptr, register int base) {
-	register const char *s = nptr; register uint16_t i = 0, c;
+u16 strtou16(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u16 i = 0, c;
 
 	for (; isspace(*s); ++s);
 
@@ -87,8 +88,8 @@ uint16_t strtou16(const char *nptr, char **endptr, register int base) {
 		else { break; }
 
 		if (c >= base) { break; }
-		if (i > (UINT16_MAX - c) / base) {
-			errno = ERANGE; i = UINT16_MAX; goto end;
+		if (i > (U16_MAX - c) / base) {
+			errno = ERANGE; i = U16_MAX; goto end;
 		}
 
 		i = i * base + c;
@@ -99,8 +100,8 @@ end:;
 }
 
 /* Convert a string to an unsigned 32-bit integer */
-uint32_t strtou32(const char *nptr, char **endptr, register int base) {
-	register const char *s = nptr; register uint32_t i = 0, c;
+u32 strtou32(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u32 i = 0, c;
 
 	for (; isspace(*s); ++s);
 
@@ -119,8 +120,8 @@ uint32_t strtou32(const char *nptr, char **endptr, register int base) {
 		else { break; }
 
 		if (c >= base) { break; }
-		if (i > (UINT32_MAX - c) / base) {
-			errno = ERANGE; i = UINT32_MAX; goto end;
+		if (i > (U32_MAX - c) / base) {
+			errno = ERANGE; i = U32_MAX; goto end;
 		}
 
 		i = i * base + c;
@@ -131,8 +132,8 @@ end:;
 }
 
 /* Convert a string to an unsigned 64-bit integer */
-uint64_t strtou64(const char *nptr, char **endptr, register int base) {
-	register const char *s = nptr; register uint64_t i = 0, c;
+u64 strtou64(const char *nptr, char **endptr, register int base) {
+	register const char *s = nptr; register u64 i = 0, c;
 
 	for (; isspace(*s); ++s);
 
@@ -151,8 +152,8 @@ uint64_t strtou64(const char *nptr, char **endptr, register int base) {
 		else { break; }
 
 		if (c >= base) { break; }
-		if (i > (UINT64_MAX - c) / base) {
-			errno = ERANGE; i = UINT64_MAX; goto end;
+		if (i > (U64_MAX - c) / base) {
+			errno = ERANGE; i = U64_MAX; goto end;
 		}
 
 		i = i * base + c;
diff --git a/src/util/util.h b/src/util/util.h
new file mode 100644
index 0000000..3b3d167
--- /dev/null
+++ b/src/util/util.h
@@ -0,0 +1,92 @@
+// util/util.h, version 1.0.0
+// Utility header file from libutil
+// Copyright (C) 2021, Jakob Wakeling
+// All rights reserved.
+
+/*
+OMKOV Permissive Licence, version 1.0
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimers.
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimers in the documentation and/or
+  other materials provided with the distribution.
+* Neither the names of the copyright holders, nor the names of its contributors
+  may be used to endorse or promote products derived from this Software without
+  specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
+HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+*/
+
+#ifndef UTIL_UTIL_H_KP8NS9DC
+#define UTIL_UTIL_H_KP8NS9DC
+
+#include <assert.h>
+#include <float.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* Type Definitions */
+typedef uint8_t   u8;
+typedef uint16_t  u16;
+typedef uint32_t  u32;
+typedef uint64_t  u64;
+typedef uintptr_t UINT;
+
+typedef int8_t   s8;
+typedef int16_t  s16;
+typedef int32_t  s32;
+typedef int64_t  s64;
+typedef intptr_t sint;
+
+typedef float       f32;
+typedef double      f64;
+typedef long double f128;
+
+/* Type Limits */
+#define U8_MIN   UINT8_MIN
+#define U8_MAX   UINT8_MAX
+#define U16_MIN  UINT16_MIN
+#define U16_MAX  UINT16_MAX
+#define U32_MIN  UINT32_MIN
+#define U32_MAX  UINT32_MAX
+#define U64_MIN  UINT64_MIN
+#define U64_MAX  UINT64_MAX
+#define UINT_MIN UINTPTR_MIN
+#define UINT_MAX UINTPTR_MAX
+
+#define S8_MIN   INT8_MIN
+#define S8_MAX   INT8_MAX
+#define S16_MIN  INT16_MIN
+#define S16_MAX  INT16_MAX
+#define S32_MIN  INT32_MIN
+#define S32_MAX  INT32_MAX
+#define S64_MIN  INT64_MIN
+#define S64_MAX  INT64_MAX
+#define SINT_MIN INTPTR_MIN
+#define SINT_MAX INTPTR_MAX
+
+#define F32_MIN  FLT_MIN
+#define F32_MAX  FLT_MAX
+#define F64_MIN  DBL_MIN
+#define F64_MAX  DBL_MAX
+#define F128_MIN LDBL_MIN
+#define F128_MAX LDBL_MAX
+
+/* Miscellaneous */
+#define BIT(x) (1 << (x))
+
+#endif // UTIL_UTIL_H_KP8NS9DC