Browse Source

添加文件列表

weicky 4 years ago
commit
7eae56b420
38 changed files with 88153 additions and 0 deletions
  1. BIN
      32x32.ico
  2. 227 0
      base64/codec.c
  3. 109 0
      base64/codec_choose.c
  4. 131 0
      base64/codecs.h
  5. 170 0
      base64/lib.c
  6. 111 0
      base64/libbase64.h
  7. 393 0
      base64/tables.h
  8. 2973 0
      json/cJSON.c
  9. 288 0
      json/cJSON.h
  10. 858 0
      main.c
  11. 308 0
      md5/md5.c
  12. 43 0
      md5/md5.h
  13. 2080 0
      parson/parson.c
  14. 240 0
      parson/parson.h
  15. 32 0
      resources.h
  16. 25 0
      resources.rc
  17. 326 0
      sha1/sha1.c
  18. 48 0
      sha1/sha1.h
  19. 115 0
      test.cbp
  20. 350 0
      test.depend
  21. 85 0
      test.layout
  22. 313 0
      timelib/astro.c
  23. 74 0
      timelib/astro.h
  24. 229 0
      timelib/dow.c
  25. 42 0
      timelib/fallbackmap.h
  26. 176 0
      timelib/interval.c
  27. 25522 0
      timelib/parse_date.c
  28. 1051 0
      timelib/parse_iso_intervals.c
  29. 713 0
      timelib/parse_tz.c
  30. 366 0
      timelib/timelib.c
  31. 955 0
      timelib/timelib.h
  32. 140 0
      timelib/timelib_private.h
  33. 47483 0
      timelib/timezonedb.h
  34. 1127 0
      timelib/timezonemap.h
  35. 539 0
      timelib/tm2unixtime.c
  36. 275 0
      timelib/unixtime2tm.c
  37. 189 0
      url/url.c
  38. 47 0
      url/url.h

BIN
32x32.ico


+ 227 - 0
base64/codec.c

@@ -0,0 +1,227 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "libbase64.h"
+#include "codecs.h"
+
+BASE64_ENC_FUNCTION(plain)
+{
+	// Assume that *out is large enough to contain the output.
+	// Theoretically it should be 4/3 the length of src.
+	const uint8_t *c = (const uint8_t *)src;
+	uint8_t *o = (uint8_t *)out;
+
+	// Use local temporaries to avoid cache thrashing:
+	size_t outl = 0;
+	struct base64_state st;
+	st.bytes = state->bytes;
+	st.carry = state->carry;
+
+	// Turn three bytes into four 6-bit numbers:
+	// in[0] = 00111111
+	// in[1] = 00112222
+	// in[2] = 00222233
+	// in[3] = 00333333
+
+	// Duff's device, a for() loop inside a switch() statement. Legal!
+	switch (st.bytes)
+	{
+		for (;;)
+		{
+		case 0:
+
+			// If we have 64-bit ints, pick off 6 bytes at a time for as long as we can,
+			// but ensure that there are at least 8 bytes available to avoid segfaulting:
+			while (srclen >= 8)
+			{
+				// Load string:
+				uint64_t str = *(uint64_t *)c;
+
+				// Reorder to 64-bit big-endian, if not already in that format. The
+				// workset must be in big-endian, otherwise the shifted bits do not
+				// carry over properly among adjacent bytes:
+				str = cpu_to_be64(str);
+
+				// Shift input by 6 bytes each round and mask in only the lower 6 bits;
+				// look up the character in the Base64 encoding table and write it to
+				// the output location:
+				*o++ = base64_table_enc[(str >> 58) & 0x3F];
+				*o++ = base64_table_enc[(str >> 52) & 0x3F];
+				*o++ = base64_table_enc[(str >> 46) & 0x3F];
+				*o++ = base64_table_enc[(str >> 40) & 0x3F];
+				*o++ = base64_table_enc[(str >> 34) & 0x3F];
+				*o++ = base64_table_enc[(str >> 28) & 0x3F];
+				*o++ = base64_table_enc[(str >> 22) & 0x3F];
+				*o++ = base64_table_enc[(str >> 16) & 0x3F];
+
+				c += 6;		// 6 bytes of input
+				outl += 8;	// 8 bytes of output
+				srclen -= 6;
+			}
+
+			if (srclen-- == 0) {
+				break;
+			}
+			*o++ = base64_table_enc[*c >> 2];
+			st.carry = (*c++ << 4) & 0x30;
+			st.bytes++;
+			outl += 1;
+
+			// Deliberate fallthrough:
+			BASE64_FALLTHROUGH
+
+		case 1:	if (srclen-- == 0) {
+				break;
+			}
+			*o++ = base64_table_enc[st.carry | (*c >> 4)];
+			st.carry = (*c++ << 2) & 0x3C;
+			st.bytes++;
+			outl += 1;
+
+			// Deliberate fallthrough:
+			BASE64_FALLTHROUGH
+
+		case 2:	if (srclen-- == 0) {
+				break;
+			}
+			*o++ = base64_table_enc[st.carry | (*c >> 6)];
+			*o++ = base64_table_enc[*c++ & 0x3F];
+			st.bytes = 0;
+			outl += 2;
+		}
+	}
+	state->bytes = st.bytes;
+	state->carry = st.carry;
+	*outlen = outl;
+}
+
+BASE64_DEC_FUNCTION(plain)
+{
+	int ret = 0;
+	const uint8_t *c = (const uint8_t *)src;
+	uint8_t *o = (uint8_t *)out;
+	uint8_t q;
+
+	// Use local temporaries to avoid cache thrashing:
+	size_t outl = 0;
+	struct base64_state st;
+	st.eof = state->eof;
+	st.bytes = state->bytes;
+	st.carry = state->carry;
+
+	// If we previously saw an EOF or an invalid character, bail out:
+	if (st.eof) {
+		*outlen = 0;
+		ret = 0;
+		// If there was a trailing '=' to check, check it:
+		if (srclen && (st.eof == BASE64_AEOF)) {
+			state->bytes = 0;
+			state->eof = BASE64_EOF;
+			ret = ((base64_table_dec[*c++] == 254) && (srclen == 1)) ? 1 : 0;
+		}
+		return ret;
+	}
+
+	// Turn four 6-bit numbers into three bytes:
+	// out[0] = 11111122
+	// out[1] = 22223333
+	// out[2] = 33444444
+
+	// Duff's device again:
+	switch (st.bytes)
+	{
+		for (;;)
+		{
+		case 0:
+			if (srclen-- == 0) {
+				ret = 1;
+				break;
+			}
+			if ((q = base64_table_dec[*c++]) >= 254) {
+				st.eof = BASE64_EOF;
+				// Treat character '=' as invalid for byte 0:
+				break;
+			}
+			st.carry = q << 2;
+			st.bytes++;
+
+			// Deliberate fallthrough:
+			BASE64_FALLTHROUGH
+
+		case 1:	if (srclen-- == 0) {
+				ret = 1;
+				break;
+			}
+			if ((q = base64_table_dec[*c++]) >= 254) {
+				st.eof = BASE64_EOF;
+				// Treat character '=' as invalid for byte 1:
+				break;
+			}
+			*o++ = st.carry | (q >> 4);
+			st.carry = q << 4;
+			st.bytes++;
+			outl++;
+
+			// Deliberate fallthrough:
+			BASE64_FALLTHROUGH
+
+		case 2:	if (srclen-- == 0) {
+				ret = 1;
+				break;
+			}
+			if ((q = base64_table_dec[*c++]) >= 254) {
+				st.bytes++;
+				// When q == 254, the input char is '='.
+				// Check if next byte is also '=':
+				if (q == 254) {
+					if (srclen-- != 0) {
+						st.bytes = 0;
+						// EOF:
+						st.eof = BASE64_EOF;
+						q = base64_table_dec[*c++];
+						ret = ((q == 254) && (srclen == 0)) ? 1 : 0;
+						break;
+					}
+					else {
+						// Almost EOF
+						st.eof = BASE64_AEOF;
+						ret = 1;
+						break;
+					}
+				}
+				// If we get here, there was an error:
+				break;
+			}
+			*o++ = st.carry | (q >> 2);
+			st.carry = q << 6;
+			st.bytes++;
+			outl++;
+
+			// Deliberate fallthrough:
+			BASE64_FALLTHROUGH
+
+		case 3:	if (srclen-- == 0) {
+				ret = 1;
+				break;
+			}
+			if ((q = base64_table_dec[*c++]) >= 254) {
+				st.bytes = 0;
+				st.eof = BASE64_EOF;
+				// When q == 254, the input char is '='. Return 1 and EOF.
+				// When q == 255, the input char is invalid. Return 0 and EOF.
+				ret = ((q == 254) && (srclen == 0)) ? 1 : 0;
+				break;
+			}
+			*o++ = st.carry | q;
+			st.carry = 0;
+			st.bytes = 0;
+			outl++;
+		}
+	}
+	state->eof = st.eof;
+	state->bytes = st.bytes;
+	state->carry = st.carry;
+	*outlen = outl;
+	return ret;
+}

+ 109 - 0
base64/codec_choose.c

@@ -0,0 +1,109 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "libbase64.h"
+#include "codecs.h"
+
+#if (__x86_64__ || __i386__ || _M_X86 || _M_X64)
+  #define BASE64_X86
+#endif
+
+#ifdef BASE64_X86
+#ifdef _MSC_VER
+	#include <intrin.h>
+	#define __cpuid_count(__level, __count, __eax, __ebx, __ecx, __edx) \
+	{						\
+		int info[4];				\
+		__cpuidex(info, __level, __count);	\
+		__eax = info[0];			\
+		__ebx = info[1];			\
+		__ecx = info[2];			\
+		__edx = info[3];			\
+	}
+	#define __cpuid(__level, __eax, __ebx, __ecx, __edx) \
+		__cpuid_count(__level, 0, __eax, __ebx, __ecx, __edx)
+#else
+	#include <cpuid.h>
+#endif
+
+#ifndef bit_AVX2
+#define bit_AVX2 (1 << 5)
+#endif
+#ifndef bit_SSSE3
+#define bit_SSSE3 (1 << 9)
+#endif
+#ifndef bit_SSE41
+#define bit_SSE41 (1 << 19)
+#endif
+#ifndef bit_SSE42
+#define bit_SSE42 (1 << 20)
+#endif
+#ifndef bit_AVX
+#define bit_AVX (1 << 28)
+#endif
+
+#define bit_XSAVE_XRSTORE (1 << 27)
+
+#ifndef _XCR_XFEATURE_ENABLED_MASK
+#define _XCR_XFEATURE_ENABLED_MASK 0
+#endif
+
+#define _XCR_XMM_AND_YMM_STATE_ENABLED_BY_OS 0x6
+#endif
+
+// Function declarations:
+#define BASE64_CODEC_FUNCS(arch)	\
+	BASE64_ENC_FUNCTION(arch);	\
+	BASE64_DEC_FUNCTION(arch);	\
+
+BASE64_CODEC_FUNCS(plain)
+
+static bool
+codec_choose_forced (struct codec *codec, int flags)
+{
+	// If the user wants to use a certain codec,
+	// always allow it, even if the codec is a no-op.
+	// For testing purposes.
+
+	if (!(flags & 0xFF)) {
+		return false;
+	}
+	codec->enc = base64_stream_encode_plain;
+    codec->dec = base64_stream_decode_plain;
+    return true;
+}
+
+static bool
+codec_choose_arm (struct codec *codec)
+{
+	(void)codec;
+	return false;
+}
+
+static bool
+codec_choose_x86 (struct codec *codec)
+{
+	(void)codec;
+	return false;
+}
+
+void
+codec_choose (struct codec *codec, int flags)
+{
+	// User forced a codec:
+	if (codec_choose_forced(codec, flags)) {
+		return;
+	}
+
+	// Runtime feature detection:
+	if (codec_choose_arm(codec)) {
+		return;
+	}
+	if (codec_choose_x86(codec)) {
+		return;
+	}
+	codec->enc = base64_stream_encode_plain;
+	codec->dec = base64_stream_decode_plain;
+}

+ 131 - 0
base64/codecs.h

@@ -0,0 +1,131 @@
+
+// Function parameters for encoding functions:
+#define BASE64_ENC_PARAMS			\
+	( struct base64_state	*state		\
+	, const char		*src		\
+	, size_t		 srclen		\
+	, char			*out		\
+	, size_t		*outlen		\
+	)
+
+// Function parameters for decoding functions:
+#define BASE64_DEC_PARAMS			\
+	( struct base64_state	*state		\
+	, const char		*src		\
+	, size_t		 srclen		\
+	, char			*out		\
+	, size_t		*outlen		\
+	)
+
+// Function signature for encoding functions:
+#define BASE64_ENC_FUNCTION(arch)		\
+	void					\
+	base64_stream_encode_ ## arch		\
+	BASE64_ENC_PARAMS
+
+// Function signature for decoding functions:
+#define BASE64_DEC_FUNCTION(arch)		\
+	int					\
+	base64_stream_decode_ ## arch		\
+	BASE64_DEC_PARAMS
+
+// Cast away unused variable, silence compiler:
+#define UNUSED(x)		((void)(x))
+
+// Stub function when encoder arch unsupported:
+#define BASE64_ENC_STUB				\
+	UNUSED(state);				\
+	UNUSED(src);				\
+	UNUSED(srclen);				\
+	UNUSED(out);				\
+						\
+	*outlen = 0;
+
+// Stub function when decoder arch unsupported:
+#define BASE64_DEC_STUB				\
+	UNUSED(state);				\
+	UNUSED(src);				\
+	UNUSED(srclen);				\
+	UNUSED(out);				\
+	UNUSED(outlen);				\
+						\
+	return -1;
+
+struct codec
+{
+	void (* enc) BASE64_ENC_PARAMS;
+	int  (* dec) BASE64_DEC_PARAMS;
+};
+
+// Define machine endianness. This is for GCC:
+#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+	#define BASE64_LITTLE_ENDIAN 1
+#else
+	#define BASE64_LITTLE_ENDIAN 0
+#endif
+
+// This is for Clang:
+#ifdef __LITTLE_ENDIAN__
+	#define BASE64_LITTLE_ENDIAN 1
+#endif
+
+#ifdef __BIG_ENDIAN__
+	#define BASE64_LITTLE_ENDIAN 0
+#endif
+
+// Endian conversion functions:
+#if BASE64_LITTLE_ENDIAN
+	#if defined(_MSC_VER)
+		// Microsoft Visual C++:
+		#define cpu_to_be32(x)	_byteswap_ulong(x)
+		#define cpu_to_be64(x)	_byteswap_uint64(x)
+		#define be32_to_cpu(x)	_byteswap_ulong(x)
+		#define be64_to_cpu(x)	_byteswap_uint64(x)
+	#else
+		// GCC and Clang:
+		#define cpu_to_be32(x)	__builtin_bswap32(x)
+		#define cpu_to_be64(x)	__builtin_bswap64(x)
+		#define be32_to_cpu(x)	__builtin_bswap32(x)
+		#define be64_to_cpu(x)	__builtin_bswap64(x)
+	#endif
+#else
+	// No conversion needed:
+	#define cpu_to_be32(x)	(x)
+	#define cpu_to_be64(x)	(x)
+	#define be32_to_cpu(x)	(x)
+	#define be64_to_cpu(x)	(x)
+#endif 
+
+// detect word size
+#ifdef _INTEGRAL_MAX_BITS
+#define BASE64_WORDSIZE _INTEGRAL_MAX_BITS
+#else
+#define BASE64_WORDSIZE __WORDSIZE
+#endif
+
+// end-of-file definitions
+// Almost end-of-file when waiting for the last '=' character:
+#define BASE64_AEOF 1
+// End-of-file when stream end has been reached or invalid input provided:
+#define BASE64_EOF 2
+
+// GCC 7 defaults to issuing a warning for fallthrough in switch statements,
+// unless the fallthrough cases are marked with an attribute. As we use
+// fallthrough deliberately, define an alias for the attribute:
+#if __GNUC__ >= 7
+  #define BASE64_FALLTHROUGH  __attribute__((fallthrough));
+#else
+  #define BASE64_FALLTHROUGH
+#endif
+
+extern void codec_choose (struct codec *, int flags);
+
+// These tables are used by all codecs
+// for fallback plain encoding/decoding:
+extern const uint8_t base64_table_enc[];
+extern const uint8_t base64_table_dec[];
+
+extern const uint32_t base64_table_dec_d0[];
+extern const uint32_t base64_table_dec_d1[];
+extern const uint32_t base64_table_dec_d2[];
+extern const uint32_t base64_table_dec_d3[];

+ 170 - 0
base64/lib.c

@@ -0,0 +1,170 @@
+#include <stdint.h>
+#include <stddef.h>
+
+#include "libbase64.h"
+#include "codecs.h"
+#include "tables.h"
+
+// These static function pointers are initialized once when the library is
+// first used, and remain in use for the remaining lifetime of the program.
+// The idea being that CPU features don't change at runtime.
+static struct codec codec = { NULL, NULL };
+
+const uint8_t
+base64_table_enc[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	"abcdefghijklmnopqrstuvwxyz"
+	"0123456789+/";
+
+// In the lookup table below, note that the value for '=' (character 61) is
+// 254, not 255. This character is used for in-band signaling of the end of
+// the datastream, and we will use that later. The characters A-Z, a-z, 0-9
+// and + / are mapped to their "decoded" values. The other bytes all map to
+// the value 255, which flags them as "invalid input".
+
+const uint8_t
+base64_table_dec[] =
+{
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,		//   0..15
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,		//  16..31
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,		//  32..47
+	 52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 254, 255, 255,		//  48..63
+	255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,		//  64..79
+	 15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,		//  80..95
+	255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,		//  96..111
+	 41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255, 255,		// 112..127
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,		// 128..143
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+};
+
+void
+base64_stream_encode_init (struct base64_state *state, int flags)
+{
+	// If any of the codec flags are set, redo choice:
+	if (codec.enc == NULL || flags & 0xFF) {
+		codec_choose(&codec, flags);
+	}
+	state->eof = 0;
+	state->bytes = 0;
+	state->carry = 0;
+	state->flags = flags;
+}
+
+void
+base64_stream_encode
+	( struct base64_state	*state
+	, const char		*src
+	, size_t		 srclen
+	, char			*out
+	, size_t		*outlen
+	)
+{
+	codec.enc(state, src, srclen, out, outlen);
+}
+
+void
+base64_stream_encode_final
+	( struct base64_state	*state
+	, char			*out
+	, size_t		*outlen
+	)
+{
+	uint8_t *o = (uint8_t *)out;
+
+	if (state->bytes == 1) {
+		*o++ = base64_table_enc[state->carry];
+		*o++ = '=';
+		*o++ = '=';
+		*outlen = 3;
+		return;
+	}
+	if (state->bytes == 2) {
+		*o++ = base64_table_enc[state->carry];
+		*o++ = '=';
+		*outlen = 2;
+		return;
+	}
+	*outlen = 0;
+}
+
+void
+base64_stream_decode_init (struct base64_state *state, int flags)
+{
+	// If any of the codec flags are set, redo choice:
+	if (codec.dec == NULL || flags & 0xFF) {
+		codec_choose(&codec, flags);
+	}
+	state->eof = 0;
+	state->bytes = 0;
+	state->carry = 0;
+	state->flags = flags;
+}
+
+int
+base64_stream_decode
+	( struct base64_state	*state
+	, const char		*src
+	, size_t		 srclen
+	, char			*out
+	, size_t		*outlen
+	)
+{
+	return codec.dec(state, src, srclen, out, outlen);
+}
+
+void
+base64_encode
+	( const char	*src
+	, size_t	 srclen
+	, char		*out
+	, size_t	*outlen
+	, int		 flags
+	)
+{
+	size_t s;
+	size_t t;
+	struct base64_state state;
+
+	// Init the stream reader:
+	base64_stream_encode_init(&state, flags);
+
+	// Feed the whole string to the stream reader:
+	base64_stream_encode(&state, src, srclen, out, &s);
+
+	// Finalize the stream by writing trailer if any:
+	base64_stream_encode_final(&state, out + s, &t);
+
+	// Final output length is stream length plus tail:
+	*outlen = s + t;
+}
+
+int
+base64_decode
+	( const char	*src
+	, size_t	 srclen
+	, char		*out
+	, size_t	*outlen
+	, int		 flags
+	)
+{
+	int ret;
+	struct base64_state state;
+
+	// Init the stream reader:
+	base64_stream_decode_init(&state, flags);
+
+	// Feed the whole string to the stream reader:
+	ret = base64_stream_decode(&state, src, srclen, out, outlen);
+
+	// If when decoding a whole block, we're still waiting for input then fail:
+	if (ret && (state.bytes == 0)) {
+		return ret;
+	}
+	return 0;
+}

+ 111 - 0
base64/libbase64.h

@@ -0,0 +1,111 @@
+#ifndef _LIBBASE64_H
+#define _LIBBASE64_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* These are the flags that can be passed in the `flags` argument. The values
+ * below force the use of a given codec, even if that codec is a no-op in the
+ * current build. Used in testing. Set to 0 for the default behavior, which is
+ * runtime feature detection on x86, a compile-time fixed codec on ARM, and
+ * the plain codec on other platforms: */
+#define BASE64_FORCE_AVX2	(1 << 0)
+#define BASE64_FORCE_NEON32	(1 << 1)
+#define BASE64_FORCE_NEON64	(1 << 2)
+#define BASE64_FORCE_PLAIN	(1 << 3)
+#define BASE64_FORCE_SSSE3	(1 << 4)
+#define BASE64_FORCE_SSE41	(1 << 5)
+#define BASE64_FORCE_SSE42	(1 << 6)
+#define BASE64_FORCE_AVX	(1 << 7)
+
+struct base64_state {
+	int eof;
+	int bytes;
+	int flags;
+	unsigned char carry;
+};
+
+/* Wrapper function to encode a plain string of given length. Output is written
+ * to *out without trailing zero. Output length in bytes is written to *outlen.
+ * The buffer in `out` has been allocated by the caller and is at least 4/3 the
+ * size of the input. See above for `flags`; set to 0 for default operation: */
+void base64_encode
+	( const char		*src
+	, size_t		 srclen
+	, char			*out
+	, size_t		*outlen
+	, int			 flags
+	) ;
+
+/* Call this before calling base64_stream_encode() to init the state. See above
+ * for `flags`; set to 0 for default operation: */
+void base64_stream_encode_init
+	( struct base64_state	*state
+	, int			 flags
+	) ;
+
+/* Encodes the block of data of given length at `src`, into the buffer at
+ * `out`. Caller is responsible for allocating a large enough out-buffer; it
+ * must be at least 4/3 the size of the in-buffer, but take some margin. Places
+ * the number of new bytes written into `outlen` (which is set to zero when the
+ * function starts). Does not zero-terminate or finalize the output. */
+void base64_stream_encode
+	( struct base64_state	*state
+	, const char		*src
+	, size_t		 srclen
+	, char			*out
+	, size_t		*outlen
+	) ;
+
+/* Finalizes the output begun by previous calls to `base64_stream_encode()`.
+ * Adds the required end-of-stream markers if appropriate. `outlen` is modified
+ * and will contain the number of new bytes written at `out` (which will quite
+ * often be zero). */
+void base64_stream_encode_final
+	( struct base64_state	*state
+	, char			*out
+	, size_t		*outlen
+	) ;
+
+/* Wrapper function to decode a plain string of given length. Output is written
+ * to *out without trailing zero. Output length in bytes is written to *outlen.
+ * The buffer in `out` has been allocated by the caller and is at least 3/4 the
+ * size of the input. See above for `flags`, set to 0 for default operation: */
+int base64_decode
+	( const char		*src
+	, size_t		 srclen
+	, char			*out
+	, size_t		*outlen
+	, int			 flags
+	) ;
+
+/* Call this before calling base64_stream_decode() to init the state. See above
+ * for `flags`; set to 0 for default operation: */
+void base64_stream_decode_init
+	( struct base64_state	*state
+	, int			 flags
+	) ;
+
+/* Decodes the block of data of given length at `src`, into the buffer at
+ * `out`. Caller is responsible for allocating a large enough out-buffer; it
+ * must be at least 3/4 the size of the in-buffer, but take some margin. Places
+ * the number of new bytes written into `outlen` (which is set to zero when the
+ * function starts). Does not zero-terminate the output. Returns 1 if all is
+ * well, and 0 if a decoding error was found, such as an invalid character.
+ * Returns -1 if the chosen codec is not included in the current build. Used by
+ * the test harness to check whether a codec is available for testing. */
+int base64_stream_decode
+	( struct base64_state	*state
+	, const char		*src
+	, size_t		 srclen
+	, char			*out
+	, size_t		*outlen
+	) ;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBBASE64_H */

+ 393 - 0
base64/tables.h

@@ -0,0 +1,393 @@
+#include <stdint.h>
+#define CHAR62 '+'
+#define CHAR63 '/'
+#define CHARPAD '='
+
+
+#if BASE64_LITTLE_ENDIAN
+
+
+/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */
+
+const uint32_t base64_table_dec_d0[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x000000f8, 0xffffffff, 0xffffffff, 0xffffffff, 0x000000fc,
+0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
+0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
+0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
+0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
+0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
+0x00000064, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
+0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
+0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
+0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
+0x000000c4, 0x000000c8, 0x000000cc, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+const uint32_t base64_table_dec_d1[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x0000e003, 0xffffffff, 0xffffffff, 0xffffffff, 0x0000f003,
+0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
+0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
+0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
+0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
+0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
+0x00009001, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
+0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
+0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
+0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
+0x00001003, 0x00002003, 0x00003003, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+const uint32_t base64_table_dec_d2[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00800f00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00c00f00,
+0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
+0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
+0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
+0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
+0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
+0x00400600, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
+0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
+0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
+0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
+0x00400c00, 0x00800c00, 0x00c00c00, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+const uint32_t base64_table_dec_d3[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x003e0000, 0xffffffff, 0xffffffff, 0xffffffff, 0x003f0000,
+0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
+0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
+0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
+0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
+0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
+0x00190000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
+0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
+0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
+0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
+0x00310000, 0x00320000, 0x00330000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+#else
+
+
+/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */
+
+const uint32_t base64_table_dec_d0[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xf8000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xfc000000,
+0xd0000000, 0xd4000000, 0xd8000000, 0xdc000000, 0xe0000000, 0xe4000000,
+0xe8000000, 0xec000000, 0xf0000000, 0xf4000000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x04000000, 0x08000000, 0x0c000000, 0x10000000, 0x14000000, 0x18000000,
+0x1c000000, 0x20000000, 0x24000000, 0x28000000, 0x2c000000, 0x30000000,
+0x34000000, 0x38000000, 0x3c000000, 0x40000000, 0x44000000, 0x48000000,
+0x4c000000, 0x50000000, 0x54000000, 0x58000000, 0x5c000000, 0x60000000,
+0x64000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x68000000, 0x6c000000, 0x70000000, 0x74000000, 0x78000000,
+0x7c000000, 0x80000000, 0x84000000, 0x88000000, 0x8c000000, 0x90000000,
+0x94000000, 0x98000000, 0x9c000000, 0xa0000000, 0xa4000000, 0xa8000000,
+0xac000000, 0xb0000000, 0xb4000000, 0xb8000000, 0xbc000000, 0xc0000000,
+0xc4000000, 0xc8000000, 0xcc000000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+const uint32_t base64_table_dec_d1[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x03e00000, 0xffffffff, 0xffffffff, 0xffffffff, 0x03f00000,
+0x03400000, 0x03500000, 0x03600000, 0x03700000, 0x03800000, 0x03900000,
+0x03a00000, 0x03b00000, 0x03c00000, 0x03d00000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00100000, 0x00200000, 0x00300000, 0x00400000, 0x00500000, 0x00600000,
+0x00700000, 0x00800000, 0x00900000, 0x00a00000, 0x00b00000, 0x00c00000,
+0x00d00000, 0x00e00000, 0x00f00000, 0x01000000, 0x01100000, 0x01200000,
+0x01300000, 0x01400000, 0x01500000, 0x01600000, 0x01700000, 0x01800000,
+0x01900000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x01a00000, 0x01b00000, 0x01c00000, 0x01d00000, 0x01e00000,
+0x01f00000, 0x02000000, 0x02100000, 0x02200000, 0x02300000, 0x02400000,
+0x02500000, 0x02600000, 0x02700000, 0x02800000, 0x02900000, 0x02a00000,
+0x02b00000, 0x02c00000, 0x02d00000, 0x02e00000, 0x02f00000, 0x03000000,
+0x03100000, 0x03200000, 0x03300000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+const uint32_t base64_table_dec_d2[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x000f8000, 0xffffffff, 0xffffffff, 0xffffffff, 0x000fc000,
+0x000d0000, 0x000d4000, 0x000d8000, 0x000dc000, 0x000e0000, 0x000e4000,
+0x000e8000, 0x000ec000, 0x000f0000, 0x000f4000, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00004000, 0x00008000, 0x0000c000, 0x00010000, 0x00014000, 0x00018000,
+0x0001c000, 0x00020000, 0x00024000, 0x00028000, 0x0002c000, 0x00030000,
+0x00034000, 0x00038000, 0x0003c000, 0x00040000, 0x00044000, 0x00048000,
+0x0004c000, 0x00050000, 0x00054000, 0x00058000, 0x0005c000, 0x00060000,
+0x00064000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00068000, 0x0006c000, 0x00070000, 0x00074000, 0x00078000,
+0x0007c000, 0x00080000, 0x00084000, 0x00088000, 0x0008c000, 0x00090000,
+0x00094000, 0x00098000, 0x0009c000, 0x000a0000, 0x000a4000, 0x000a8000,
+0x000ac000, 0x000b0000, 0x000b4000, 0x000b8000, 0x000bc000, 0x000c0000,
+0x000c4000, 0x000c8000, 0x000cc000, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+const uint32_t base64_table_dec_d3[256] = {
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00003e00, 0xffffffff, 0xffffffff, 0xffffffff, 0x00003f00,
+0x00003400, 0x00003500, 0x00003600, 0x00003700, 0x00003800, 0x00003900,
+0x00003a00, 0x00003b00, 0x00003c00, 0x00003d00, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000,
+0x00000100, 0x00000200, 0x00000300, 0x00000400, 0x00000500, 0x00000600,
+0x00000700, 0x00000800, 0x00000900, 0x00000a00, 0x00000b00, 0x00000c00,
+0x00000d00, 0x00000e00, 0x00000f00, 0x00001000, 0x00001100, 0x00001200,
+0x00001300, 0x00001400, 0x00001500, 0x00001600, 0x00001700, 0x00001800,
+0x00001900, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0x00001a00, 0x00001b00, 0x00001c00, 0x00001d00, 0x00001e00,
+0x00001f00, 0x00002000, 0x00002100, 0x00002200, 0x00002300, 0x00002400,
+0x00002500, 0x00002600, 0x00002700, 0x00002800, 0x00002900, 0x00002a00,
+0x00002b00, 0x00002c00, 0x00002d00, 0x00002e00, 0x00002f00, 0x00003000,
+0x00003100, 0x00003200, 0x00003300, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+};
+
+
+#endif

File diff suppressed because it is too large
+ 2973 - 0
json/cJSON.c


+ 288 - 0
json/cJSON.h

@@ -0,0 +1,288 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in 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:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  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
+  AUTHORS OR COPYRIGHT HOLDERS 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 IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type)   type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 12
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False  (1 << 0)
+#define cJSON_True   (1 << 1)
+#define cJSON_NULL   (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array  (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw    (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+    struct cJSON *next;
+    struct cJSON *prev;
+    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+    struct cJSON *child;
+
+    /* The type of the item, as above. */
+    int type;
+
+    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
+    char *valuestring;
+    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+    int valueint;
+    /* The item's number, if type==cJSON_Number */
+    double valuedouble;
+
+    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+    char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+      void *(CJSON_CDECL *malloc_fn)(size_t sz);
+      void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check if the item is a string and return its valuestring */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant, 
+ * but should point to a readable and writable adress area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 858 - 0
main.c

@@ -0,0 +1,858 @@
+#define UNICODE
+
+#if defined(UNICODE) && !defined(_UNICODE)
+    #define _UNICODE
+#elif defined(_UNICODE) && !defined(UNICODE)
+    #define UNICODE
+#endif
+
+#include <stdio.h>
+#include <wchar.h>
+#include <tchar.h>
+#include <windows.h>
+#include <time.h>
+
+#include "base64/libbase64.h"
+#include "md5/md5.h"
+#include "sha1/sha1.h"
+#include "url/url.h"
+#include "parson/parson.h"
+#include "resources.h"
+#include "timelib/timelib.h"
+
+#define MIN_WIDTH 600
+#define MIN_HEIGHT 500
+#define FRAME_PADDING 3
+#define ROW_PADDING 10
+#define TOP_ROW_HEIGHT 26
+#define TOP_ITEM_WIDTH 60
+#define TOP_ITEM_SPACE 6
+#define BOTTOM_ROW_HEIGHT 26
+#define BOTTOM_ITEM_WIDTH 80
+#define BOTTOM_ITEM_SPACE 10
+
+/*  Declare Windows procedure  */
+LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
+
+/*  Make the class name into a global variable  */
+TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");
+HINSTANCE gInst;
+HMENU gMenu;
+HWND gWindows[256] = {};
+WORD gLastMenu = IDM_BASE64;
+WORD gLastFocus = IDC_EDIT;
+int gUTF8 = 1;
+timelib_tzinfo *gTimeZoneInfo;
+
+static BOOL RegWC(HINSTANCE inst);
+static void OnCreate(HWND hwnd);
+static void OnSizing(HWND hwnd, WPARAM fwSide, LPRECT lpRect);
+static void OnSize(HWND hwnd);
+static void OnMenuClicked(HWND hwnd, WORD id);
+static void OnAccelerator(HWND hwnd, WORD id);
+static void OnClicked(HWND hwnd, WORD id);
+static void ButtonOnClicked(HWND hwnd, WORD id);
+static char *wchar_to_utf8(const TCHAR *in, int in_len, int *out_len);
+static TCHAR *utf8_to_wchar(const char *in, int in_len, int *out_len);
+static char *wchar_to_acp(const TCHAR *in, int in_len, int *out_len);
+static TCHAR *acp_to_wchar(const char *in, int in_len, int *out_len);
+static char *nl_unix2win(const char *in, char *out);
+static TCHAR *base64_encode_string(const TCHAR *in, int in_len, int *out_len, int utf8);
+static TCHAR *base64_decode_string(const TCHAR *in, int in_len, int *out_len, int utf8);
+static TCHAR *url_encode_string(const TCHAR *in, int in_len, int *out_len, int utf8);
+static TCHAR *url_decode_string(const TCHAR *in, int in_len, int *out_len, int utf8);
+static TCHAR *md5_string(const TCHAR *in, int in_len, int *out_len, int utf8);
+static TCHAR *sha1_string(const TCHAR *in, int in_len, int *out_len, int utf8);
+static TCHAR *json_format(const TCHAR *in, int in_len, int *out_len, int utf8);
+static TCHAR *time_convert_to_string(const TCHAR *in, int in_len, int *out_len);
+static TCHAR *string_convert_to_time(const TCHAR *in, int in_len, int *out_len);
+static timelib_tzinfo *my_date_parse_tzfile_wrapper(char *formal_tzname, const timelib_tzdb *tzdb);
+
+int WINAPI WinMain (HINSTANCE hThisInstance,
+                     HINSTANCE hPrevInstance,
+                     LPSTR lpszArgument,
+                     int nCmdShow)
+{
+    HWND hwnd;               /* This is the handle for our window */
+    MSG messages;            /* Here messages to the application are saved */
+    HACCEL hAccel;
+    int tziCode;
+
+    gInst = hThisInstance;
+    /* Register the window class, and if it fails quit the program */
+    if (!RegWC (hThisInstance))
+        return 0;
+
+    gMenu = LoadMenu(hThisInstance, MAKEINTRESOURCE(IDM_MAIN));
+    hAccel = LoadAccelerators(hThisInstance, MAKEINTRESOURCE(IDD_ACCEL));
+
+    /* The class is registered, let's create the program*/
+    hwnd = CreateWindowEx (
+           0,                   /* Extended possibilites for variation */
+           szClassName,         /* Classname */
+           _T("常用编解码工具"),       /* Title Text */
+           WS_OVERLAPPEDWINDOW, /* default window */
+           CW_USEDEFAULT,       /* Windows decides the position */
+           CW_USEDEFAULT,       /* where the window ends up on the screen */
+           MIN_WIDTH,                 /* The programs width */
+           MIN_HEIGHT,                 /* and height in pixels */
+           HWND_DESKTOP,        /* The window is a child-window to desktop */
+           gMenu,                /* No menu */
+           hThisInstance,       /* Program Instance handler */
+           NULL                 /* No Window Creation data */
+           );
+    gWindows[0] = hwnd;
+
+    /* Make the window visible on the screen */
+    ShowWindow (hwnd, nCmdShow);
+
+    gTimeZoneInfo = timelib_parse_tzfile("Etc/GMT-8", timelib_builtin_db(), &tziCode);
+
+    /* Run the message loop. It will run until GetMessage() returns 0 */
+    while (GetMessage (&messages, NULL, 0, 0))
+    {
+        if (TranslateAccelerator(hwnd, hAccel, &messages) == 0)
+        {
+            /* Translate virtual-key messages into character messages */
+            TranslateMessage(&messages);
+        }
+        /* Send message to WindowProcedure */
+        DispatchMessage(&messages);
+    }
+
+    /* The program return-value is 0 - The value that PostQuitMessage() gave */
+    return messages.wParam;
+}
+
+static BOOL RegWC(HINSTANCE inst)
+{
+    WNDCLASSEX wincl;        /* Data structure for the windowclass */
+
+    /* The Window structure */
+    wincl.hInstance = inst;
+    wincl.lpszClassName = szClassName;
+    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
+    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
+    wincl.cbSize = sizeof (WNDCLASSEX);
+
+    /* Use default icon and mouse-pointer */
+    wincl.hIcon = LoadIcon (inst, MAKEINTRESOURCE(IDI_APP));
+    wincl.hIconSm = LoadIcon (inst, MAKEINTRESOURCE(IDI_APP));
+    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
+    wincl.lpszMenuName = NULL;                 /* No menu */
+    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
+    wincl.cbWndExtra = 0;                      /* structure or the window instance */
+    /* Use Windows's default colour as the background of the window */
+    wincl.hbrBackground = (HBRUSH) COLOR_WINDOW;
+
+    /* Register the window class, and if it fails quit the program */
+    return RegisterClassEx (&wincl);
+}
+
+
+/*  This function is called by the Windows function DispatchMessage()  */
+
+LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    WORD wh, wl;
+
+    switch (message)                  /* handle the messages */
+    {
+        case WM_CREATE:
+            OnCreate(hwnd);
+            break;
+
+        case WM_SIZING:
+            OnSizing(hwnd, wParam, (LPRECT)lParam);
+            return TRUE;
+
+        case WM_SIZE:
+            OnSize(hwnd);
+            break;
+
+        case WM_COMMAND:
+            wh = HIWORD(wParam); //message
+            wl = LOWORD(wParam); //control id
+            if (lParam == (LPARAM)NULL)
+            {
+                if (wh == 0)
+                {
+                    OnMenuClicked(hwnd, wl);
+                }
+                else if (wh == 1)
+                {
+                    OnAccelerator(hwnd, wl);
+                }
+            }
+            else if (wh == BN_CLICKED)
+            {
+                OnClicked(hwnd, wl);
+            }
+            break;
+
+        case WM_NOTIFY:
+            break;
+
+        case WM_DESTROY:
+            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
+            break;
+
+        default:                      /* for messages that we don't deal with */
+            return DefWindowProc (hwnd, message, wParam, lParam);
+    }
+
+    return 0;
+}
+
+static void OnCreate(HWND hwnd)
+{
+    RECT rect;
+    long topRowL, topRowT, bottomRowL, bottomRowT, bodyL, bodyT, bodyW, bodyH;
+    HFONT font;
+
+    GetClientRect(hwnd, &rect);
+    topRowL = bottomRowL = bodyL = rect.left + FRAME_PADDING;
+    topRowT = rect.top + FRAME_PADDING;
+    bottomRowT = rect.bottom - FRAME_PADDING - BOTTOM_ROW_HEIGHT;
+    bodyT = topRowT + TOP_ROW_HEIGHT + ROW_PADDING;
+    bodyW = rect.right - rect.left - FRAME_PADDING - FRAME_PADDING;
+    bodyH = bottomRowT - ROW_PADDING - bodyT;
+
+    gWindows[IDC_LABEL] = CreateWindowEx(0, _T("STATIC"), _T("编码:"), WS_CHILD | WS_VISIBLE, topRowL, topRowT+4, TOP_ITEM_WIDTH, TOP_ROW_HEIGHT-4, hwnd, (HMENU)IDC_LABEL, gInst, NULL);
+    gWindows[IDC_UTF8] = CreateWindowEx(0, _T("BUTTON"), _T("UTF8"), WS_CHILD | BS_RADIOBUTTON | WS_VISIBLE, topRowL + (TOP_ITEM_WIDTH + TOP_ITEM_SPACE), topRowT, TOP_ITEM_WIDTH, TOP_ROW_HEIGHT, hwnd, (HMENU)IDC_UTF8, gInst, NULL);
+    gWindows[IDC_GBK] = CreateWindowEx(0, _T("BUTTON"), _T("GBK"), WS_CHILD | BS_RADIOBUTTON | WS_VISIBLE, topRowL + (TOP_ITEM_WIDTH + TOP_ITEM_SPACE)*2, topRowT, TOP_ITEM_WIDTH, TOP_ROW_HEIGHT, hwnd, (HMENU)IDC_GBK, gInst, NULL);
+    gWindows[IDC_EDIT] = CreateWindowEx(0, _T("EDIT"), NULL, WS_CHILD | WS_VSCROLL | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL, bodyL, bodyT, bodyW, bodyH, hwnd, (HMENU)IDC_EDIT, gInst, NULL);
+    gWindows[IDC_ENC] = CreateWindowEx(0, _T("BUTTON"), _T("编码"), WS_CHILD | WS_VISIBLE, bottomRowL, bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, hwnd, (HMENU)IDC_ENC, gInst, NULL);
+    gWindows[IDC_DEC] = CreateWindowEx(0, _T("BUTTON"), _T("解码"), WS_CHILD | WS_VISIBLE, bottomRowL + (BOTTOM_ITEM_WIDTH + BOTTOM_ITEM_SPACE), bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, hwnd, (HMENU)IDC_DEC, gInst, NULL);
+    gWindows[IDC_FORMAT] = CreateWindowEx(0, _T("BUTTON"), _T("格式化"), WS_CHILD, bottomRowL, bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, hwnd, (HMENU)IDC_FORMAT, gInst, NULL);
+    gWindows[IDC_TIME2STR] = CreateWindowEx(0, _T("BUTTON"), _T("转字符串"), WS_CHILD, bottomRowL, bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, hwnd, (HMENU)IDC_TIME2STR, gInst, NULL);
+    gWindows[IDC_STR2TIME] = CreateWindowEx(0, _T("BUTTON"), _T("转时间戳"), WS_CHILD, bottomRowL + (BOTTOM_ITEM_WIDTH + BOTTOM_ITEM_SPACE), bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, hwnd, (HMENU)IDC_STR2TIME, gInst, NULL);
+
+    font = CreateFont(0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, _T("宋体"));
+    if (font == NULL)
+    {
+        MessageBox(hwnd, _T("系统未安装宋体!"), _T("提示"), MB_OK);
+    }
+    else
+    {
+        SendMessage(gWindows[IDC_LABEL], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_UTF8], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_GBK], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_EDIT], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_ENC], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_DEC], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_FORMAT], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_TIME2STR], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+        SendMessage(gWindows[IDC_STR2TIME], WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0));
+    }
+
+    CheckRadioButton(hwnd, IDC_UTF8, IDC_GBK, IDC_UTF8);
+	SendMessage(gWindows[IDC_EDIT], EM_SETLIMITTEXT, (WPARAM)0x7FFFFFFE, 0);
+    SetFocus(gWindows[IDC_EDIT]);
+}
+
+static void OnSizing(HWND hwnd, WPARAM fsSide, LPRECT lpRect)
+{
+    long width, height;
+
+    width = lpRect->right - lpRect->left;
+    height = lpRect->bottom - lpRect->top;
+    if (width < MIN_WIDTH)
+    {
+        if (fsSide & WMSZ_LEFT || fsSide & WMSZ_TOPLEFT || fsSide & WMSZ_BOTTOMLEFT)
+        {
+            lpRect->left = lpRect->right - MIN_WIDTH;
+        }
+        else if (fsSide & WMSZ_RIGHT || fsSide & WMSZ_TOPRIGHT || fsSide & WMSZ_BOTTOMRIGHT)
+        {
+            lpRect->right = lpRect->left + MIN_WIDTH;
+        }
+    }
+    if (height < MIN_HEIGHT)
+    {
+        if (fsSide & WMSZ_TOP || fsSide & WMSZ_TOPLEFT || fsSide & WMSZ_TOPRIGHT)
+        {
+            lpRect->top = lpRect->bottom - MIN_HEIGHT;
+        }
+        else if (fsSide & WMSZ_BOTTOM || fsSide & WMSZ_BOTTOMLEFT || fsSide & WMSZ_BOTTOMRIGHT)
+        {
+            lpRect->bottom = lpRect->top + MIN_HEIGHT;
+        }
+    }
+}
+
+static void OnSize(HWND hwnd)
+{
+    RECT rect = {};
+    long topRowT, bottomRowL, bottomRowT, bodyL, bodyT, bodyW, bodyH;
+
+    GetClientRect(hwnd, &rect);
+    bottomRowL = bodyL = rect.left + FRAME_PADDING;
+    topRowT = rect.top + FRAME_PADDING;
+    bottomRowT = rect.bottom - FRAME_PADDING - BOTTOM_ROW_HEIGHT;
+    bodyT = topRowT + TOP_ROW_HEIGHT + ROW_PADDING;
+    bodyW = rect.right - rect.left - FRAME_PADDING - FRAME_PADDING;
+    bodyH = bottomRowT - ROW_PADDING - bodyT;
+    MoveWindow(gWindows[IDC_ENC], bottomRowL, bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, TRUE);
+    MoveWindow(gWindows[IDC_DEC], bottomRowL + (BOTTOM_ITEM_WIDTH + BOTTOM_ITEM_SPACE), bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, TRUE);
+    MoveWindow(gWindows[IDC_FORMAT], bottomRowL, bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, TRUE);
+    MoveWindow(gWindows[IDC_TIME2STR], bottomRowL, bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, TRUE);
+    MoveWindow(gWindows[IDC_STR2TIME], bottomRowL + (BOTTOM_ITEM_WIDTH + BOTTOM_ITEM_SPACE), bottomRowT, BOTTOM_ITEM_WIDTH, BOTTOM_ROW_HEIGHT, TRUE);
+    MoveWindow(gWindows[IDC_EDIT], bodyL, bodyT, bodyW, bodyH, TRUE);
+}
+
+static void OnMenuClicked(HWND hwnd, WORD id)
+{
+    if (id == IDM_ABOUT)
+    {
+        MessageBox(hwnd, _T("快捷键\r\nCtrl + A: 全选\r\nCtrl + Q: 退出\r\nCtrl + F: 切换焦点\r\nCtrl + T: 切换UTF-8\r\nCtrl + G: 切换GBK\r\n[Weicky 2019]"), _T("提示"), MB_OK);
+        return;
+    }
+    SetMenuDefaultItem(gMenu, id, FALSE);
+    EnableWindow(gWindows[IDC_UTF8], id == IDM_TIME ? FALSE : TRUE);
+    EnableWindow(gWindows[IDC_GBK], id == IDM_JSON || id == IDM_TIME ? FALSE : TRUE);
+    ShowWindow(gWindows[IDC_ENC], id == IDM_BASE64 || id == IDM_URL || id == IDM_MD5 || id == IDM_SHA1 ? SW_SHOW : SW_HIDE);
+    ShowWindow(gWindows[IDC_DEC], id == IDM_BASE64 || id == IDM_URL ? SW_SHOW : SW_HIDE);
+    ShowWindow(gWindows[IDC_FORMAT], id == IDM_JSON ? SW_SHOW : SW_HIDE);
+    ShowWindow(gWindows[IDC_TIME2STR], id == IDM_TIME ? SW_SHOW : SW_HIDE);
+    ShowWindow(gWindows[IDC_STR2TIME], id == IDM_TIME ? SW_SHOW : SW_HIDE);
+    if (id == IDM_JSON)
+    {
+        CheckRadioButton(hwnd, IDC_UTF8, IDC_GBK, IDC_UTF8);
+    }
+    if (id == IDM_TIME)
+    {
+        char ts[32] = "";
+        TCHAR *wts;
+        int wts_len;
+
+        sprintf(ts, "%ld", time(NULL));
+        wts = acp_to_wchar(ts, strlen(ts), &wts_len);
+        SetWindowText(gWindows[IDC_EDIT], wts);
+        free(wts);
+    }
+    DrawMenuBar(gWindows[0]);
+    gLastMenu = id;
+}
+
+static void OnAccelerator(HWND hwnd, WORD id)
+{
+    switch (id)
+    {
+        case IDD_SELECT: //ctrl + A
+            SendMessage(gWindows[IDC_EDIT], EM_SETSEL, (WPARAM)0, (LPARAM)-1);
+            break;
+
+        case IDD_QUIT: //ctrl + Q
+            if (MessageBox(hwnd, _T("确定要退出吗?"), _T("提示"), MB_YESNO) == IDYES)
+            {
+                DestroyWindow(hwnd);
+            }
+            break;
+
+        case IDD_FOCUS: //ctrl + T
+            while (1)
+            {
+                gLastFocus = gLastFocus == IDC_STR2TIME ? IDC_EDIT : gLastFocus + 1;
+                if (gLastFocus == IDC_LABEL || gLastFocus == IDC_UTF8 || gLastFocus == IDC_GBK || IsWindowEnabled(gWindows[gLastFocus]) == FALSE || IsWindowVisible(gWindows[gLastFocus]) == FALSE)
+                {
+                    continue;
+                }
+                SetFocus(gWindows[gLastFocus]);
+                break;
+            }
+            break;
+
+        case IDD_UTF8:
+            if (IsWindowEnabled(gWindows[IDC_UTF8]) == TRUE)
+            {
+                CheckRadioButton(hwnd, IDC_UTF8, IDC_GBK, IDC_UTF8);
+            }
+            break;
+
+        case IDD_GBK:
+            if (IsWindowEnabled(gWindows[IDC_GBK]) == TRUE)
+            {
+                CheckRadioButton(hwnd, IDC_UTF8, IDC_GBK, IDC_GBK);
+            }
+            break;
+    }
+}
+
+static void OnClicked(HWND hwnd, WORD id)
+{
+    switch (id)
+    {
+        case IDC_UTF8:
+            CheckRadioButton(hwnd, IDC_UTF8, IDC_GBK, IDC_UTF8);
+            gUTF8 = 1;
+            break;
+
+        case IDC_GBK:
+            CheckRadioButton(hwnd, IDC_UTF8, IDC_GBK, IDC_GBK);
+            gUTF8 = 0;
+            break;
+
+        case IDC_ENC:
+            ButtonOnClicked(hwnd, id);
+            break;
+
+        case IDC_DEC:
+            ButtonOnClicked(hwnd, id);
+            break;
+
+        case IDC_FORMAT:
+            ButtonOnClicked(hwnd, id);
+            break;
+
+        case IDC_TIME2STR:
+            ButtonOnClicked(hwnd, id);
+            break;
+
+        case IDC_STR2TIME:
+            ButtonOnClicked(hwnd, id);
+            break;
+    }
+}
+
+static void ButtonOnClicked(HWND hwnd, WORD id)
+{
+    TCHAR *input, *output;
+    int inputLen, outputLen;
+
+    inputLen = GetWindowTextLength(gWindows[IDC_EDIT]);
+    if (inputLen)
+    {
+        input = (TCHAR *)calloc(inputLen + 1, sizeof(TCHAR));
+        GetWindowText(gWindows[IDC_EDIT], input, inputLen + 1);
+
+        switch (gLastMenu)
+        {
+            case IDM_BASE64:
+                if (id == IDC_ENC)
+                {
+                    output = base64_encode_string(input, inputLen, &outputLen, gUTF8);
+                }
+                else if (id == IDC_DEC)
+                {
+                    output = base64_decode_string(input, inputLen, &outputLen, gUTF8);
+                }
+                else
+                {
+                    return;
+                }
+                break;
+
+            case IDM_URL:
+                if (id == IDC_ENC)
+                {
+                    output = url_encode_string(input, inputLen, &outputLen, gUTF8);
+                }
+                else if (id == IDC_DEC)
+                {
+                    output = url_decode_string(input, inputLen, &outputLen, gUTF8);
+                }
+                else
+                {
+                    return;
+                }
+                break;
+
+            case IDM_MD5:
+                output = md5_string(input, inputLen, &outputLen, gUTF8);
+                break;
+
+            case IDM_SHA1:
+                output = sha1_string(input, inputLen, &outputLen, gUTF8);
+                break;
+
+            case IDM_JSON:
+                output = json_format(input, inputLen, &outputLen, gUTF8);
+                break;
+
+            case IDM_TIME:
+                if (id == IDC_TIME2STR)
+                {
+                    output = time_convert_to_string(input, inputLen, &outputLen);
+                }
+                else if (id == IDC_STR2TIME)
+                {
+                    output = string_convert_to_time(input, inputLen, &outputLen);
+                }
+                else
+                {
+                    return;
+                }
+                break;
+        }
+
+        //输出&清理
+        SetWindowText(gWindows[IDC_EDIT], output);
+        free(output);
+        free(input);
+    }
+}
+
+static char *nl_unix2win(const char *in, char *out)
+{
+    char *cpy, c;
+
+    cpy = out;
+    while ((c = *in++))
+    {
+        if (c == '\r' && *in == '\n')
+        {
+            *cpy++ = c;
+            *cpy++ = *in++;
+        }
+        else if (c == '\n')
+        {
+            *cpy++ = '\r';
+        }
+        *cpy++ = c;
+    }
+    return out;
+}
+
+static char *wchar_to_utf8(const TCHAR *in, int in_len, int *out_len)
+{
+    char *out;
+
+    *out_len = WideCharToMultiByte(CP_UTF8, 0, in, in_len, NULL, 0, NULL, NULL);
+    if (*out_len == 0)
+    {
+        return NULL;
+    }
+    out = (char *)calloc(*out_len + 1, 1);
+    WideCharToMultiByte(CP_UTF8, 0, in, in_len, out, *out_len, NULL, NULL);
+    return out;
+}
+
+static TCHAR *utf8_to_wchar(const char *in, int in_len, int *out_len)
+{
+    TCHAR *out;
+
+    *out_len = MultiByteToWideChar(CP_UTF8, 0, in, in_len, NULL, 0);
+    if (*out_len == 0)
+    {
+        return NULL;
+    }
+    out = (TCHAR *)calloc((*out_len + 1) * sizeof(TCHAR), 1);
+    MultiByteToWideChar(CP_UTF8, 0, in, in_len, out, *out_len);
+    return out;
+}
+
+static char *wchar_to_acp(const TCHAR *in, int in_len, int *out_len)
+{
+    char *out;
+
+    *out_len = WideCharToMultiByte(CP_ACP, 0, in, in_len, NULL, 0, NULL, NULL);
+    if (*out_len == 0)
+    {
+        return NULL;
+    }
+    out = (char *)calloc(*out_len + 1, 1);
+    WideCharToMultiByte(CP_ACP, 0, in, in_len, out, *out_len, NULL, NULL);
+    return out;
+}
+
+static TCHAR *acp_to_wchar(const char *in, int in_len, int *out_len)
+{
+    TCHAR *out;
+
+    *out_len = MultiByteToWideChar(CP_ACP, 0, in, in_len, NULL, 0);
+    if (*out_len == 0)
+    {
+        return NULL;
+    }
+    out = (TCHAR *)calloc((*out_len + 1) * sizeof(TCHAR), 1);
+    MultiByteToWideChar(CP_ACP, 0, in, in_len, out, *out_len);
+    return out;
+}
+
+static TCHAR *base64_encode_string(const TCHAR *in, int in_len, int *out_len, int utf8)
+{
+    char *cpy, *res;
+    int cpy_len, res_len;
+    TCHAR *out;
+
+    if (utf8)
+    {
+        cpy = wchar_to_utf8(in, in_len, &cpy_len);
+    }
+    else
+    {
+        cpy = wchar_to_acp(in, in_len, &cpy_len);
+    }
+    res_len = cpy_len * 3;
+    res = (char *)calloc(res_len + 1, 1);
+    base64_encode(cpy, cpy_len, res, (size_t *)&res_len, 0);
+    free(cpy);
+    if (utf8)
+    {
+        out = utf8_to_wchar(res, res_len, out_len);
+    }
+    else
+    {
+        out = acp_to_wchar(res, res_len, out_len);
+    }
+    free(res);
+    return out;
+}
+
+static TCHAR *base64_decode_string(const TCHAR *in, int in_len, int *out_len, int utf8)
+{
+    char *cpy, *res;
+    int cpy_len, res_len;
+    TCHAR *out;
+
+    if (utf8)
+    {
+        cpy = wchar_to_utf8(in, in_len, &cpy_len);
+    }
+    else
+    {
+        cpy = wchar_to_acp(in, in_len, &cpy_len);
+    }
+    res_len = cpy_len * 3;
+    res = (char *)calloc(res_len + 1, 1);
+    base64_decode(cpy, cpy_len, res, (size_t *)&res_len, 0);
+    free(cpy);
+    if (utf8)
+    {
+        out = utf8_to_wchar(res, res_len, out_len);
+    }
+    else
+    {
+        out = acp_to_wchar(res, res_len, out_len);
+    }
+    free(res);
+    return out;
+}
+
+static TCHAR *url_encode_string(const TCHAR *in, int in_len, int *out_len, int utf8)
+{
+    char *cpy, *res;
+    int cpy_len, res_len;
+    TCHAR *out;
+
+    if (utf8)
+    {
+        cpy = wchar_to_utf8(in, in_len, &cpy_len);
+    }
+    else
+    {
+        cpy = wchar_to_acp(in, in_len, &cpy_len);
+    }
+    res_len = cpy_len * 3;
+    res = (char *)calloc(res_len + 1, 1);
+    url_encode(cpy, cpy_len, res, (size_t *)&res_len);
+    free(cpy);
+    if (utf8)
+    {
+        out = utf8_to_wchar(res, res_len, out_len);
+    }
+    else
+    {
+        out = acp_to_wchar(res, res_len, out_len);
+    }
+    free(res);
+    return out;
+}
+
+static TCHAR *url_decode_string(const TCHAR *in, int in_len, int *out_len, int utf8)
+{
+    char *cpy, *res;
+    int cpy_len, res_len;
+    TCHAR *out;
+
+    if (utf8)
+    {
+        cpy = wchar_to_utf8(in, in_len, &cpy_len);
+    }
+    else
+    {
+        cpy = wchar_to_acp(in, in_len, &cpy_len);
+    }
+    res_len = cpy_len * 3;
+    res = (char *)calloc(res_len + 1, 1);
+    url_decode(cpy, cpy_len, res, (size_t *)&res_len);
+    free(cpy);
+    if (utf8)
+    {
+        out = utf8_to_wchar(res, res_len, out_len);
+    }
+    else
+    {
+        out = acp_to_wchar(res, res_len, out_len);
+    }
+    free(res);
+    return out;
+}
+
+static TCHAR *md5_string(const TCHAR *in, int in_len, int *out_len, int utf8)
+{
+    char *cpy, *res;
+    int cpy_len, res_len;
+    TCHAR *out;
+
+    if (utf8)
+    {
+        cpy = wchar_to_utf8(in, in_len, &cpy_len);
+    }
+    else
+    {
+        cpy = wchar_to_acp(in, in_len, &cpy_len);
+    }
+    res_len = 32;
+    res = (char *)calloc(res_len + 1, 1);
+    md5(cpy, cpy_len, res);
+    free(cpy);
+    if (utf8)
+    {
+        out = utf8_to_wchar(res, res_len, out_len);
+    }
+    else
+    {
+        out = acp_to_wchar(res, res_len, out_len);
+    }
+    free(res);
+    return out;
+}
+
+static TCHAR *sha1_string(const TCHAR *in, int in_len, int *out_len, int utf8)
+{
+    char *cpy, *res;
+    int cpy_len, res_len;
+    TCHAR *out;
+
+    if (utf8)
+    {
+        cpy = wchar_to_utf8(in, in_len, &cpy_len);
+    }
+    else
+    {
+        cpy = wchar_to_acp(in, in_len, &cpy_len);
+    }
+    res_len = 40;
+    res = (char *)calloc(res_len + 1, 1);
+    sha1(cpy, cpy_len, res);
+    free(cpy);
+    if (utf8)
+    {
+        out = utf8_to_wchar(res, res_len, out_len);
+    }
+    else
+    {
+        out = acp_to_wchar(res, res_len, out_len);
+    }
+    free(res);
+    return out;
+}
+
+static TCHAR *json_format(const TCHAR *in, int in_len, int *out_len, int utf8)
+{
+    char *cpy, *json, *json_cpy;
+    int cpy_len;
+    JSON_Value *obj;
+    TCHAR *out;
+
+    if (utf8)
+    {
+        cpy = wchar_to_utf8(in, in_len, &cpy_len);
+    }
+    else
+    {
+        cpy = wchar_to_acp(in, in_len, &cpy_len);
+    }
+    obj = json_parse_string_with_comments(cpy);
+    free(cpy);
+    if (obj == NULL)
+    {
+        *out_len = 5;
+        out = (TCHAR *)calloc(*out_len, sizeof(TCHAR));
+        wcscpy(out, _T("null"));
+    }
+    else
+    {
+        json = json_serialize_to_string_pretty(obj);
+        json_value_free(obj);
+        json_cpy = (char *)calloc(strlen(json) * 2 + 1, 1);
+        nl_unix2win(json, json_cpy);
+        json_free_serialized_string(json);
+        out = utf8_to_wchar(json_cpy, strlen(json_cpy), out_len);
+        free(json_cpy);
+    }
+    return out;
+}
+
+static TCHAR *time_convert_to_string(const TCHAR *in, int in_len, int *out_len)
+{
+    char *cpy, res[32] = "";
+    int cpy_len;
+    time_t ts;
+    TCHAR *out;
+    timelib_time *t;
+
+    //wchar*转int
+    cpy = wchar_to_acp(in, in_len, &cpy_len);
+    ts = (time_t)atoi(cpy);
+    free(cpy);
+    //int转timelib_time(struct)
+	t = timelib_time_ctor();
+    t->tz_info = gTimeZoneInfo;
+    t->zone_type = TIMELIB_ZONETYPE_ID;
+    timelib_unixtime2local(t, ts);
+    //timelib_time转char*
+    sprintf(res, "%ld-%02ld-%02ld %02ld:%02ld:%02ld", (long)t->y, (long)t->m, (long)t->d, (long)t->h, (long)t->i, (long)t->s);
+    timelib_time_dtor(t);
+    //char*转wchar*
+    out = acp_to_wchar(res, strlen(res), out_len);
+    return out;
+}
+
+static TCHAR *string_convert_to_time(const TCHAR *in, int in_len, int *out_len)
+{
+    char *cpy, res[32] = "";
+    int cpy_len, error1, error2;
+    int64_t ts;
+    TCHAR *out;
+    timelib_time *t, *now;
+	timelib_error_container *error;
+
+    //wchar*转char*
+    cpy = wchar_to_acp(in, in_len, &cpy_len);
+    ts = (time_t)atoi(cpy);
+    free(cpy);
+    //char*转timelib_time(struct)再转int64_t
+    now = timelib_time_ctor();
+	now->tz_info = gTimeZoneInfo;
+	now->zone_type = TIMELIB_ZONETYPE_ID;
+	timelib_unixtime2local(now, time(NULL));
+    t = timelib_strtotime(cpy, strlen(cpy), &error, timelib_builtin_db(), (timelib_tz_get_wrapper)my_date_parse_tzfile_wrapper);
+    error1 = error->error_count;
+    timelib_error_container_dtor(error);
+    timelib_fill_holes(t, now, TIMELIB_NO_CLONE);
+	timelib_update_ts(t, gTimeZoneInfo);
+	ts = (int64_t)timelib_date_to_int(t, &error2);
+	timelib_time_dtor(now);
+	timelib_time_dtor(t);
+	//将int64_t转为char*
+	if (error1 || error2)
+    {
+        *out_len = in_len;
+        return in;
+    }
+    else
+    {
+        sprintf(res, "%ld", (long)ts);
+    }
+	//将char*转为TCHAR*
+    out = acp_to_wchar(res, strlen(res), out_len);
+    return out;
+}
+
+static timelib_tzinfo *my_date_parse_tzfile_wrapper(char *formal_tzname, const timelib_tzdb *tzdb)
+{
+	return gTimeZoneInfo;
+}

+ 308 - 0
md5/md5.c

@@ -0,0 +1,308 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security,
+ * Inc. MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Written by Solar Designer <solar at openwall.com> in 2001, and placed
+ * in the public domain.  There's absolutely no warranty.
+ *
+ * This differs from Colin Plumb's older public domain implementation in
+ * that no 32-bit integer data type is required, there's no compile-time
+ * endianness configuration, and the function prototypes match OpenSSL's.
+ * The primary goals are portability and ease of use.
+ *
+ * This implementation is meant to be fast, but not as fast as possible.
+ * Some known optimizations are not included to reduce source code size
+ * and avoid compile-time configuration.
+ */
+
+#include <string.h>
+#include "md5.h"
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z)			((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z)			((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z)			((x) ^ (y) ^ (z))
+#define I(x, y, z)			((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+	(a) += f((b), (c), (d)) + (x) + (t); \
+	(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+	(a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization.  Nothing will break if it
+ * doesn't work.
+ */
+#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
+# define SET(n) \
+	(*(uint32_t *)&ptr[(n) * 4])
+# define GET(n) \
+	SET(n)
+#else
+# define SET(n) \
+	(ctx->block[(n)] = \
+	(uint32_t)ptr[(n) * 4] | \
+	((uint32_t)ptr[(n) * 4 + 1] << 8) | \
+	((uint32_t)ptr[(n) * 4 + 2] << 16) | \
+	((uint32_t)ptr[(n) * 4 + 3] << 24))
+# define GET(n) \
+	(ctx->block[(n)])
+#endif
+
+void make_digest(char *md5str, const unsigned char *digest)
+{
+	make_digest_ex(md5str, digest, 16);
+}
+
+void make_digest_ex(char *md5str, const unsigned char *digest, int len)
+{
+	static const char hexits[17] = "0123456789abcdef";
+	int i;
+
+	for (i = 0; i < len; i++) {
+		md5str[i * 2]       = hexits[digest[i] >> 4];
+		md5str[(i * 2) + 1] = hexits[digest[i] &  0x0F];
+	}
+	md5str[len * 2] = '\0';
+}
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters.  There are no alignment requirements.
+ */
+static const void *body(MD5_CTX *ctx, const void *data, size_t size)
+{
+	const unsigned char *ptr;
+	uint32_t a, b, c, d;
+	uint32_t saved_a, saved_b, saved_c, saved_d;
+
+	ptr = data;
+
+	a = ctx->a;
+	b = ctx->b;
+	c = ctx->c;
+	d = ctx->d;
+
+	do {
+		saved_a = a;
+		saved_b = b;
+		saved_c = c;
+		saved_d = d;
+
+/* Round 1 */
+		STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+		STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+		STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+		STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+		STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+		STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+		STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+		STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+		STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+		STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+		STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+		STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+		STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+		STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+		STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+		STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+		STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+		STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+		STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+		STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+		STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+		STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+		STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+		STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+		STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+		STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+		STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+		STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+		STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+		STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+		STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+		STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+		STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+		STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+		STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+		STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+		STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+		STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+		STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+		STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+		STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+		STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+		STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+		STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+		STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+		STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+		STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+		STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+		STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+		STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+		STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+		STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+		STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+		STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+		STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+		STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+		STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+		STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+		STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+		STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+		STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+		STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+		STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+		STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+		a += saved_a;
+		b += saved_b;
+		c += saved_c;
+		d += saved_d;
+
+		ptr += 64;
+	} while (size -= 64);
+
+	ctx->a = a;
+	ctx->b = b;
+	ctx->c = c;
+	ctx->d = d;
+
+	return ptr;
+}
+
+void MD5Init(MD5_CTX *ctx)
+{
+	ctx->a = 0x67452301;
+	ctx->b = 0xefcdab89;
+	ctx->c = 0x98badcfe;
+	ctx->d = 0x10325476;
+
+	ctx->lo = 0;
+	ctx->hi = 0;
+}
+
+void MD5Update(MD5_CTX *ctx, const void *data, size_t size)
+{
+	uint32_t saved_lo;
+	uint32_t used, free;
+
+	saved_lo = ctx->lo;
+	if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) {
+		ctx->hi++;
+	}
+	ctx->hi += size >> 29;
+
+	used = saved_lo & 0x3f;
+
+	if (used) {
+		free = 64 - used;
+
+		if (size < free) {
+			memcpy(&ctx->buffer[used], data, size);
+			return;
+		}
+
+		memcpy(&ctx->buffer[used], data, free);
+		data = (unsigned char *)data + free;
+		size -= free;
+		body(ctx, ctx->buffer, 64);
+	}
+
+	if (size >= 64) {
+		data = body(ctx, data, size & ~(size_t)0x3f);
+		size &= 0x3f;
+	}
+
+	memcpy(ctx->buffer, data, size);
+}
+
+void MD5Final(unsigned char *result, MD5_CTX *ctx)
+{
+	uint32_t used, free;
+
+	used = ctx->lo & 0x3f;
+
+	ctx->buffer[used++] = 0x80;
+
+	free = 64 - used;
+
+	if (free < 8) {
+		memset(&ctx->buffer[used], 0, free);
+		body(ctx, ctx->buffer, 64);
+		used = 0;
+		free = 64;
+	}
+
+	memset(&ctx->buffer[used], 0, free - 8);
+
+	ctx->lo <<= 3;
+	ctx->buffer[56] = ctx->lo;
+	ctx->buffer[57] = ctx->lo >> 8;
+	ctx->buffer[58] = ctx->lo >> 16;
+	ctx->buffer[59] = ctx->lo >> 24;
+	ctx->buffer[60] = ctx->hi;
+	ctx->buffer[61] = ctx->hi >> 8;
+	ctx->buffer[62] = ctx->hi >> 16;
+	ctx->buffer[63] = ctx->hi >> 24;
+
+	body(ctx, ctx->buffer, 64);
+
+	result[0] = ctx->a;
+	result[1] = ctx->a >> 8;
+	result[2] = ctx->a >> 16;
+	result[3] = ctx->a >> 24;
+	result[4] = ctx->b;
+	result[5] = ctx->b >> 8;
+	result[6] = ctx->b >> 16;
+	result[7] = ctx->b >> 24;
+	result[8] = ctx->c;
+	result[9] = ctx->c >> 8;
+	result[10] = ctx->c >> 16;
+	result[11] = ctx->c >> 24;
+	result[12] = ctx->d;
+	result[13] = ctx->d >> 8;
+	result[14] = ctx->d >> 16;
+	result[15] = ctx->d >> 24;
+
+	memset((void*)ctx, 0, sizeof(*ctx));
+}
+
+void md5(void *data, size_t data_size, void *hashstr)
+{
+	MD5_CTX context;
+	unsigned char digest[16];
+
+	MD5Init(&context);
+	MD5Update(&context, data, data_size);
+	MD5Final(digest, &context);
+	make_digest_ex(hashstr, digest, 16);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */

+ 43 - 0
md5/md5.h

@@ -0,0 +1,43 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+
+void make_digest(char *md5str, const unsigned char *digest);
+void make_digest_ex(char *md5str, const unsigned char *digest, int len);
+
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security,
+ * Inc. MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Written by Solar Designer <solar at openwall.com> in 2001, and placed
+ * in the public domain.  There's absolutely no warranty.
+ *
+ * See md5.c for more information.
+ */
+
+/* MD5 context. */
+typedef struct {
+	uint32_t lo, hi;
+	uint32_t a, b, c, d;
+	unsigned char buffer[64];
+	uint32_t block[16];
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *ctx);
+void MD5Update(MD5_CTX *ctx, const void *data, size_t size);
+void MD5Final(unsigned char *result, MD5_CTX *ctx);
+
+void md5(void *data, size_t data_size, void *hashstr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

File diff suppressed because it is too large
+ 2080 - 0
parson/parson.c


+ 240 - 0
parson/parson.h

@@ -0,0 +1,240 @@
+/*
+ SPDX-License-Identifier: MIT
+
+ Parson ( http://kgabis.github.com/parson/ )
+ Copyright (c) 2012 - 2019 Krzysztof Gabis
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in 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:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ 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
+ AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ THE SOFTWARE.
+*/
+
+#ifndef parson_parson_h
+#define parson_parson_h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stddef.h>   /* size_t */
+
+/* Types and enums */
+typedef struct json_object_t JSON_Object;
+typedef struct json_array_t  JSON_Array;
+typedef struct json_value_t  JSON_Value;
+
+enum json_value_type {
+    JSONError   = -1,
+    JSONNull    = 1,
+    JSONString  = 2,
+    JSONNumber  = 3,
+    JSONObject  = 4,
+    JSONArray   = 5,
+    JSONBoolean = 6
+};
+typedef int JSON_Value_Type;
+
+enum json_result_t {
+    JSONSuccess = 0,
+    JSONFailure = -1
+};
+typedef int JSON_Status;
+
+typedef void * (*JSON_Malloc_Function)(size_t);
+typedef void   (*JSON_Free_Function)(void *);
+
+/* Call only once, before calling any other function from parson API. If not called, malloc and free
+   from stdlib will be used for all allocations */
+void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
+
+/* Sets if slashes should be escaped or not when serializing JSON. By default slashes are escaped.
+ This function sets a global setting and is not thread safe. */
+void json_set_escape_slashes(int escape_slashes);
+
+/* Parses first JSON value in a file, returns NULL in case of error */
+JSON_Value * json_parse_file(const char *filename);
+
+/* Parses first JSON value in a file and ignores comments (/ * * / and //),
+   returns NULL in case of error */
+JSON_Value * json_parse_file_with_comments(const char *filename);
+
+/*  Parses first JSON value in a string, returns NULL in case of error */
+JSON_Value * json_parse_string(const char *string);
+
+/*  Parses first JSON value in a string and ignores comments (/ * * / and //),
+    returns NULL in case of error */
+JSON_Value * json_parse_string_with_comments(const char *string);
+
+/* Serialization */
+size_t      json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
+JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
+JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
+char *      json_serialize_to_string(const JSON_Value *value);
+
+/* Pretty serialization */
+size_t      json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
+JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
+JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);
+char *      json_serialize_to_string_pretty(const JSON_Value *value);
+
+void        json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
+
+/* Comparing */
+int  json_value_equals(const JSON_Value *a, const JSON_Value *b);
+
+/* Validation
+   This is *NOT* JSON Schema. It validates json by checking if object have identically
+   named fields with matching types.
+   For example schema {"name":"", "age":0} will validate
+   {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
+   but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
+   In case of arrays, only first value in schema is checked against all values in tested array.
+   Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
+   null validates values of every type.
+ */
+JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
+
+/*
+ * JSON Object
+ */
+JSON_Value  * json_object_get_value  (const JSON_Object *object, const char *name);
+const char  * json_object_get_string (const JSON_Object *object, const char *name);
+JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
+JSON_Array  * json_object_get_array  (const JSON_Object *object, const char *name);
+double        json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
+int           json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
+
+/* dotget functions enable addressing values with dot notation in nested objects,
+ just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
+ Because valid names in JSON can contain dots, some values may be inaccessible
+ this way. */
+JSON_Value  * json_object_dotget_value  (const JSON_Object *object, const char *name);
+const char  * json_object_dotget_string (const JSON_Object *object, const char *name);
+JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
+JSON_Array  * json_object_dotget_array  (const JSON_Object *object, const char *name);
+double        json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
+int           json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
+
+/* Functions to get available names */
+size_t        json_object_get_count   (const JSON_Object *object);
+const char  * json_object_get_name    (const JSON_Object *object, size_t index);
+JSON_Value  * json_object_get_value_at(const JSON_Object *object, size_t index);
+JSON_Value  * json_object_get_wrapping_value(const JSON_Object *object);
+
+/* Functions to check if object has a value with a specific name. Returned value is 1 if object has
+ * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */
+int json_object_has_value        (const JSON_Object *object, const char *name);
+int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
+
+int json_object_dothas_value        (const JSON_Object *object, const char *name);
+int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
+
+/* Creates new name-value pair or frees and replaces old value with a new one.
+ * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
+JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
+JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
+JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
+JSON_Status json_object_set_null(JSON_Object *object, const char *name);
+
+/* Works like dotget functions, but creates whole hierarchy if necessary.
+ * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
+JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
+JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
+JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
+JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
+
+/* Frees and removes name-value pair */
+JSON_Status json_object_remove(JSON_Object *object, const char *name);
+
+/* Works like dotget function, but removes name-value pair only on exact match. */
+JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
+
+/* Removes all name-value pairs in object */
+JSON_Status json_object_clear(JSON_Object *object);
+
+/*
+ *JSON Array
+ */
+JSON_Value  * json_array_get_value  (const JSON_Array *array, size_t index);
+const char  * json_array_get_string (const JSON_Array *array, size_t index);
+JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
+JSON_Array  * json_array_get_array  (const JSON_Array *array, size_t index);
+double        json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */
+int           json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
+size_t        json_array_get_count  (const JSON_Array *array);
+JSON_Value  * json_array_get_wrapping_value(const JSON_Array *array);
+
+/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
+ * Order of values in array may change during execution.  */
+JSON_Status json_array_remove(JSON_Array *array, size_t i);
+
+/* Frees and removes from array value at given index and replaces it with given one.
+ * Does nothing and returns JSONFailure if index doesn't exist.
+ * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
+JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
+JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
+JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
+JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
+
+/* Frees and removes all values from array */
+JSON_Status json_array_clear(JSON_Array *array);
+
+/* Appends new value at the end of array.
+ * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
+JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
+JSON_Status json_array_append_string(JSON_Array *array, const char *string);
+JSON_Status json_array_append_number(JSON_Array *array, double number);
+JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
+JSON_Status json_array_append_null(JSON_Array *array);
+
+/*
+ *JSON Value
+ */
+JSON_Value * json_value_init_object (void);
+JSON_Value * json_value_init_array  (void);
+JSON_Value * json_value_init_string (const char *string); /* copies passed string */
+JSON_Value * json_value_init_number (double number);
+JSON_Value * json_value_init_boolean(int boolean);
+JSON_Value * json_value_init_null   (void);
+JSON_Value * json_value_deep_copy   (const JSON_Value *value);
+void         json_value_free        (JSON_Value *value);
+
+JSON_Value_Type json_value_get_type   (const JSON_Value *value);
+JSON_Object *   json_value_get_object (const JSON_Value *value);
+JSON_Array  *   json_value_get_array  (const JSON_Value *value);
+const char  *   json_value_get_string (const JSON_Value *value);
+double          json_value_get_number (const JSON_Value *value);
+int             json_value_get_boolean(const JSON_Value *value);
+JSON_Value  *   json_value_get_parent (const JSON_Value *value);
+
+/* Same as above, but shorter */
+JSON_Value_Type json_type   (const JSON_Value *value);
+JSON_Object *   json_object (const JSON_Value *value);
+JSON_Array  *   json_array  (const JSON_Value *value);
+const char  *   json_string (const JSON_Value *value);
+double          json_number (const JSON_Value *value);
+int             json_boolean(const JSON_Value *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 32 - 0
resources.h

@@ -0,0 +1,32 @@
+#ifndef _RESOURCES_H
+#define _RESOURCES_H
+
+#define IDI_APP 2
+
+#define IDC_EDIT 10
+#define IDC_LABEL 11
+#define IDC_UTF8 12
+#define IDC_GBK 13
+#define IDC_ENC 21
+#define IDC_DEC 22
+#define IDC_FORMAT 23
+#define IDC_TIME2STR 24
+#define IDC_STR2TIME 25
+
+#define IDM_MAIN 100
+#define IDM_BASE64 101
+#define IDM_URL 102
+#define IDM_MD5 103
+#define IDM_SHA1 104
+#define IDM_JSON 105
+#define IDM_TIME 106
+#define IDM_ABOUT 107
+
+#define IDD_ACCEL 200
+#define IDD_SELECT 201
+#define IDD_QUIT 202
+#define IDD_FOCUS 203
+#define IDD_UTF8 204
+#define IDD_GBK 205
+
+#endif

+ 25 - 0
resources.rc

@@ -0,0 +1,25 @@
+
+#include "winuser.h"
+#include "resources.h"
+
+IDI_APP ICON "32x32.ico"
+
+IDM_MAIN MENUEX
+{
+    MENUITEM "&Base64 ", IDM_BASE64, MFT_STRING, MFS_DEFAULT
+    MENUITEM "&URL ", IDM_URL, MFT_STRING, 0
+    MENUITEM "&MD5 ", IDM_MD5, MFT_STRING, 0
+    MENUITEM "&SHA1 ", IDM_SHA1, MFT_STRING, 0
+    MENUITEM "&JSON ", IDM_JSON, MFT_STRING, 0
+    MENUITEM "&TimeStamp ", IDM_TIME, MFT_STRING, 0
+    MENUITEM "&?", IDM_ABOUT, MFT_STRING, 0
+}
+
+IDD_ACCEL ACCELERATORS
+{
+    "^a", IDD_SELECT
+    "^q", IDD_QUIT
+    "^f", IDD_FOCUS
+    "^t", IDD_UTF8
+    "^g", IDD_GBK
+}

+ 326 - 0
sha1/sha1.c

@@ -0,0 +1,326 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2018 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Stefan Esser <sesser@php.net>                                |
+   +----------------------------------------------------------------------+
+*/
+
+/* This code is heavily based on the PHP md5 implementation */
+
+#include <string.h>
+#include "sha1.h"
+#include "../md5/md5.h"
+
+void make_sha1_digest(char *sha1str, unsigned char *digest)
+{
+	make_digest_ex(sha1str, digest, 20);
+}
+
+void sha1(void *data, size_t data_len, void *hash)
+{
+	SHA1_CTX context;
+	unsigned char digest[20];
+
+	SHA1Init(&context);
+	SHA1Update(&context, (unsigned char *) data, data_len);
+	SHA1Final(digest, &context);
+	make_digest_ex(hash, digest, 20);
+}
+
+static void SHA1Transform(uint32_t[5], const unsigned char[64]);
+static void SHA1Encode(unsigned char *, uint32_t *, unsigned int);
+static void SHA1Decode(uint32_t *, const unsigned char *, unsigned int);
+
+static const unsigned char PADDING[64] =
+{
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic SHA1 functions.
+ */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((x) ^ (y) ^ (z))
+#define H(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))
+#define I(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* W[i]
+ */
+#define W(i) ( tmp=x[(i-3)&15]^x[(i-8)&15]^x[(i-14)&15]^x[i&15], \
+	(x[i&15]=ROTATE_LEFT(tmp, 1)) )
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ */
+#define FF(a, b, c, d, e, w) { \
+ (e) += F ((b), (c), (d)) + (w) + (uint32_t)(0x5A827999); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+  }
+#define GG(a, b, c, d, e, w) { \
+ (e) += G ((b), (c), (d)) + (w) + (uint32_t)(0x6ED9EBA1); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+  }
+#define HH(a, b, c, d, e, w) { \
+ (e) += H ((b), (c), (d)) + (w) + (uint32_t)(0x8F1BBCDC); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+  }
+#define II(a, b, c, d, e, w) { \
+ (e) += I ((b), (c), (d)) + (w) + (uint32_t)(0xCA62C1D6); \
+ (e) += ROTATE_LEFT ((a), 5); \
+ (b) = ROTATE_LEFT((b), 30); \
+  }
+
+void SHA1Init(SHA1_CTX * context)
+{
+	context->count[0] = context->count[1] = 0;
+	/* Load magic initialization constants.
+	 */
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xefcdab89;
+	context->state[2] = 0x98badcfe;
+	context->state[3] = 0x10325476;
+	context->state[4] = 0xc3d2e1f0;
+}
+
+void SHA1Update(SHA1_CTX * context, const unsigned char *input,
+			   size_t inputLen)
+{
+	unsigned int i, index, partLen;
+
+	/* Compute number of bytes mod 64 */
+	index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+	/* Update number of bits */
+	if ((context->count[0] += ((uint32_t) inputLen << 3))
+		< ((uint32_t) inputLen << 3))
+		context->count[1]++;
+	context->count[1] += ((uint32_t) inputLen >> 29);
+
+	partLen = 64 - index;
+
+	/* Transform as many times as possible.
+	 */
+	if (inputLen >= partLen) {
+		memcpy
+			((unsigned char*) & context->buffer[index], (unsigned char*) input, partLen);
+		SHA1Transform(context->state, context->buffer);
+
+		for (i = partLen; i + 63 < inputLen; i += 64)
+			SHA1Transform(context->state, &input[i]);
+
+		index = 0;
+	} else
+		i = 0;
+
+	/* Buffer remaining input */
+	memcpy
+		((unsigned char*) & context->buffer[index], (unsigned char*) & input[i],
+		 inputLen - i);
+}
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX * context)
+{
+	unsigned char bits[8];
+	unsigned int index, padLen;
+
+	/* Save number of bits */
+	bits[7] = context->count[0] & 0xFF;
+	bits[6] = (context->count[0] >> 8) & 0xFF;
+	bits[5] = (context->count[0] >> 16) & 0xFF;
+	bits[4] = (context->count[0] >> 24) & 0xFF;
+	bits[3] = context->count[1] & 0xFF;
+	bits[2] = (context->count[1] >> 8) & 0xFF;
+	bits[1] = (context->count[1] >> 16) & 0xFF;
+	bits[0] = (context->count[1] >> 24) & 0xFF;
+
+	/* Pad out to 56 mod 64.
+	 */
+	index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+	padLen = (index < 56) ? (56 - index) : (120 - index);
+	SHA1Update(context, PADDING, padLen);
+
+	/* Append length (before padding) */
+	SHA1Update(context, bits, 8);
+
+	/* Store state in digest */
+	SHA1Encode(digest, context->state, 20);
+
+	/* Zeroize sensitive information.
+	 */
+	memset((unsigned char*) context, 0, sizeof(*context));
+}
+
+/* {{{ SHA1Transform
+ * SHA1 basic transformation. Transforms state based on block.
+ */
+static void SHA1Transform(state, block)
+uint32_t state[5];
+const unsigned char block[64];
+{
+	uint32_t a = state[0], b = state[1], c = state[2];
+	uint32_t d = state[3], e = state[4], x[16], tmp;
+
+	SHA1Decode(x, block, 64);
+
+	/* Round 1 */
+	FF(a, b, c, d, e, x[0]);   /* 1 */
+	FF(e, a, b, c, d, x[1]);   /* 2 */
+	FF(d, e, a, b, c, x[2]);   /* 3 */
+	FF(c, d, e, a, b, x[3]);   /* 4 */
+	FF(b, c, d, e, a, x[4]);   /* 5 */
+	FF(a, b, c, d, e, x[5]);   /* 6 */
+	FF(e, a, b, c, d, x[6]);   /* 7 */
+	FF(d, e, a, b, c, x[7]);   /* 8 */
+	FF(c, d, e, a, b, x[8]);   /* 9 */
+	FF(b, c, d, e, a, x[9]);   /* 10 */
+	FF(a, b, c, d, e, x[10]);  /* 11 */
+	FF(e, a, b, c, d, x[11]);  /* 12 */
+	FF(d, e, a, b, c, x[12]);  /* 13 */
+	FF(c, d, e, a, b, x[13]);  /* 14 */
+	FF(b, c, d, e, a, x[14]);  /* 15 */
+	FF(a, b, c, d, e, x[15]);  /* 16 */
+	FF(e, a, b, c, d, W(16));  /* 17 */
+	FF(d, e, a, b, c, W(17));  /* 18 */
+	FF(c, d, e, a, b, W(18));  /* 19 */
+	FF(b, c, d, e, a, W(19));  /* 20 */
+
+	/* Round 2 */
+	GG(a, b, c, d, e, W(20));  /* 21 */
+	GG(e, a, b, c, d, W(21));  /* 22 */
+	GG(d, e, a, b, c, W(22));  /* 23 */
+	GG(c, d, e, a, b, W(23));  /* 24 */
+	GG(b, c, d, e, a, W(24));  /* 25 */
+	GG(a, b, c, d, e, W(25));  /* 26 */
+	GG(e, a, b, c, d, W(26));  /* 27 */
+	GG(d, e, a, b, c, W(27));  /* 28 */
+	GG(c, d, e, a, b, W(28));  /* 29 */
+	GG(b, c, d, e, a, W(29));  /* 30 */
+	GG(a, b, c, d, e, W(30));  /* 31 */
+	GG(e, a, b, c, d, W(31));  /* 32 */
+	GG(d, e, a, b, c, W(32));  /* 33 */
+	GG(c, d, e, a, b, W(33));  /* 34 */
+	GG(b, c, d, e, a, W(34));  /* 35 */
+	GG(a, b, c, d, e, W(35));  /* 36 */
+	GG(e, a, b, c, d, W(36));  /* 37 */
+	GG(d, e, a, b, c, W(37));  /* 38 */
+	GG(c, d, e, a, b, W(38));  /* 39 */
+	GG(b, c, d, e, a, W(39));  /* 40 */
+
+	/* Round 3 */
+	HH(a, b, c, d, e, W(40));  /* 41 */
+	HH(e, a, b, c, d, W(41));  /* 42 */
+	HH(d, e, a, b, c, W(42));  /* 43 */
+	HH(c, d, e, a, b, W(43));  /* 44 */
+	HH(b, c, d, e, a, W(44));  /* 45 */
+	HH(a, b, c, d, e, W(45));  /* 46 */
+	HH(e, a, b, c, d, W(46));  /* 47 */
+	HH(d, e, a, b, c, W(47));  /* 48 */
+	HH(c, d, e, a, b, W(48));  /* 49 */
+	HH(b, c, d, e, a, W(49));  /* 50 */
+	HH(a, b, c, d, e, W(50));  /* 51 */
+	HH(e, a, b, c, d, W(51));  /* 52 */
+	HH(d, e, a, b, c, W(52));  /* 53 */
+	HH(c, d, e, a, b, W(53));  /* 54 */
+	HH(b, c, d, e, a, W(54));  /* 55 */
+	HH(a, b, c, d, e, W(55));  /* 56 */
+	HH(e, a, b, c, d, W(56));  /* 57 */
+	HH(d, e, a, b, c, W(57));  /* 58 */
+	HH(c, d, e, a, b, W(58));  /* 59 */
+	HH(b, c, d, e, a, W(59));  /* 60 */
+
+	/* Round 4 */
+	II(a, b, c, d, e, W(60));  /* 61 */
+	II(e, a, b, c, d, W(61));  /* 62 */
+	II(d, e, a, b, c, W(62));  /* 63 */
+	II(c, d, e, a, b, W(63));  /* 64 */
+	II(b, c, d, e, a, W(64));  /* 65 */
+	II(a, b, c, d, e, W(65));  /* 66 */
+	II(e, a, b, c, d, W(66));  /* 67 */
+	II(d, e, a, b, c, W(67));  /* 68 */
+	II(c, d, e, a, b, W(68));  /* 69 */
+	II(b, c, d, e, a, W(69));  /* 70 */
+	II(a, b, c, d, e, W(70));  /* 71 */
+	II(e, a, b, c, d, W(71));  /* 72 */
+	II(d, e, a, b, c, W(72));  /* 73 */
+	II(c, d, e, a, b, W(73));  /* 74 */
+	II(b, c, d, e, a, W(74));  /* 75 */
+	II(a, b, c, d, e, W(75));  /* 76 */
+	II(e, a, b, c, d, W(76));  /* 77 */
+	II(d, e, a, b, c, W(77));  /* 78 */
+	II(c, d, e, a, b, W(78));  /* 79 */
+	II(b, c, d, e, a, W(79));  /* 80 */
+
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+	state[4] += e;
+
+	/* Zeroize sensitive information. */
+	memset((unsigned char*) x, 0, sizeof(x));
+}
+/* }}} */
+
+/* {{{ SHA1Encode
+   Encodes input (uint32_t) into output (unsigned char). Assumes len is
+   a multiple of 4.
+ */
+static void SHA1Encode(output, input, len)
+unsigned char *output;
+uint32_t *input;
+unsigned int len;
+{
+	unsigned int i, j;
+
+	for (i = 0, j = 0; j < len; i++, j += 4) {
+		output[j] = (unsigned char) ((input[i] >> 24) & 0xff);
+		output[j + 1] = (unsigned char) ((input[i] >> 16) & 0xff);
+		output[j + 2] = (unsigned char) ((input[i] >> 8) & 0xff);
+		output[j + 3] = (unsigned char) (input[i] & 0xff);
+	}
+}
+/* }}} */
+
+/* {{{ SHA1Decode
+   Decodes input (unsigned char) into output (uint32_t). Assumes len is
+   a multiple of 4.
+ */
+static void SHA1Decode(output, input, len)
+uint32_t *output;
+const unsigned char *input;
+unsigned int len;
+{
+	unsigned int i, j;
+
+	for (i = 0, j = 0; j < len; i++, j += 4)
+		output[i] = ((uint32_t) input[j + 3]) | (((uint32_t) input[j + 2]) << 8) |
+			(((uint32_t) input[j + 1]) << 16) | (((uint32_t) input[j]) << 24);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */

+ 48 - 0
sha1/sha1.h

@@ -0,0 +1,48 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2018 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Stefan Esser <sesser@php.net>                                |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+
+/* SHA1 context. */
+typedef struct {
+	uint32_t state[5];		/* state (ABCD) */
+	uint32_t count[2];		/* number of bits, modulo 2^64 (lsb first) */
+	unsigned char buffer[64];	/* input buffer */
+} SHA1_CTX;
+
+void SHA1Init(SHA1_CTX *);
+void SHA1Update(SHA1_CTX *, const unsigned char *, size_t);
+void SHA1Final(unsigned char[20], SHA1_CTX *);
+void make_sha1_digest(char *sha1str, unsigned char *digest);
+
+void sha1(void *data, size_t data_len, void *hash);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 115 - 0
test.cbp

@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+	<FileVersion major="1" minor="6" />
+	<Project>
+		<Option title="test" />
+		<Option pch_mode="2" />
+		<Option compiler="gcc" />
+		<Build>
+			<Target title="Debug">
+				<Option output="bin/Debug/test" prefix_auto="1" extension_auto="1" />
+				<Option object_output="obj/Debug/" />
+				<Option type="0" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-g" />
+				</Compiler>
+			</Target>
+			<Target title="Release">
+				<Option output="bin/Release/test" prefix_auto="1" extension_auto="1" />
+				<Option object_output="obj/Release/" />
+				<Option type="0" />
+				<Option compiler="gcc" />
+				<Compiler>
+					<Add option="-O2" />
+				</Compiler>
+				<Linker>
+					<Add option="-s" />
+				</Linker>
+			</Target>
+		</Build>
+		<Compiler>
+			<Add option="-Wall" />
+		</Compiler>
+		<Linker>
+			<Add library="gdi32" />
+			<Add library="user32" />
+			<Add library="kernel32" />
+			<Add library="comctl32" />
+		</Linker>
+		<Unit filename="app.ico" />
+		<Unit filename="base64/codec.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="base64/codec_choose.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="base64/codecs.h" />
+		<Unit filename="base64/lib.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="base64/libbase64.h" />
+		<Unit filename="base64/tables.h" />
+		<Unit filename="main.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="md5/md5.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="md5/md5.h" />
+		<Unit filename="parson/parson.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="parson/parson.h" />
+		<Unit filename="resources.h" />
+		<Unit filename="resources.rc">
+			<Option compilerVar="WINDRES" />
+		</Unit>
+		<Unit filename="sha1/sha1.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="sha1/sha1.h" />
+		<Unit filename="timelib/astro.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/astro.h" />
+		<Unit filename="timelib/dow.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/fallbackmap.h" />
+		<Unit filename="timelib/interval.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/parse_date.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/parse_iso_intervals.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/parse_tz.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/timelib.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/timelib.h" />
+		<Unit filename="timelib/timelib_private.h" />
+		<Unit filename="timelib/timezonedb.h" />
+		<Unit filename="timelib/timezonemap.h" />
+		<Unit filename="timelib/tm2unixtime.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="timelib/unixtime2tm.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="url/url.c">
+			<Option compilerVar="CC" />
+		</Unit>
+		<Unit filename="url/url.h" />
+		<Extensions>
+			<code_completion />
+			<envvars />
+			<debugger />
+		</Extensions>
+	</Project>
+</CodeBlocks_project_file>

+ 350 - 0
test.depend

@@ -0,0 +1,350 @@
+# depslib dependency file v1.0
+1572605784 source:e:\workroot\cpp\test\main.cpp
+	<tchar.h>
+	<windows.h>
+	"base64/libbase64.h"
+	"md5/md5.h"
+
+1572599695 source:e:\workroot\cpp\test\base64\codec.c
+	<stdint.h>
+	<stddef.h>
+	<stdlib.h>
+	"libbase64.h"
+	"codecs.h"
+
+1572950895 e:\workroot\cpp\test\base64\libbase64.h
+
+1572600315 e:\workroot\cpp\test\base64\codecs.h
+
+1572600873 source:e:\workroot\cpp\test\base64\lib.c
+	<stdint.h>
+	<stddef.h>
+	"libbase64.h"
+	"codecs.h"
+	"tables.h"
+
+1572377700 e:\workroot\cpp\test\base64\tables.h
+	<stdint.h>
+
+1572600779 source:e:\workroot\cpp\test\base64\codec_choose.c
+	<stdbool.h>
+	<stdint.h>
+	<stddef.h>
+	<stdint.h>
+	"libbase64.h"
+	"codecs.h"
+	<intrin.h>
+	<cpuid.h>
+
+1572606054 source:e:\workroot\cpp\test\md5\md5.c
+	<string.h>
+	"md5.h"
+
+1572950902 e:\workroot\cpp\test\md5\md5.h
+	<stdio.h>
+	<stdint.h>
+
+1574164652 source:e:\workroot\cpp\test\main.c
+	<stdio.h>
+	<wchar.h>
+	<tchar.h>
+	<windows.h>
+	<time.h>
+	"base64/libbase64.h"
+	"md5/md5.h"
+	"sha1/sha1.h"
+	"url/url.h"
+	"parson/parson.h"
+	"resources.h"
+	"timelib/timelib.h"
+
+1574041117 source:e:\workroot\cpp\test\sha1\sha1.c
+	<string.h>
+	"sha1.h"
+	"../md5/md5.h"
+
+1572950907 e:\workroot\cpp\test\sha1\sha1.h
+	<stdio.h>
+	<stdint.h>
+
+1572942956 source:e:\workroot\cpp\test\url\url.c
+	<stdlib.h>
+	<string.h>
+	<ctype.h>
+	<sys/types.h>
+
+1572651512 source:e:\workroot\cpp\test\json\cjson.c
+	<string.h>
+	<stdio.h>
+	<math.h>
+	<stdlib.h>
+	<limits.h>
+	<ctype.h>
+	<locale.h>
+	"cJSON.h"
+
+1572651512 e:\workroot\cpp\test\json\cjson.h
+	<stddef.h>
+
+1572950912 e:\workroot\cpp\test\url\url.h
+
+1574145862 e:\workroot\cpp\test\resources.h
+
+1574145881 source:e:\workroot\cpp\test\resources.rc
+	"winuser.h"
+	"resources.h"
+
+1571731871 source:e:\workroot\cpp\test\timelib\astro.c
+	"timelib.h"
+	<stdio.h>
+	<math.h>
+	"astro.h"
+
+1574132375 e:\workroot\cpp\test\timelib\timelib.h
+	"timelib_config.h"
+	<stdlib.h>
+	<stdbool.h>
+	<limits.h>
+	<inttypes.h>
+	<stdint.h>
+
+1571731871 e:\workroot\cpp\test\timelib\astro.h
+
+1571731871 source:e:\workroot\cpp\test\timelib\dow.c
+	"timelib.h"
+	"timelib_private.h"
+
+1571731871 e:\workroot\cpp\test\timelib\timelib_private.h
+	"timelib_config.h"
+	<sys/time.h>
+	<winsock2.h>
+	<string.h>
+	<sys/types.h>
+	<stdint.h>
+	<unistd.h>
+	<io.h>
+	<dirent.h>
+	<stdio.h>
+	<limits.h>
+
+1571731871 source:e:\workroot\cpp\test\timelib\interval.c
+	"timelib.h"
+	"timelib_private.h"
+	<math.h>
+
+1571731871 source:e:\workroot\cpp\test\timelib\parse_date.c
+	"timelib.h"
+	"timelib_private.h"
+	<ctype.h>
+	<math.h>
+	<assert.h>
+	"timezonemap.h"
+	"fallbackmap.h"
+
+1571731871 e:\workroot\cpp\test\timelib\timezonemap.h
+
+1571731871 e:\workroot\cpp\test\timelib\fallbackmap.h
+
+1571731871 source:e:\workroot\cpp\test\timelib\parse_iso_intervals.c
+	"timelib.h"
+	"timelib_private.h"
+	<ctype.h>
+
+1571731871 source:e:\workroot\cpp\test\timelib\parse_tz.c
+	"timelib.h"
+	"timelib_private.h"
+	"timezonedb.h"
+
+1571731871 e:\workroot\cpp\test\timelib\timezonedb.h
+
+1571731871 source:e:\workroot\cpp\test\timelib\timelib.c
+	"timelib.h"
+	"timelib_private.h"
+	<ctype.h>
+	<math.h>
+
+1571731871 source:e:\workroot\cpp\test\timelib\tm2unixtime.c
+	"timelib.h"
+	"timelib_private.h"
+
+1571731871 source:e:\workroot\cpp\test\timelib\unixtime2tm.c
+	"timelib.h"
+	"timelib_private.h"
+
+1573488062 source:e:\workroot\cpp\test\parson\parson.c
+	"parson.h"
+	<stdio.h>
+	<stdlib.h>
+	<string.h>
+	<ctype.h>
+	<math.h>
+	<errno.h>
+
+1573488062 e:\workroot\cpp\test\parson\parson.h
+	<stddef.h>
+
+1572599695 source:e:\workroot\cpp\codebox\base64\codec.c
+	<stdint.h>
+	<stddef.h>
+	<stdlib.h>
+	"libbase64.h"
+	"codecs.h"
+
+1572950895 e:\workroot\cpp\codebox\base64\libbase64.h
+
+1572600315 e:\workroot\cpp\codebox\base64\codecs.h
+
+1572600779 source:e:\workroot\cpp\codebox\base64\codec_choose.c
+	<stdbool.h>
+	<stdint.h>
+	<stddef.h>
+	<stdint.h>
+	"libbase64.h"
+	"codecs.h"
+	<intrin.h>
+	<cpuid.h>
+
+1572600873 source:e:\workroot\cpp\codebox\base64\lib.c
+	<stdint.h>
+	<stddef.h>
+	"libbase64.h"
+	"codecs.h"
+	"tables.h"
+
+1572377700 e:\workroot\cpp\codebox\base64\tables.h
+	<stdint.h>
+
+1577153903 source:e:\workroot\cpp\codebox\main.c
+	<stdio.h>
+	<wchar.h>
+	<tchar.h>
+	<windows.h>
+	<time.h>
+	"base64/libbase64.h"
+	"md5/md5.h"
+	"sha1/sha1.h"
+	"url/url.h"
+	"parson/parson.h"
+	"resources.h"
+	"timelib/timelib.h"
+
+1572950902 e:\workroot\cpp\codebox\md5\md5.h
+	<stdio.h>
+	<stdint.h>
+
+1572950907 e:\workroot\cpp\codebox\sha1\sha1.h
+	<stdio.h>
+	<stdint.h>
+
+1572950912 e:\workroot\cpp\codebox\url\url.h
+
+1573488062 e:\workroot\cpp\codebox\parson\parson.h
+	<stddef.h>
+
+1574145862 e:\workroot\cpp\codebox\resources.h
+
+1574132375 e:\workroot\cpp\codebox\timelib\timelib.h
+	"timelib_config.h"
+	<stdlib.h>
+	<stdbool.h>
+	<limits.h>
+	<inttypes.h>
+	<stdint.h>
+
+1572606054 source:e:\workroot\cpp\codebox\md5\md5.c
+	<string.h>
+	"md5.h"
+
+1573488062 source:e:\workroot\cpp\codebox\parson\parson.c
+	"parson.h"
+	<stdio.h>
+	<stdlib.h>
+	<string.h>
+	<ctype.h>
+	<math.h>
+	<errno.h>
+
+1574145881 source:e:\workroot\cpp\codebox\resources.rc
+	"winuser.h"
+	"resources.h"
+
+1574041117 source:e:\workroot\cpp\codebox\sha1\sha1.c
+	<string.h>
+	"sha1.h"
+	"../md5/md5.h"
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\astro.c
+	"timelib.h"
+	<stdio.h>
+	<math.h>
+	"astro.h"
+
+1571731871 e:\workroot\cpp\codebox\timelib\astro.h
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\dow.c
+	"timelib.h"
+	"timelib_private.h"
+
+1571731871 e:\workroot\cpp\codebox\timelib\timelib_private.h
+	"timelib_config.h"
+	<sys/time.h>
+	<winsock2.h>
+	<string.h>
+	<sys/types.h>
+	<stdint.h>
+	<unistd.h>
+	<io.h>
+	<dirent.h>
+	<stdio.h>
+	<limits.h>
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\interval.c
+	"timelib.h"
+	"timelib_private.h"
+	<math.h>
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\parse_date.c
+	"timelib.h"
+	"timelib_private.h"
+	<ctype.h>
+	<math.h>
+	<assert.h>
+	"timezonemap.h"
+	"fallbackmap.h"
+
+1571731871 e:\workroot\cpp\codebox\timelib\timezonemap.h
+
+1571731871 e:\workroot\cpp\codebox\timelib\fallbackmap.h
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\parse_iso_intervals.c
+	"timelib.h"
+	"timelib_private.h"
+	<ctype.h>
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\parse_tz.c
+	"timelib.h"
+	"timelib_private.h"
+	"timezonedb.h"
+
+1571731871 e:\workroot\cpp\codebox\timelib\timezonedb.h
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\timelib.c
+	"timelib.h"
+	"timelib_private.h"
+	<ctype.h>
+	<math.h>
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\tm2unixtime.c
+	"timelib.h"
+	"timelib_private.h"
+
+1571731871 source:e:\workroot\cpp\codebox\timelib\unixtime2tm.c
+	"timelib.h"
+	"timelib_private.h"
+
+1572942956 source:e:\workroot\cpp\codebox\url\url.c
+	<stdlib.h>
+	<string.h>
+	<ctype.h>
+	<sys/types.h>
+

+ 85 - 0
test.layout

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_layout_file>
+	<FileVersion major="1" minor="0" />
+	<ActiveTarget name="Release" />
+	<File name="md5\md5.c" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="7069" topLine="196" />
+		</Cursor>
+	</File>
+	<File name="timelib\timelib.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="12262" topLine="371" />
+		</Cursor>
+	</File>
+	<File name="timelib\timelib_private.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="2522" topLine="63" />
+		</Cursor>
+	</File>
+	<File name="url\url.c" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="4157" topLine="147" />
+		</Cursor>
+	</File>
+	<File name="url\url.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="1293" topLine="7" />
+		</Cursor>
+	</File>
+	<File name="sha1\sha1.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="1674" topLine="8" />
+		</Cursor>
+	</File>
+	<File name="md5\md5.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="885" topLine="3" />
+		</Cursor>
+	</File>
+	<File name="base64\codecs.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="354" topLine="0" />
+		</Cursor>
+	</File>
+	<File name="resources.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="574" topLine="0" />
+		</Cursor>
+	</File>
+	<File name="sha1\sha1.c" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="8951" topLine="235" />
+		</Cursor>
+	</File>
+	<File name="base64\codec.c" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="0" topLine="48" />
+		</Cursor>
+	</File>
+	<File name="main.c" open="0" top="0" tabpos="1" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="10404" topLine="209" />
+		</Cursor>
+	</File>
+	<File name="resources.rc" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="578" topLine="0" />
+		</Cursor>
+	</File>
+	<File name="base64\lib.c" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="3537" topLine="101" />
+		</Cursor>
+	</File>
+	<File name="parson\parson.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="10964" topLine="192" />
+		</Cursor>
+	</File>
+	<File name="base64\libbase64.h" open="0" top="0" tabpos="0" split="0" active="1" splitpos="0" zoom_1="0" zoom_2="0">
+		<Cursor>
+			<Cursor1 position="2768" topLine="55" />
+		</Cursor>
+	</File>
+</CodeBlocks_layout_file>

+ 313 - 0
timelib/astro.c

@@ -0,0 +1,313 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+/*
+   | Algorithms are taken from a public domain source by Paul             |
+   | Schlyter, who wrote this in December 1992                            |
+ */
+
+#include "timelib.h"
+#include <stdio.h>
+#include <math.h>
+
+#define days_since_2000_Jan_0(y,m,d) \
+	(367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L)
+
+#ifndef PI
+# define PI        3.1415926535897932384
+#endif
+
+#define RADEG     ( 180.0 / PI )
+#define DEGRAD    ( PI / 180.0 )
+
+/* The trigonometric functions in degrees */
+
+#define sind(x)  sin((x)*DEGRAD)
+#define cosd(x)  cos((x)*DEGRAD)
+#define tand(x)  tan((x)*DEGRAD)
+
+#define atand(x)    (RADEG*atan(x))
+#define asind(x)    (RADEG*asin(x))
+#define acosd(x)    (RADEG*acos(x))
+#define atan2d(y,x) (RADEG*atan2(y,x))
+
+
+/* Following are some macros around the "workhorse" function __daylen__ */
+/* They mainly fill in the desired values for the reference altitude    */
+/* below the horizon, and also selects whether this altitude should     */
+/* refer to the Sun's center or its upper limb.                         */
+
+
+#include "astro.h"
+
+/******************************************************************/
+/* This function reduces any angle to within the first revolution */
+/* by subtracting or adding even multiples of 360.0 until the     */
+/* result is >= 0.0 and < 360.0                                   */
+/******************************************************************/
+
+#define INV360    (1.0 / 360.0)
+
+/*****************************************/
+/* Reduce angle to within 0..360 degrees */
+/*****************************************/
+static double astro_revolution(double x)
+{
+	return (x - 360.0 * floor(x * INV360));
+}
+
+/*********************************************/
+/* Reduce angle to within +180..+180 degrees */
+/*********************************************/
+static double astro_rev180( double x )
+{
+	return (x - 360.0 * floor(x * INV360 + 0.5));
+}
+
+/*******************************************************************/
+/* This function computes GMST0, the Greenwich Mean Sidereal Time  */
+/* at 0h UT (i.e. the sidereal time at the Greenwhich meridian at  */
+/* 0h UT).  GMST is then the sidereal time at Greenwich at any     */
+/* time of the day.  I've generalized GMST0 as well, and define it */
+/* as:  GMST0 = GMST - UT  --  this allows GMST0 to be computed at */
+/* other times than 0h UT as well.  While this sounds somewhat     */
+/* contradictory, it is very practical:  instead of computing      */
+/* GMST like:                                                      */
+/*                                                                 */
+/*  GMST = (GMST0) + UT * (366.2422/365.2422)                      */
+/*                                                                 */
+/* where (GMST0) is the GMST last time UT was 0 hours, one simply  */
+/* computes:                                                       */
+/*                                                                 */
+/*  GMST = GMST0 + UT                                              */
+/*                                                                 */
+/* where GMST0 is the GMST "at 0h UT" but at the current moment!   */
+/* Defined in this way, GMST0 will increase with about 4 min a     */
+/* day.  It also happens that GMST0 (in degrees, 1 hr = 15 degr)   */
+/* is equal to the Sun's mean longitude plus/minus 180 degrees!    */
+/* (if we neglect aberration, which amounts to 20 seconds of arc   */
+/* or 1.33 seconds of time)                                        */
+/*                                                                 */
+/*******************************************************************/
+
+static double astro_GMST0(double d)
+{
+	double sidtim0;
+	/* Sidtime at 0h UT = L (Sun's mean longitude) + 180.0 degr  */
+	/* L = M + w, as defined in sunpos().  Since I'm too lazy to */
+	/* add these numbers, I'll let the C compiler do it for me.  */
+	/* Any decent C compiler will add the constants at compile   */
+	/* time, imposing no runtime or code overhead.               */
+	sidtim0 = astro_revolution((180.0 + 356.0470 + 282.9404) + (0.9856002585 + 4.70935E-5) * d);
+	return sidtim0;
+}
+
+/* This function computes the Sun's position at any instant */
+
+/******************************************************/
+/* Computes the Sun's ecliptic longitude and distance */
+/* at an instant given in d, number of days since     */
+/* 2000 Jan 0.0.  The Sun's ecliptic latitude is not  */
+/* computed, since it's always very near 0.           */
+/******************************************************/
+static void astro_sunpos(double d, double *lon, double *r)
+{
+	double M,         /* Mean anomaly of the Sun */
+	       w,         /* Mean longitude of perihelion */
+	                  /* Note: Sun's mean longitude = M + w */
+	       e,         /* Eccentricity of Earth's orbit */
+	       E,         /* Eccentric anomaly */
+	       x, y,      /* x, y coordinates in orbit */
+	       v;         /* True anomaly */
+
+	/* Compute mean elements */
+	M = astro_revolution(356.0470 + 0.9856002585 * d);
+	w = 282.9404 + 4.70935E-5 * d;
+	e = 0.016709 - 1.151E-9 * d;
+
+	/* Compute true longitude and radius vector */
+	E = M + e * RADEG * sind(M) * (1.0 + e * cosd(M));
+	x = cosd(E) - e;
+	y = sqrt(1.0 - e*e) * sind(E);
+	*r = sqrt(x*x + y*y);              /* Solar distance */
+	v = atan2d(y, x);                  /* True anomaly */
+	*lon = v + w;                        /* True solar longitude */
+	if (*lon >= 360.0) {
+		*lon -= 360.0;                   /* Make it 0..360 degrees */
+	}
+}
+
+static void astro_sun_RA_dec(double d, double *RA, double *dec, double *r)
+{
+	double lon, obl_ecl, x, y, z;
+
+	/* Compute Sun's ecliptical coordinates */
+	astro_sunpos(d, &lon, r);
+
+	/* Compute ecliptic rectangular coordinates (z=0) */
+	x = *r * cosd(lon);
+	y = *r * sind(lon);
+
+	/* Compute obliquity of ecliptic (inclination of Earth's axis) */
+	obl_ecl = 23.4393 - 3.563E-7 * d;
+
+	/* Convert to equatorial rectangular coordinates - x is unchanged */
+	z = y * sind(obl_ecl);
+	y = y * cosd(obl_ecl);
+
+	/* Convert to spherical coordinates */
+	*RA = atan2d(y, x);
+	*dec = atan2d(z, sqrt(x*x + y*y));
+}
+
+/**
+ * Note: timestamp = unixtimestamp (NEEDS to be 00:00:00 UT)
+ *       Eastern longitude positive, Western longitude negative
+ *       Northern latitude positive, Southern latitude negative
+ *       The longitude value IS critical in this function!
+ *       altit = the altitude which the Sun should cross
+ *               Set to -35/60 degrees for rise/set, -6 degrees
+ *               for civil, -12 degrees for nautical and -18
+ *               degrees for astronomical twilight.
+ *         upper_limb: non-zero -> upper limb, zero -> center
+ *               Set to non-zero (e.g. 1) when computing rise/set
+ *               times, and to zero when computing start/end of
+ *               twilight.
+ *        *rise = where to store the rise time
+ *        *set  = where to store the set  time
+ *                Both times are relative to the specified altitude,
+ *                and thus this function can be used to compute
+ *                various twilight times, as well as rise/set times
+ * Return value:  0 = sun rises/sets this day, times stored at
+ *                    *trise and *tset.
+ *               +1 = sun above the specified "horizon" 24 hours.
+ *                    *trise set to time when the sun is at south,
+ *                    minus 12 hours while *tset is set to the south
+ *                    time plus 12 hours. "Day" length = 24 hours
+ *               -1 = sun is below the specified "horizon" 24 hours
+ *                    "Day" length = 0 hours, *trise and *tset are
+ *                    both set to the time when the sun is at south.
+ *
+ */
+int timelib_astro_rise_set_altitude(timelib_time *t_loc, double lon, double lat, double altit, int upper_limb, double *h_rise, double *h_set, timelib_sll *ts_rise, timelib_sll *ts_set, timelib_sll *ts_transit)
+{
+	double  d,  /* Days since 2000 Jan 0.0 (negative before) */
+	sr,         /* Solar distance, astronomical units */
+	sRA,        /* Sun's Right Ascension */
+	sdec,       /* Sun's declination */
+	sradius,    /* Sun's apparent radius */
+	t,          /* Diurnal arc */
+	tsouth,     /* Time when Sun is at south */
+	sidtime;    /* Local sidereal time */
+	timelib_time *t_utc;
+	timelib_sll   timestamp, old_sse;
+
+	int rc = 0; /* Return cde from function - usually 0 */
+
+	/* Normalize time */
+	old_sse = t_loc->sse;
+	t_loc->h = 12;
+	t_loc->i = t_loc->s = 0;
+	timelib_update_ts(t_loc, NULL);
+
+	/* Calculate TS belonging to UTC 00:00 of the current day, for input into
+	 * the algorithm */
+	t_utc = timelib_time_ctor();
+	t_utc->y = t_loc->y;
+	t_utc->m = t_loc->m;
+	t_utc->d = t_loc->d;
+	t_utc->h = t_utc->i = t_utc->s = 0;
+	timelib_update_ts(t_utc, NULL);
+
+	/* Compute d of 12h local mean solar time */
+	timestamp = t_utc->sse;
+	d = timelib_ts_to_j2000(timestamp) + 2 - lon/360.0;
+
+	/* Compute local sidereal time of this moment */
+	sidtime = astro_revolution(astro_GMST0(d) + 180.0 + lon);
+
+	/* Compute Sun's RA + Decl at this moment */
+	astro_sun_RA_dec( d, &sRA, &sdec, &sr );
+
+	/* Compute time when Sun is at south - in hours UT */
+	tsouth = 12.0 - astro_rev180(sidtime - sRA) / 15.0;
+
+	/* Compute the Sun's apparent radius, degrees */
+	sradius = 0.2666 / sr;
+
+	/* Do correction to upper limb, if necessary */
+	if (upper_limb) {
+		altit -= sradius;
+	}
+
+	/* Compute the diurnal arc that the Sun traverses to reach */
+	/* the specified altitude altit: */
+	{
+		double cost;
+		cost = (sind(altit) - sind(lat) * sind(sdec)) / (cosd(lat) * cosd(sdec));
+		*ts_transit = t_utc->sse + (tsouth * 3600);
+		if (cost >= 1.0) {
+			rc = -1;
+			t = 0.0;       /* Sun always below altit */
+
+			*ts_rise = *ts_set = t_utc->sse + (tsouth * 3600);
+		} else if (cost <= -1.0) {
+			rc = +1;
+			t = 12.0;      /* Sun always above altit */
+
+			*ts_rise = t_loc->sse - (12 * 3600);
+			*ts_set  = t_loc->sse + (12 * 3600);
+		} else {
+			t = acosd(cost) / 15.0;   /* The diurnal arc, hours */
+
+			/* Store rise and set times - as Unix Timestamp */
+			*ts_rise = ((tsouth - t) * 3600) + t_utc->sse;
+			*ts_set  = ((tsouth + t) * 3600) + t_utc->sse;
+
+			*h_rise = (tsouth - t);
+			*h_set  = (tsouth + t);
+		}
+	}
+
+	/* Kill temporary time and restore original sse */
+	timelib_time_dtor(t_utc);
+	t_loc->sse = old_sse;
+
+	return rc;
+}
+
+double timelib_ts_to_julianday(timelib_sll ts)
+{
+	double tmp;
+
+	tmp = (double) ts;
+	tmp /= (double) 86400;
+	tmp += (double) 2440587.5;
+
+	return tmp;
+}
+
+double timelib_ts_to_j2000(timelib_sll ts)
+{
+	return timelib_ts_to_julianday(ts) - 2451545;
+}

+ 74 - 0
timelib/astro.h

@@ -0,0 +1,74 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+/* This macro computes the length of the day, from sunrise to sunset. */
+/* Sunrise/set is considered to occur when the Sun's upper limb is    */
+/* 35 arc minutes below the horizon (this accounts for the refraction */
+/* of the Earth's atmosphere).                                        */
+#define day_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -35.0/60.0, 1 )
+
+/* This macro computes the length of the day, including civil twilight. */
+/* Civil twilight starts/ends when the Sun's center is 6 degrees below  */
+/* the horizon.                                                         */
+#define day_civil_twilight_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -6.0, 0 )
+
+/* This macro computes the length of the day, incl. nautical twilight.  */
+/* Nautical twilight starts/ends when the Sun's center is 12 degrees    */
+/* below the horizon.                                                   */
+#define day_nautical_twilight_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -12.0, 0 )
+
+/* This macro computes the length of the day, incl. astronomical twilight. */
+/* Astronomical twilight starts/ends when the Sun's center is 18 degrees   */
+/* below the horizon.                                                      */
+#define day_astronomical_twilight_length(year,month,day,lon,lat)  \
+        __daylen__( year, month, day, lon, lat, -18.0, 0 )
+
+
+/* This macro computes times for sunrise/sunset.                      */
+/* Sunrise/set is considered to occur when the Sun's upper limb is    */
+/* 35 arc minutes below the horizon (this accounts for the refraction */
+/* of the Earth's atmosphere).                                        */
+#define timelib_astro_sun_rise_set(ts,lon,lat,hrise,hset,rise,set)  \
+        timelib_astro_rise_set_altitude( ts, lon, lat, -35.0/60.0, 1, hrise, hset, rise, set )
+
+/* This macro computes the start and end times of civil twilight.       */
+/* Civil twilight starts/ends when the Sun's center is 6 degrees below  */
+/* the horizon.                                                         */
+#define civil_twilight(ts,lon,lat,start,end)  \
+        timelib_astro_rise_set_altitude( ts, lon, lat, -6.0, 0, start, end )
+
+/* This macro computes the start and end times of nautical twilight.    */
+/* Nautical twilight starts/ends when the Sun's center is 12 degrees    */
+/* below the horizon.                                                   */
+#define nautical_twilight(ts,lon,lat,start,end)  \
+        timelib_astro_rise_set_altitude( ts, lon, lat, -12.0, 0, start, end )
+
+/* This macro computes the start and end times of astronomical twilight.   */
+/* Astronomical twilight starts/ends when the Sun's center is 18 degrees   */
+/* below the horizon.                                                      */
+#define astronomical_twilight(ts,lon,lat,start,end)  \
+        timelib_astro_rise_set_altitude( ts, lon, lat, -18.0, 0, start, end )

+ 229 - 0
timelib/dow.c

@@ -0,0 +1,229 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+
+static int m_table_common[13] = { -1, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 }; /* 1 = jan */
+static int m_table_leap[13] =   { -1, 6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 }; /* 1 = jan */
+
+static timelib_sll positive_mod(timelib_sll x, timelib_sll y)
+{
+	timelib_sll tmp;
+
+	tmp = x % y;
+	if (tmp < 0) {
+		tmp += y;
+	}
+
+	return tmp;
+}
+
+static timelib_sll century_value(timelib_sll j)
+{
+	return 6 - positive_mod(j, 4) * 2;
+}
+
+static timelib_sll timelib_day_of_week_ex(timelib_sll y, timelib_sll m, timelib_sll d, int iso)
+{
+	timelib_sll c1, y1, m1, dow;
+
+	/* Only valid for Gregorian calendar, commented out as we don't handle
+	 * Julian calendar. We just return the 'wrong' day of week to be
+	 * consistent. */
+	c1 = century_value(y / 100);
+	y1 = positive_mod(y, 100);
+	m1 = timelib_is_leap(y) ? m_table_leap[m] : m_table_common[m];
+	dow = positive_mod((c1 + y1 + m1 + (y1 / 4) + d), 7);
+	if (iso) {
+		if (dow == 0) {
+			dow = 7;
+		}
+	}
+	return dow;
+}
+
+timelib_sll timelib_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d)
+{
+	return timelib_day_of_week_ex(y, m, d, 0);
+}
+
+timelib_sll timelib_iso_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d)
+{
+	return timelib_day_of_week_ex(y, m, d, 1);
+}
+
+                                /*     jan  feb  mar  apr  may  jun  jul  aug  sep  oct  nov  dec */
+static int d_table_common[13]  = {  0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334 };
+static int d_table_leap[13]    = {  0,   0,  31,  60,  91, 121, 152, 182, 213, 244, 274, 305, 335 };
+static int ml_table_common[13] = {  0,  31,  28,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
+static int ml_table_leap[13]   = {  0,  31,  29,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
+
+timelib_sll timelib_day_of_year(timelib_sll y, timelib_sll m, timelib_sll d)
+{
+	return (timelib_is_leap(y) ? d_table_leap[m] : d_table_common[m]) + d - 1;
+}
+
+timelib_sll timelib_days_in_month(timelib_sll y, timelib_sll m)
+{
+	return timelib_is_leap(y) ? ml_table_leap[m] : ml_table_common[m];
+}
+
+void timelib_isoweek_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iw, timelib_sll *iy)
+{
+	int y_leap, prev_y_leap, doy, jan1weekday, weekday;
+
+	y_leap = timelib_is_leap(y);
+	prev_y_leap = timelib_is_leap(y-1);
+	doy = timelib_day_of_year(y, m, d) + 1;
+	if (y_leap && m > 2) {
+		doy++;
+	}
+	jan1weekday = timelib_day_of_week(y, 1, 1);
+	weekday = timelib_day_of_week(y, m, d);
+	if (weekday == 0) weekday = 7;
+	if (jan1weekday == 0) jan1weekday = 7;
+	/* Find if Y M D falls in YearNumber Y-1, WeekNumber 52 or 53 */
+	if (doy <= (8 - jan1weekday) && jan1weekday > 4) {
+		*iy = y - 1;
+		if (jan1weekday == 5 || (jan1weekday == 6 && prev_y_leap)) {
+			*iw = 53;
+		} else {
+			*iw = 52;
+		}
+	} else {
+		*iy = y;
+	}
+	/* 8. Find if Y M D falls in YearNumber Y+1, WeekNumber 1 */
+	if (*iy == y) {
+		int i;
+
+		i = y_leap ? 366 : 365;
+		if ((i - (doy - y_leap)) < (4 - weekday)) {
+			*iy = y + 1;
+			*iw = 1;
+			return;
+		}
+	}
+	/* 9. Find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 */
+	if (*iy == y) {
+		int j;
+
+		j = doy + (7 - weekday) + (jan1weekday - 1);
+		*iw = j / 7;
+		if (jan1weekday > 4) {
+			*iw -= 1;
+		}
+	}
+}
+
+void timelib_isodate_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iy, timelib_sll *iw, timelib_sll *id)
+{
+	timelib_isoweek_from_date(y, m, d, iw, iy);
+	*id = timelib_day_of_week_ex(y, m, d, 1);
+}
+
+static timelib_sll timelib_daynr_from_weeknr_ex(timelib_sll iy, timelib_sll iw, timelib_sll id, timelib_sll *y)
+{
+	timelib_sll dow, day;
+
+	/* Figure out the dayofweek for y-1-1 */
+	dow = timelib_day_of_week(iy, 1, 1);
+	/* then use that to figure out the offset for day 1 of week 1 */
+	day = 0 - (dow > 4 ? dow - 7 : dow);
+	/* and adjust the year to the natural year if we need to */
+	*y = (iw == 1 && day < 0 && id < dow) ? iy - 1 : iy;
+
+	/* Add weeks and days */
+	return day + ((iw - 1) * 7) + id;
+}
+
+timelib_sll timelib_daynr_from_weeknr(timelib_sll iy, timelib_sll iw, timelib_sll id)
+{
+	timelib_sll dummy_iso_year;
+
+	return timelib_daynr_from_weeknr_ex(iy, iw, id, &dummy_iso_year);
+}
+
+void timelib_date_from_isodate(timelib_sll iy, timelib_sll iw, timelib_sll id, timelib_sll *y, timelib_sll *m, timelib_sll *d)
+{
+	timelib_sll daynr = timelib_daynr_from_weeknr_ex(iy, iw, id, y) + 1;
+	int *table;
+
+	*m = 0;
+
+	if (daynr <= 0) {
+		*y += 1;
+	}
+
+	if (timelib_is_leap(*y)) {
+		table = ml_table_leap;
+		if (daynr > 366) {
+			*y += 1;
+			daynr -= 366;
+		}
+	} else {
+		table = ml_table_common;
+		if (daynr > 365) {
+			*y += 1;
+			daynr -= 365;
+		}
+	}
+
+	do {
+		daynr -= table[*m];
+		(*m)++;
+	} while (daynr > table[*m]);
+
+	if (daynr <= 0) {
+		daynr += 31;
+		*y -= 1;
+		*m = 12;
+	}
+
+	*d = daynr;
+}
+
+int timelib_valid_time(timelib_sll h, timelib_sll i, timelib_sll s)
+{
+	if (h < 0 || h > 23 || i < 0 || i > 59 || s < 0 || s > 59) {
+		return 0;
+	}
+	return 1;
+}
+
+int timelib_valid_date(timelib_sll y, timelib_sll m, timelib_sll d)
+{
+	if (m < 1 || m > 12 || d < 1 || d > timelib_days_in_month(y, m)) {
+		return 0;
+	}
+	return 1;
+}
+#if 0
+int main(void)
+{
+	printf("dow = %d\n", timelib_day_of_week(1978, 12, 22)); /* 5 */
+	printf("dow = %d\n", timelib_day_of_week(2005,  2, 19)); /* 6 */
+}
+#endif

+ 42 - 0
timelib/fallbackmap.h

@@ -0,0 +1,42 @@
+	{ "sst",   0, -660 * 60, "Pacific/Apia" },
+	{ "hst",   0, -600 * 60, "Pacific/Honolulu" },
+	{ "akst",  0, -540 * 60, "America/Anchorage" },
+	{ "akdt",  1, -480 * 60, "America/Anchorage" },
+	{ "pst",   0, -480 * 60, "America/Los_Angeles" },
+	{ "pdt",   1, -420 * 60, "America/Los_Angeles" },
+	{ "mst",   0, -420 * 60, "America/Denver" },
+	{ "mdt",   1, -360 * 60, "America/Denver" },
+	{ "cst",   0, -360 * 60, "America/Chicago" },
+	{ "cdt",   1, -300 * 60, "America/Chicago" },
+	{ "est",   0, -300 * 60, "America/New_York" },
+	{ "vet",   0, -270 * 60, "America/Caracas" },
+	{ "edt",   1, -240 * 60, "America/New_York" },
+	{ "ast",   0, -240 * 60, "America/Halifax" },
+	{ "adt",   1, -180 * 60, "America/Halifax" },
+	{ "brt",   0, -180 * 60, "America/Sao_Paulo" },
+	{ "brst",  1, -120 * 60, "America/Sao_Paulo" },
+	{ "azost", 0,  -60 * 60, "Atlantic/Azores" },
+	{ "azodt", 1,    0 * 60, "Atlantic/Azores" },
+	{ "gmt",   0,    0 * 60, "Europe/London" },
+	{ "bst",   1,   60 * 60, "Europe/London" },
+	{ "cet",   0,   60 * 60, "Europe/Paris" },
+	{ "cest",  1,  120 * 60, "Europe/Paris" },
+	{ "eet",   0,  120 * 60, "Europe/Helsinki" },
+	{ "eest",  1,  180 * 60, "Europe/Helsinki" },
+	{ "msk",   0,  180 * 60, "Europe/Moscow" },
+	{ "msd",   1,  240 * 60, "Europe/Moscow" },
+	{ "gst",   0,  240 * 60, "Asia/Dubai" },
+	{ "pkt",   0,  300 * 60, "Asia/Karachi" },
+	{ "ist",   0,  330 * 60, "Asia/Kolkata" },
+	{ "npt",   0,  345 * 60, "Asia/Katmandu" },
+	{ "yekt",  1,  360 * 60, "Asia/Yekaterinburg" },
+	{ "novst", 1,  420 * 60, "Asia/Novosibirsk" },
+	{ "krat",  0,  420 * 60, "Asia/Krasnoyarsk" },
+	{ "cst",   0,  480 * 60, "Asia/Shanghai" },
+	{ "krast", 1,  480 * 60, "Asia/Krasnoyarsk" },
+	{ "jst",   0,  540 * 60, "Asia/Tokyo" },
+	{ "est",   0,  600 * 60, "Australia/Melbourne" },
+	{ "cst",   1,  630 * 60, "Australia/Adelaide" },
+	{ "est",   1,  660 * 60, "Australia/Melbourne" },
+	{ "nzst",  0,  720 * 60, "Pacific/Auckland" },
+	{ "nzdt",  1,  780 * 60, "Pacific/Auckland" },

+ 176 - 0
timelib/interval.c

@@ -0,0 +1,176 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+#include <math.h>
+
+timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two)
+{
+	timelib_rel_time *rt;
+	timelib_time *swp;
+	timelib_sll dst_corr = 0 ,dst_h_corr = 0, dst_m_corr = 0;
+	timelib_time one_backup, two_backup;
+
+	rt = timelib_rel_time_ctor();
+	rt->invert = 0;
+	if (
+		(one->sse > two->sse) ||
+		(one->sse == two->sse && one->us > two->us)
+	) {
+		swp = two;
+		two = one;
+		one = swp;
+		rt->invert = 1;
+	}
+
+	/* Calculate correction for DST change over, but only if the TZ type is ID
+	 * and it's the same */
+	if (one->zone_type == 3 && two->zone_type == 3
+		&& (strcmp(one->tz_info->name, two->tz_info->name) == 0)
+		&& (one->z != two->z))
+	{
+		dst_corr = two->z - one->z;
+		dst_h_corr = dst_corr / 3600;
+		dst_m_corr = (dst_corr % 3600) / 60;
+	}
+
+	/* Save old TZ info */
+	memcpy(&one_backup, one, sizeof(one_backup));
+	memcpy(&two_backup, two, sizeof(two_backup));
+
+    timelib_apply_localtime(one, 0);
+    timelib_apply_localtime(two, 0);
+
+	rt->y = two->y - one->y;
+	rt->m = two->m - one->m;
+	rt->d = two->d - one->d;
+	rt->h = two->h - one->h;
+	rt->i = two->i - one->i;
+	rt->s = two->s - one->s;
+	rt->us = two->us - one->us;
+	if (one_backup.dst == 0 && two_backup.dst == 1 && two->sse >= one->sse + 86400 - dst_corr) {
+		rt->h += dst_h_corr;
+		rt->i += dst_m_corr;
+	}
+
+	rt->days = fabs(floor((one->sse - two->sse - (dst_h_corr * 3600) - (dst_m_corr * 60)) / 86400));
+
+	timelib_do_rel_normalize(rt->invert ? one : two, rt);
+
+	/* We need to do this after normalisation otherwise we can't get "24H" */
+	if (one_backup.dst == 1 && two_backup.dst == 0 && two->sse >= one->sse + 86400) {
+		if (two->sse < one->sse + 86400 - dst_corr) {
+			rt->d--;
+			rt->h = 24;
+		} else {
+			rt->h += dst_h_corr;
+			rt->i += dst_m_corr;
+		}
+	}
+
+	/* Restore old TZ info */
+	memcpy(one, &one_backup, sizeof(one_backup));
+	memcpy(two, &two_backup, sizeof(two_backup));
+
+	return rt;
+}
+
+timelib_time *timelib_add(timelib_time *old_time, timelib_rel_time *interval)
+{
+	int bias = 1;
+	timelib_time *t = timelib_time_clone(old_time);
+
+	if (interval->have_weekday_relative || interval->have_special_relative) {
+		memcpy(&t->relative, interval, sizeof(timelib_rel_time));
+	} else {
+		if (interval->invert) {
+			bias = -1;
+		}
+		memset(&t->relative, 0, sizeof(timelib_rel_time));
+		t->relative.y = interval->y * bias;
+		t->relative.m = interval->m * bias;
+		t->relative.d = interval->d * bias;
+		t->relative.h = interval->h * bias;
+		t->relative.i = interval->i * bias;
+		t->relative.s = interval->s * bias;
+		t->relative.us = interval->us * bias;
+	}
+	t->have_relative = 1;
+	t->sse_uptodate = 0;
+
+	timelib_update_ts(t, NULL);
+
+//	printf("%lld %lld %d\n", old_time->dst, t->dst, (t->sse - old_time->sse));
+	/* Adjust for backwards DST changeover */
+	if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
+		t->sse -= old_time->z;
+		t->sse += t->z;
+	}
+
+	timelib_update_from_sse(t);
+	t->have_relative = 0;
+
+	return t;
+}
+
+timelib_time *timelib_sub(timelib_time *old_time, timelib_rel_time *interval)
+{
+	int bias = 1;
+	timelib_time *t = timelib_time_clone(old_time);
+
+	if (interval->invert) {
+		bias = -1;
+	}
+
+	memset(&t->relative, 0, sizeof(timelib_rel_time));
+	t->relative.y = 0 - (interval->y * bias);
+	t->relative.m = 0 - (interval->m * bias);
+	t->relative.d = 0 - (interval->d * bias);
+	t->relative.h = 0 - (interval->h * bias);
+	t->relative.i = 0 - (interval->i * bias);
+	t->relative.s = 0 - (interval->s * bias);
+	t->relative.us = 0 - (interval->us * bias);
+	t->have_relative = 1;
+	t->sse_uptodate = 0;
+
+	timelib_update_ts(t, NULL);
+
+	/* Adjust for backwards DST changeover */
+	if (old_time->dst == 1 && t->dst == 0 && !interval->y && !interval->m && !interval->d) {
+		t->sse -= old_time->z;
+		t->sse += t->z;
+	}
+	/* Adjust for forwards DST changeover */
+	if (old_time->dst == 0 && t->dst == 1 && !interval->y && !interval->m && !interval->d ) {
+		t->sse -= old_time->z;
+		t->sse += t->z;
+	}
+
+	timelib_update_from_sse(t);
+
+	t->have_relative = 0;
+
+	return t;
+}

File diff suppressed because it is too large
+ 25522 - 0
timelib/parse_date.c


File diff suppressed because it is too large
+ 1051 - 0
timelib/parse_iso_intervals.c


+ 713 - 0
timelib/parse_tz.c

@@ -0,0 +1,713 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ * Copyright (c) 2018 MongoDB, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+
+#define TIMELIB_SUPPORTS_V2DATA
+#include "timezonedb.h"
+
+#if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__))
+# if defined(__LITTLE_ENDIAN__)
+#  undef WORDS_BIGENDIAN
+# else
+#  if defined(__BIG_ENDIAN__)
+#   define WORDS_BIGENDIAN
+#  endif
+# endif
+#endif
+
+#if defined(__s390__)
+# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#  define WORDS_BIGENDIAN
+# else
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
+
+#ifdef WORDS_BIGENDIAN
+static inline uint32_t timelib_conv_int_unsigned(uint32_t value)
+{
+	return value;
+}
+
+static inline uint64_t timelib_conv_int64_unsigned(uint64_t value)
+{
+	return value;
+}
+#else
+static inline uint32_t timelib_conv_int_unsigned(uint32_t value)
+{
+	return
+		((value & 0x000000ff) << 24) +
+		((value & 0x0000ff00) <<  8) +
+		((value & 0x00ff0000) >>  8) +
+		((value & 0xff000000) >> 24);
+}
+
+static inline uint64_t timelib_conv_int64_unsigned(uint64_t value)
+{
+	return
+		((value & 0x00000000000000ff) << 56) +
+		((value & 0x000000000000ff00) << 40) +
+		((value & 0x0000000000ff0000) << 24) +
+		((value & 0x00000000ff000000) <<  8) +
+		((value & 0x000000ff00000000) >>  8) +
+		((value & 0x0000ff0000000000) >> 24) +
+		((value & 0x00ff000000000000) >> 40) +
+		((value & 0xff00000000000000) >> 56);
+}
+#endif
+
+#define timelib_conv_int_signed(value) ((int32_t) timelib_conv_int_unsigned((int32_t) value))
+#define timelib_conv_int64_signed(value) ((int64_t) timelib_conv_int64_unsigned((int64_t) value))
+
+static int read_php_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	uint32_t version;
+
+	/* read ID */
+	version = (*tzf)[3] - '0';
+	*tzf += 4;
+
+	/* read BC flag */
+	tz->bc = (**tzf == '\1');
+	*tzf += 1;
+
+	/* read country code */
+	memcpy(tz->location.country_code, *tzf, 2);
+	tz->location.country_code[2] = '\0';
+	*tzf += 2;
+
+	/* skip rest of preamble */
+	*tzf += 13;
+
+	return version;
+}
+
+static int read_tzif_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	uint32_t version;
+
+	/* read ID */
+	switch ((*tzf)[4]) {
+		case '\0':
+			version = 0;
+			break;
+		case '2':
+			version = 2;
+			break;
+		case '3':
+			version = 3;
+			break;
+		default:
+			return -1;
+	}
+	*tzf += 5;
+
+	/* set BC flag and country code to default */
+	tz->bc = 0;
+	tz->location.country_code[0] = '?';
+	tz->location.country_code[1] = '?';
+	tz->location.country_code[2] = '\0';
+
+	/* skip rest of preamble */
+	*tzf += 15;
+
+	return version;
+}
+
+static int read_preamble(const unsigned char **tzf, timelib_tzinfo *tz, unsigned int *type)
+{
+	/* read marker (TZif) or (PHP) */
+	if (memcmp(*tzf, "PHP", 3) == 0) {
+		*type = TIMELIB_TZINFO_PHP;
+		return read_php_preamble(tzf, tz);
+	} else if (memcmp(*tzf, "TZif", 4) == 0) {
+		*type = TIMELIB_TZINFO_ZONEINFO;
+		return read_tzif_preamble(tzf, tz);
+	} else {
+		return -1;
+	}
+}
+
+static void read_32bit_header(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	uint32_t buffer[6];
+
+	memcpy(&buffer, *tzf, sizeof(buffer));
+	tz->_bit32.ttisgmtcnt = timelib_conv_int_unsigned(buffer[0]);
+	tz->_bit32.ttisstdcnt = timelib_conv_int_unsigned(buffer[1]);
+	tz->_bit32.leapcnt    = timelib_conv_int_unsigned(buffer[2]);
+	tz->_bit32.timecnt    = timelib_conv_int_unsigned(buffer[3]);
+	tz->_bit32.typecnt    = timelib_conv_int_unsigned(buffer[4]);
+	tz->_bit32.charcnt    = timelib_conv_int_unsigned(buffer[5]);
+
+	*tzf += sizeof(buffer);
+}
+
+static int read_64bit_transitions(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	int64_t *buffer = NULL;
+	uint32_t i;
+	unsigned char *cbuffer = NULL;
+
+	if (tz->bit64.timecnt) {
+		buffer = (int64_t*) timelib_malloc(tz->bit64.timecnt * sizeof(int64_t));
+		if (!buffer) {
+			return TIMELIB_ERROR_CANNOT_ALLOCATE;
+		}
+		memcpy(buffer, *tzf, sizeof(int64_t) * tz->bit64.timecnt);
+		*tzf += (sizeof(int64_t) * tz->bit64.timecnt);
+		for (i = 0; i < tz->bit64.timecnt; i++) {
+			buffer[i] = timelib_conv_int64_signed(buffer[i]);
+			/* Sanity check to see whether TS is just increasing */
+			if (i > 0 && !(buffer[i] > buffer[i - 1])) {
+				return TIMELIB_ERROR_CORRUPT_TRANSITIONS_DONT_INCREASE;
+			}
+		}
+
+		cbuffer = (unsigned char*) timelib_malloc(tz->bit64.timecnt * sizeof(unsigned char));
+		if (!cbuffer) {
+			timelib_free(buffer);
+			return TIMELIB_ERROR_CANNOT_ALLOCATE;
+		}
+		memcpy(cbuffer, *tzf, sizeof(unsigned char) * tz->bit64.timecnt);
+		*tzf += sizeof(unsigned char) * tz->bit64.timecnt;
+	}
+
+	tz->trans = buffer;
+	tz->trans_idx = cbuffer;
+
+	return 0;
+}
+
+static void skip_32bit_transitions(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	if (tz->_bit32.timecnt) {
+		*tzf += (sizeof(int32_t) * tz->_bit32.timecnt);
+		*tzf += sizeof(unsigned char) * tz->_bit32.timecnt;
+	}
+}
+
+static int read_64bit_types(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	unsigned char *buffer;
+	int32_t *leap_buffer;
+	unsigned int i, j;
+
+	/* Offset Types */
+	buffer = (unsigned char*) timelib_malloc(tz->bit64.typecnt * sizeof(unsigned char) * 6);
+	if (!buffer) {
+		return TIMELIB_ERROR_CANNOT_ALLOCATE;
+	}
+	memcpy(buffer, *tzf, sizeof(unsigned char) * 6 * tz->bit64.typecnt);
+	*tzf += sizeof(unsigned char) * 6 * tz->bit64.typecnt;
+
+	tz->type = (ttinfo*) timelib_malloc(tz->bit64.typecnt * sizeof(ttinfo));
+	if (!tz->type) {
+		timelib_free(buffer);
+		return TIMELIB_ERROR_CANNOT_ALLOCATE;
+	}
+
+	for (i = 0; i < tz->bit64.typecnt; i++) {
+		j = i * 6;
+		tz->type[i].offset = 0;
+		tz->type[i].offset += (int32_t) (((uint32_t) buffer[j]) << 24) + (buffer[j + 1] << 16) + (buffer[j + 2] << 8) + tz->type[i].offset + buffer[j + 3];
+		tz->type[i].isdst = buffer[j + 4];
+		tz->type[i].abbr_idx = buffer[j + 5];
+	}
+	timelib_free(buffer);
+
+	/* Abbreviations */
+	tz->timezone_abbr = (char*) timelib_malloc(tz->bit64.charcnt);
+	if (!tz->timezone_abbr) {
+		return TIMELIB_ERROR_CORRUPT_NO_ABBREVIATION;
+	}
+	memcpy(tz->timezone_abbr, *tzf, sizeof(char) * tz->bit64.charcnt);
+	*tzf += sizeof(char) * tz->bit64.charcnt;
+
+	/* Leap seconds (only use in 'right/') format */
+	if (tz->bit64.leapcnt) {
+		leap_buffer = (int32_t *) timelib_malloc(tz->bit64.leapcnt * (sizeof(int64_t) + sizeof(int32_t)));
+		if (!leap_buffer) {
+			return TIMELIB_ERROR_CANNOT_ALLOCATE;
+		}
+		memcpy(leap_buffer, *tzf, tz->bit64.leapcnt * (sizeof(int64_t) + sizeof(int32_t)));
+		*tzf += tz->bit64.leapcnt * (sizeof(int64_t) + sizeof(int32_t));
+
+		tz->leap_times = (tlinfo*) timelib_malloc(tz->bit64.leapcnt * sizeof(tlinfo));
+		if (!tz->leap_times) {
+			timelib_free(leap_buffer);
+			return TIMELIB_ERROR_CANNOT_ALLOCATE;
+		}
+		for (i = 0; i < tz->bit64.leapcnt; i++) {
+			tz->leap_times[i].trans = timelib_conv_int64_signed(leap_buffer[i * 3 + 1] * 4294967296 + leap_buffer[i * 3]);
+			tz->leap_times[i].offset = timelib_conv_int_signed(leap_buffer[i * 3 + 2]);
+		}
+		timelib_free(leap_buffer);
+	}
+
+	/* Standard/Wall Indicators (unused) */
+	if (tz->bit64.ttisstdcnt) {
+		buffer = (unsigned char*) timelib_malloc(tz->bit64.ttisstdcnt * sizeof(unsigned char));
+		if (!buffer) {
+			return TIMELIB_ERROR_CANNOT_ALLOCATE;
+		}
+		memcpy(buffer, *tzf, sizeof(unsigned char) * tz->bit64.ttisstdcnt);
+		*tzf += sizeof(unsigned char) * tz->bit64.ttisstdcnt;
+
+		for (i = 0; i < tz->bit64.ttisstdcnt; i++) {
+			tz->type[i].isstdcnt = buffer[i];
+		}
+		timelib_free(buffer);
+	}
+
+	/* UT/Local Time Indicators (unused) */
+	if (tz->bit64.ttisgmtcnt) {
+		buffer = (unsigned char*) timelib_malloc(tz->bit64.ttisgmtcnt * sizeof(unsigned char));
+		if (!buffer) {
+			return TIMELIB_ERROR_CANNOT_ALLOCATE;
+		}
+		memcpy(buffer, *tzf, sizeof(unsigned char) * tz->bit64.ttisgmtcnt);
+		*tzf += sizeof(unsigned char) * tz->bit64.ttisgmtcnt;
+
+		for (i = 0; i < tz->bit64.ttisgmtcnt; i++) {
+			tz->type[i].isgmtcnt = buffer[i];
+		}
+		timelib_free(buffer);
+	}
+
+	return 0;
+}
+
+static void skip_32bit_types(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	/* Offset Types */
+	*tzf += sizeof(unsigned char) * 6 * tz->_bit32.typecnt;
+
+	/* Abbreviations */
+	*tzf += sizeof(char) * tz->_bit32.charcnt;
+
+	/* Leap seconds (only use in 'right/') format */
+	if (tz->_bit32.leapcnt) {
+		*tzf += sizeof(int32_t) * tz->_bit32.leapcnt * 2;
+	}
+
+	/* Standard/Wall Indicators (unused) */
+	if (tz->_bit32.ttisstdcnt) {
+		*tzf += sizeof(unsigned char) * tz->_bit32.ttisstdcnt;
+	}
+
+	/* UT/Local Time Indicators (unused) */
+	if (tz->_bit32.ttisgmtcnt) {
+		*tzf += sizeof(unsigned char) * tz->_bit32.ttisgmtcnt;
+	}
+}
+
+static void skip_posix_string(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	int n_count = 0;
+
+	do {
+		if (*tzf[0] == '\n') {
+			n_count++;
+		}
+		(*tzf)++;
+	} while (n_count < 2);
+}
+
+static void read_location(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	uint32_t buffer[3];
+	uint32_t comments_len;
+
+	memcpy(&buffer, *tzf, sizeof(buffer));
+	tz->location.latitude = timelib_conv_int_unsigned(buffer[0]);
+	tz->location.latitude = (tz->location.latitude / 100000) - 90;
+	tz->location.longitude = timelib_conv_int_unsigned(buffer[1]);
+	tz->location.longitude = (tz->location.longitude / 100000) - 180;
+	comments_len = timelib_conv_int_unsigned(buffer[2]);
+	*tzf += sizeof(buffer);
+
+	tz->location.comments = timelib_malloc(comments_len + 1);
+	memcpy(tz->location.comments, *tzf, comments_len);
+	tz->location.comments[comments_len] = '\0';
+	*tzf += comments_len;
+}
+
+static void set_default_location_and_comments(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	tz->location.latitude = 0;
+	tz->location.longitude = 0;
+	tz->location.comments = timelib_malloc(2);
+	tz->location.comments[0] = '?';
+	tz->location.comments[1] = '\0';
+}
+
+void timelib_dump_tzinfo(timelib_tzinfo *tz)
+{
+	uint32_t i;
+
+	printf("Country Code:      %s\n", tz->location.country_code);
+	printf("Geo Location:      %f,%f\n", tz->location.latitude, tz->location.longitude);
+	printf("Comments:\n%s\n",          tz->location.comments);
+	printf("BC:                %s\n",  tz->bc ? "" : "yes");
+
+	printf("\n64-bit:\n");
+	printf("UTC/Local count:   " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.ttisgmtcnt);
+	printf("Std/Wall count:    " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.ttisstdcnt);
+	printf("Leap.sec. count:   " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.leapcnt);
+	printf("Trans. count:      " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.timecnt);
+	printf("Local types count: " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.typecnt);
+	printf("Zone Abbr. count:  " TIMELIB_ULONG_FMT "\n", (timelib_ulong) tz->bit64.charcnt);
+
+	printf ("%16s (%20s) = %3d [%5ld %1d %3d '%s' (%d,%d)]\n",
+		"", "", 0,
+		(long int) tz->type[0].offset,
+		tz->type[0].isdst,
+		tz->type[0].abbr_idx,
+		&tz->timezone_abbr[tz->type[0].abbr_idx],
+		tz->type[0].isstdcnt,
+		tz->type[0].isgmtcnt
+		);
+	for (i = 0; i < tz->bit64.timecnt; i++) {
+		printf ("%016" PRIX64 " (%20" PRId64 ") = %3d [%5ld %1d %3d '%s' (%d,%d)]\n",
+			tz->trans[i], tz->trans[i], tz->trans_idx[i],
+			(long int) tz->type[tz->trans_idx[i]].offset,
+			tz->type[tz->trans_idx[i]].isdst,
+			tz->type[tz->trans_idx[i]].abbr_idx,
+			&tz->timezone_abbr[tz->type[tz->trans_idx[i]].abbr_idx],
+			tz->type[tz->trans_idx[i]].isstdcnt,
+			tz->type[tz->trans_idx[i]].isgmtcnt
+			);
+	}
+	for (i = 0; i < tz->bit64.leapcnt; i++) {
+		printf ("%016" PRIX64 " (%20ld) = %d\n",
+			tz->leap_times[i].trans,
+			(long) tz->leap_times[i].trans,
+			tz->leap_times[i].offset);
+	}
+}
+
+static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const timelib_tzdb *tzdb)
+{
+	int left = 0, right = tzdb->index_size - 1;
+
+	if (tzdb->index_size == 0) {
+		return 0;
+	}
+
+	do {
+		int mid = ((unsigned)left + right) >> 1;
+		int cmp = timelib_strcasecmp(timezone, tzdb->index[mid].id);
+
+		if (cmp < 0) {
+			right = mid - 1;
+		} else if (cmp > 0) {
+			left = mid + 1;
+		} else { /* (cmp == 0) */
+			(*tzf) = &(tzdb->data[tzdb->index[mid].pos]);
+			return 1;
+		}
+
+	} while (left <= right);
+
+	return 0;
+}
+
+const timelib_tzdb *timelib_builtin_db(void)
+{
+	return &timezonedb_builtin;
+}
+
+const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(const timelib_tzdb *tzdb, int *count)
+{
+	*count = tzdb->index_size;
+	return tzdb->index;
+}
+
+int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb)
+{
+	const unsigned char *tzf;
+	return (seek_to_tz_position(&tzf, timezone, tzdb));
+}
+
+static int skip_64bit_preamble(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	if (memcmp(*tzf, "TZif2", 5) == 0) {
+		*tzf += 20;
+		return 1;
+	} else if (memcmp(*tzf, "TZif3", 5) == 0) {
+		*tzf += 20;
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static void read_64bit_header(const unsigned char **tzf, timelib_tzinfo *tz)
+{
+	uint32_t buffer[6];
+
+	memcpy(&buffer, *tzf, sizeof(buffer));
+	tz->bit64.ttisgmtcnt = timelib_conv_int_unsigned(buffer[0]);
+	tz->bit64.ttisstdcnt = timelib_conv_int_unsigned(buffer[1]);
+	tz->bit64.leapcnt    = timelib_conv_int_unsigned(buffer[2]);
+	tz->bit64.timecnt    = timelib_conv_int_unsigned(buffer[3]);
+	tz->bit64.typecnt    = timelib_conv_int_unsigned(buffer[4]);
+	tz->bit64.charcnt    = timelib_conv_int_unsigned(buffer[5]);
+	*tzf += sizeof(buffer);
+}
+
+static timelib_tzinfo* timelib_tzinfo_ctor(char *name)
+{
+	timelib_tzinfo *t;
+	t = timelib_calloc(1, sizeof(timelib_tzinfo));
+	t->name = timelib_strdup(name);
+
+	return t;
+}
+
+timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb, int *error_code)
+{
+	const unsigned char *tzf;
+	timelib_tzinfo *tmp;
+	int version;
+	int transitions_result, types_result;
+	unsigned int type; /* TIMELIB_TZINFO_PHP or TIMELIB_TZINFO_ZONEINFO */
+
+	if (seek_to_tz_position(&tzf, timezone, tzdb)) {
+		tmp = timelib_tzinfo_ctor(timezone);
+
+		version = read_preamble(&tzf, tmp, &type);
+		if (version < 2 || version > 3) {
+			*error_code = TIMELIB_ERROR_UNSUPPORTED_VERSION;
+			timelib_tzinfo_dtor(tmp);
+			return NULL;
+		}
+//printf("- timezone: %s, version: %0d\n", timezone, version);
+
+		read_32bit_header(&tzf, tmp);
+		skip_32bit_transitions(&tzf, tmp);
+		skip_32bit_types(&tzf, tmp);
+
+		if (!skip_64bit_preamble(&tzf, tmp)) {
+			/* 64 bit preamble is not in place */
+			*error_code = TIMELIB_ERROR_CORRUPT_NO_64BIT_PREAMBLE;
+			timelib_tzinfo_dtor(tmp);
+			return NULL;
+		}
+		read_64bit_header(&tzf, tmp);
+		if ((transitions_result = read_64bit_transitions(&tzf, tmp)) != 0) {
+			/* Corrupt file as transitions do not increase */
+			*error_code = transitions_result;
+			timelib_tzinfo_dtor(tmp);
+			return NULL;
+		}
+		if ((types_result = read_64bit_types(&tzf, tmp)) != 0) {
+			*error_code = types_result;
+			timelib_tzinfo_dtor(tmp);
+			return NULL;
+		}
+		skip_posix_string(&tzf, tmp);
+
+		if (type == TIMELIB_TZINFO_PHP) {
+			read_location(&tzf, tmp);
+		} else {
+			set_default_location_and_comments(&tzf, tmp);
+		}
+	} else {
+		*error_code = TIMELIB_ERROR_NO_SUCH_TIMEZONE;
+		tmp = NULL;
+	}
+
+	return tmp;
+}
+
+void timelib_tzinfo_dtor(timelib_tzinfo *tz)
+{
+	TIMELIB_TIME_FREE(tz->name);
+	TIMELIB_TIME_FREE(tz->trans);
+	TIMELIB_TIME_FREE(tz->trans_idx);
+	TIMELIB_TIME_FREE(tz->type);
+	TIMELIB_TIME_FREE(tz->timezone_abbr);
+	TIMELIB_TIME_FREE(tz->leap_times);
+	TIMELIB_TIME_FREE(tz->location.comments);
+	TIMELIB_TIME_FREE(tz);
+	tz = NULL;
+}
+
+timelib_tzinfo *timelib_tzinfo_clone(timelib_tzinfo *tz)
+{
+	timelib_tzinfo *tmp = timelib_tzinfo_ctor(tz->name);
+	tmp->_bit32.ttisgmtcnt = tz->_bit32.ttisgmtcnt;
+	tmp->_bit32.ttisstdcnt = tz->_bit32.ttisstdcnt;
+	tmp->_bit32.leapcnt = tz->_bit32.leapcnt;
+	tmp->_bit32.timecnt = tz->_bit32.timecnt;
+	tmp->_bit32.typecnt = tz->_bit32.typecnt;
+	tmp->_bit32.charcnt = tz->_bit32.charcnt;
+	tmp->bit64.ttisgmtcnt = tz->bit64.ttisgmtcnt;
+	tmp->bit64.ttisstdcnt = tz->bit64.ttisstdcnt;
+	tmp->bit64.leapcnt = tz->bit64.leapcnt;
+	tmp->bit64.timecnt = tz->bit64.timecnt;
+	tmp->bit64.typecnt = tz->bit64.typecnt;
+	tmp->bit64.charcnt = tz->bit64.charcnt;
+
+	if (tz->bit64.timecnt) {
+		tmp->trans = (int64_t *) timelib_malloc(tz->bit64.timecnt * sizeof(int64_t));
+		tmp->trans_idx = (unsigned char*) timelib_malloc(tz->bit64.timecnt * sizeof(unsigned char));
+		memcpy(tmp->trans, tz->trans, tz->bit64.timecnt * sizeof(int64_t));
+		memcpy(tmp->trans_idx, tz->trans_idx, tz->bit64.timecnt * sizeof(unsigned char));
+	}
+
+	tmp->type = (ttinfo*) timelib_malloc(tz->bit64.typecnt * sizeof(ttinfo));
+	memcpy(tmp->type, tz->type, tz->bit64.typecnt * sizeof(ttinfo));
+
+	tmp->timezone_abbr = (char*) timelib_malloc(tz->bit64.charcnt);
+	memcpy(tmp->timezone_abbr, tz->timezone_abbr, tz->bit64.charcnt);
+
+	if (tz->bit64.leapcnt) {
+		tmp->leap_times = (tlinfo*) timelib_malloc(tz->bit64.leapcnt * sizeof(tlinfo));
+		memcpy(tmp->leap_times, tz->leap_times, tz->bit64.leapcnt * sizeof(tlinfo));
+	}
+
+	return tmp;
+}
+
+static ttinfo* fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time)
+{
+	uint32_t i;
+
+	/* If there is no transition time, we pick the first one, if that doesn't
+	 * exist we return NULL */
+	if (!tz->bit64.timecnt || !tz->trans) {
+		if (tz->bit64.typecnt == 1) {
+			*transition_time = INT64_MIN;
+			return &(tz->type[0]);
+		}
+		return NULL;
+	}
+
+	/* If the TS is lower than the first transition time, then we scan over
+	 * all the transition times to find the first non-DST one, or the first
+	 * one in case there are only DST entries. Not sure which smartass came up
+	 * with this idea in the first though :) */
+	if (ts < tz->trans[0]) {
+		*transition_time = INT64_MIN;
+		return &(tz->type[0]);
+	}
+
+	/* In all other cases we loop through the available transition times to find
+	 * the correct entry */
+	for (i = 0; i < tz->bit64.timecnt; i++) {
+		if (ts < tz->trans[i]) {
+			*transition_time = tz->trans[i - 1];
+			return &(tz->type[tz->trans_idx[i - 1]]);
+		}
+	}
+	*transition_time = tz->trans[tz->bit64.timecnt - 1];
+	return &(tz->type[tz->trans_idx[tz->bit64.timecnt - 1]]);
+}
+
+static tlinfo* fetch_leaptime_offset(timelib_tzinfo *tz, timelib_sll ts)
+{
+	int i;
+
+	if (!tz->bit64.leapcnt || !tz->leap_times) {
+		return NULL;
+	}
+
+	for (i = tz->bit64.leapcnt - 1; i > 0; i--) {
+		if (ts > tz->leap_times[i].trans) {
+			return &(tz->leap_times[i]);
+		}
+	}
+	return NULL;
+}
+
+int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz)
+{
+	ttinfo *to;
+	timelib_sll dummy;
+
+	if ((to = fetch_timezone_offset(tz, ts, &dummy))) {
+		return to->isdst;
+	}
+	return -1;
+}
+
+timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz)
+{
+	ttinfo *to;
+	tlinfo *tl;
+	int32_t offset = 0, leap_secs = 0;
+	char *abbr;
+	timelib_time_offset *tmp = timelib_time_offset_ctor();
+	timelib_sll                transition_time;
+
+	if ((to = fetch_timezone_offset(tz, ts, &transition_time))) {
+		offset = to->offset;
+		abbr = &(tz->timezone_abbr[to->abbr_idx]);
+		tmp->is_dst = to->isdst;
+		tmp->transition_time = transition_time;
+	} else {
+		offset = 0;
+		abbr = tz->timezone_abbr;
+		tmp->is_dst = 0;
+		tmp->transition_time = 0;
+	}
+
+	if ((tl = fetch_leaptime_offset(tz, ts))) {
+		leap_secs = -tl->offset;
+	}
+
+	tmp->offset = offset;
+	tmp->leap_secs = leap_secs;
+	tmp->abbr = abbr ? timelib_strdup(abbr) : timelib_strdup("GMT");
+
+	return tmp;
+}
+
+timelib_sll timelib_get_current_offset(timelib_time *t)
+{
+	timelib_time_offset *gmt_offset;
+	timelib_sll retval;
+
+	switch (t->zone_type) {
+		case TIMELIB_ZONETYPE_ABBR:
+		case TIMELIB_ZONETYPE_OFFSET:
+			return t->z + (t->dst * 3600);
+
+		case TIMELIB_ZONETYPE_ID:
+			gmt_offset = timelib_get_time_zone_info(t->sse, t->tz_info);
+			retval = gmt_offset->offset;
+			timelib_time_offset_dtor(gmt_offset);
+			return retval;
+
+		default:
+			return 0;
+	}
+}

+ 366 - 0
timelib/timelib.c

@@ -0,0 +1,366 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ *
+ * Portions copyright (c) 1998-2017 Zend Technologies Ltd.
+ *
+ * The timelib_strcasecmp and timelib_strncasecmp are taken from PHP's
+ * Zend/zend_operators.[hc] source files.
+ *
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+#include <ctype.h>
+#include <math.h>
+
+#define TIMELIB_LLABS(y) (y < 0 ? (y * -1) : y)
+
+const char *timelib_error_messages[8] = {
+	"No error",
+	"Can not allocate buffer for parsing",
+	"Corrupt tzfile: The transitions in the file don't always increase",
+	"Corrupt tzfile: The expected 64-bit preamble is missing",
+	"Corrupt tzfile: No abbreviation could be found for a transition",
+	"The version used in this timezone identifier is unsupported",
+	"No timezone with this name could be found",
+};
+
+const char *timelib_get_error_message(int error_code)
+{
+	int entries = sizeof(timelib_error_messages) / sizeof(char*);
+
+	if (error_code >= 0 && error_code < entries) {
+		return timelib_error_messages[error_code];
+	}
+	return "Unknown error code";
+}
+
+timelib_time* timelib_time_ctor(void)
+{
+	timelib_time *t;
+	t = timelib_calloc(1, sizeof(timelib_time));
+
+	return t;
+}
+
+void timelib_time_dtor(timelib_time* t)
+{
+	TIMELIB_TIME_FREE(t->tz_abbr);
+	TIMELIB_TIME_FREE(t);
+}
+
+int timelib_time_compare(timelib_time *t1, timelib_time *t2)
+{
+	if (t1->sse == t2->sse) {
+		if (t1->us == t2->us) {
+			return 0;
+		}
+
+		return (t1->us < t2->us) ? -1 : 1;
+	}
+
+	return (t1->sse < t2->sse) ? -1 : 1;
+}
+
+timelib_time* timelib_time_clone(timelib_time *orig)
+{
+	timelib_time *tmp = timelib_time_ctor();
+	memcpy(tmp, orig, sizeof(timelib_time));
+	if (orig->tz_abbr) {
+		tmp->tz_abbr = timelib_strdup(orig->tz_abbr);
+	}
+	if (orig->tz_info) {
+		tmp->tz_info = orig->tz_info;
+	}
+	return tmp;
+}
+
+timelib_rel_time* timelib_rel_time_ctor(void)
+{
+	timelib_rel_time *t;
+	t = timelib_calloc(1, sizeof(timelib_rel_time));
+
+	return t;
+}
+
+void timelib_rel_time_dtor(timelib_rel_time* t)
+{
+	TIMELIB_TIME_FREE(t);
+}
+
+timelib_rel_time* timelib_rel_time_clone(timelib_rel_time *rel)
+{
+	timelib_rel_time *tmp = timelib_rel_time_ctor();
+	memcpy(tmp, rel, sizeof(timelib_rel_time));
+	return tmp;
+}
+
+void timelib_time_tz_abbr_update(timelib_time* tm, char* tz_abbr)
+{
+	unsigned int i;
+	size_t tz_abbr_len = strlen(tz_abbr);
+
+	TIMELIB_TIME_FREE(tm->tz_abbr);
+	tm->tz_abbr = timelib_strdup(tz_abbr);
+	for (i = 0; i < tz_abbr_len; i++) {
+		tm->tz_abbr[i] = toupper(tz_abbr[i]);
+	}
+}
+
+timelib_time_offset* timelib_time_offset_ctor(void)
+{
+	timelib_time_offset *t;
+	t = timelib_calloc(1, sizeof(timelib_time_offset));
+
+	return t;
+}
+
+void timelib_time_offset_dtor(timelib_time_offset* t)
+{
+	TIMELIB_TIME_FREE(t->abbr);
+	TIMELIB_TIME_FREE(t);
+}
+
+char *timelib_get_tz_abbr_ptr(timelib_time *t)
+{
+	if (!t->sse_uptodate) {
+		timelib_update_ts(t, NULL);
+	};
+	return t->tz_abbr;
+}
+
+void timelib_error_container_dtor(timelib_error_container *errors)
+{
+	int i;
+
+	for (i = 0; i < errors->warning_count; i++) {
+		timelib_free(errors->warning_messages[i].message);
+	}
+	timelib_free(errors->warning_messages);
+	for (i = 0; i < errors->error_count; i++) {
+		timelib_free(errors->error_messages[i].message);
+	}
+	timelib_free(errors->error_messages);
+	timelib_free(errors);
+}
+
+timelib_long timelib_date_to_int(timelib_time *d, int *error)
+{
+	timelib_sll ts;
+
+	ts = d->sse;
+
+	if (ts < TIMELIB_LONG_MIN || ts > TIMELIB_LONG_MAX) {
+		if (error) {
+			*error = 1;
+		}
+		return 0;
+	}
+	if (error) {
+		*error = 0;
+	}
+	return (timelib_long) d->sse;
+}
+
+void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec)
+{
+	if (h > 0) {
+		*hour = floor(h);
+		*min = floor((h - *hour) * 60);
+		*sec = (h - *hour - ((float) *min / 60)) * 3600;
+	} else {
+		*hour = ceil(h);
+		*min = 0 - ceil((h - *hour) * 60);
+		*sec = 0 - (h - *hour - ((float) *min / -60)) * 3600;
+	}
+}
+
+void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h)
+{
+	if (hour > 0) {
+		*h = ((double)hour + (double)min / 60 + (double)sec / 3600);
+	} else {
+		*h = ((double)hour - (double)min / 60 - (double)sec / 3600);
+	}
+}
+
+static const unsigned char timelib_tolower_map[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+	0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+#define timelib_tolower(c) (timelib_tolower_map[(unsigned char)(c)])
+#undef MIN
+#undef MAX
+#define MAX(a, b)  (((a)>(b))?(a):(b))
+#define MIN(a, b)  (((a)<(b))?(a):(b))
+
+int timelib_strcasecmp(const char *s1, const char *s2)
+{
+	size_t len;
+	size_t len1 = strlen(s1);
+	size_t len2 = strlen(s2);
+	int c1, c2;
+
+	if (s1 == s2) {
+		return 0;
+	}
+
+	len = MIN(len1, len2);
+	while (len--) {
+		c1 = timelib_tolower(*(unsigned char *)s1++);
+		c2 = timelib_tolower(*(unsigned char *)s2++);
+		if (c1 != c2) {
+			return c1 - c2;
+		}
+	}
+
+	return (int)(len1 - len2);
+}
+
+int timelib_strncasecmp(const char *s1, const char *s2, size_t length)
+{
+	size_t len;
+	size_t len1 = strlen(s1);
+	size_t len2 = strlen(s2);
+	int c1, c2;
+
+	if (s1 == s2) {
+		return 0;
+	}
+	len = MIN(length, MIN(len1, len2));
+	while (len--) {
+		c1 = timelib_tolower(*(unsigned char *)s1++);
+		c2 = timelib_tolower(*(unsigned char *)s2++);
+		if (c1 != c2) {
+			return c1 - c2;
+		}
+	}
+
+	return (int)(MIN(length, len1) - MIN(length, len2));
+}
+
+#undef MIN
+#undef MAX
+
+void timelib_dump_date(timelib_time *d, int options)
+{
+	if ((options & 2) == 2) {
+		printf("TYPE: %d ", d->zone_type);
+	}
+	printf("TS: %lld | %s%04lld-%02lld-%02lld %02lld:%02lld:%02lld",
+		d->sse, d->y < 0 ? "-" : "", TIMELIB_LLABS(d->y), d->m, d->d, d->h, d->i, d->s);
+	if (d->us > 0) {
+		printf(" 0.%06lld", d->us);
+	}
+
+	if (d->is_localtime) {
+		switch (d->zone_type) {
+			case TIMELIB_ZONETYPE_OFFSET: /* Only offset */
+				printf(" GMT %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
+				break;
+			case TIMELIB_ZONETYPE_ID: /* Timezone struct */
+				/* Show abbreviation if wanted */
+				if (d->tz_abbr) {
+					printf(" %s", d->tz_abbr);
+				}
+				/* Do we have a TimeZone struct? */
+				if (d->tz_info) {
+					printf(" %s", d->tz_info->name);
+				}
+				break;
+			case TIMELIB_ZONETYPE_ABBR:
+				printf(" %s", d->tz_abbr);
+				printf(" %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
+				break;
+		}
+	}
+
+	if ((options & 1) == 1) {
+		if (d->have_relative) {
+			printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS",
+				d->relative.y, d->relative.m, d->relative.d, d->relative.h, d->relative.i, d->relative.s);
+			if (d->relative.us) {
+				printf(" 0.%06lld", d->relative.us);
+			}
+			if (d->relative.first_last_day_of != 0) {
+				switch (d->relative.first_last_day_of) {
+					case 1:
+						printf(" / first day of");
+						break;
+					case 2:
+						printf(" / last day of");
+						break;
+				}
+			}
+			if (d->relative.have_weekday_relative) {
+				printf(" / %d.%d", d->relative.weekday, d->relative.weekday_behavior);
+			}
+			if (d->relative.have_special_relative) {
+				switch (d->relative.special.type) {
+					case TIMELIB_SPECIAL_WEEKDAY:
+						printf(" / %lld weekday", d->relative.special.amount);
+						break;
+					case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH:
+						printf(" / x y of z month");
+						break;
+					case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH:
+						printf(" / last y of z month");
+						break;
+				}
+			}
+		}
+	}
+	printf("\n");
+}
+
+void timelib_dump_rel_time(timelib_rel_time *d)
+{
+	printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS (days: %lld)%s",
+		d->y, d->m, d->d, d->h, d->i, d->s, d->days, d->invert ? " inverted" : "");
+	if (d->first_last_day_of != 0) {
+		switch (d->first_last_day_of) {
+			case 1:
+				printf(" / first day of");
+				break;
+			case 2:
+				printf(" / last day of");
+				break;
+		}
+	}
+	printf("\n");
+}

+ 955 - 0
timelib/timelib.h

@@ -0,0 +1,955 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ * Copyright (c) 2018 MongoDB, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __TIMELIB_H__
+#define __TIMELIB_H__
+
+#ifdef HAVE_TIMELIB_CONFIG_H
+# include "timelib_config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <inttypes.h>
+
+# ifndef HAVE_INT32_T
+#  if SIZEOF_INT == 4
+typedef int int32_t;
+#  elif SIZEOF_LONG == 4
+typedef long int int32_t;
+#  endif
+# endif
+
+# ifndef HAVE_UINT32_T
+#  if SIZEOF_INT == 4
+typedef unsigned int uint32_t;
+#  elif SIZEOF_LONG == 4
+typedef unsigned long int uint32_t;
+#  endif
+# endif
+
+#ifdef _WIN32
+# if _MSC_VER >= 1600
+# include <stdint.h>
+# endif
+# ifndef SIZEOF_INT
+#  define SIZEOF_INT 4
+# endif
+# ifndef SIZEOF_LONG
+#  define SIZEOF_LONG 4
+# endif
+# ifndef PRId32
+#  define PRId32       "I32d"
+# endif
+# ifndef PRIu32
+#  define PRIu32       "I32u"
+# endif
+# ifndef PRId64
+#  define PRId64       "I64d"
+# endif
+# ifndef PRIu64
+#  define PRIu64       "I64u"
+# endif
+# ifndef INT32_MAX
+#define INT32_MAX    _I32_MAX
+# endif
+# ifndef INT32_MIN
+#define INT32_MIN    ((int32_t)_I32_MIN)
+# endif
+# ifndef UINT32_MAX
+#define UINT32_MAX   _UI32_MAX
+# endif
+# ifndef INT64_MIN
+#define INT64_MIN    ((int64_t)_I64_MIN)
+# endif
+# ifndef INT64_MAX
+#define INT64_MAX    _I64_MAX
+# endif
+# ifndef UINT64_MAX
+#define UINT64_MAX   _UI64_MAX
+# endif
+#endif
+
+#if (defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64)) && !defined(TIMELIB_FORCE_LONG32)
+typedef int64_t timelib_long;
+typedef uint64_t timelib_ulong;
+# define TIMELIB_LONG_MAX INT64_MAX
+# define TIMELIB_LONG_MIN INT64_MIN
+# define TIMELIB_ULONG_MAX UINT64_MAX
+# define TIMELIB_LONG_FMT "%" PRId64
+# define TIMELIB_ULONG_FMT "%" PRIu64
+#else
+typedef int32_t timelib_long;
+typedef uint32_t timelib_ulong;
+# define TIMELIB_LONG_MAX INT32_MAX
+# define TIMELIB_LONG_MIN INT32_MIN
+# define TIMELIB_ULONG_MAX UINT32_MAX
+# define TIMELIB_LONG_FMT "%" PRId32
+# define TIMELIB_ULONG_FMT "%" PRIu32
+#endif
+
+#if defined(_MSC_VER)
+typedef uint64_t timelib_ull;
+typedef int64_t timelib_sll;
+# define TIMELIB_LL_CONST(n) n ## i64
+#else
+typedef unsigned long long timelib_ull;
+typedef signed long long timelib_sll;
+# define TIMELIB_LL_CONST(n) n ## ll
+#endif
+
+typedef struct _ttinfo ttinfo;
+typedef struct _tlinfo tlinfo;
+
+typedef struct _tlocinfo
+{
+	char country_code[3];
+	double latitude;
+	double longitude;
+	char *comments;
+} tlocinfo;
+
+typedef struct _timelib_tzinfo
+{
+	char    *name;
+	struct {
+		uint32_t ttisgmtcnt;
+		uint32_t ttisstdcnt;
+		uint32_t leapcnt;
+		uint32_t timecnt;
+		uint32_t typecnt;
+		uint32_t charcnt;
+	} _bit32;
+	struct {
+		uint64_t ttisgmtcnt;
+		uint64_t ttisstdcnt;
+		uint64_t leapcnt;
+		uint64_t timecnt;
+		uint64_t typecnt;
+		uint64_t charcnt;
+	} bit64;
+
+	int64_t *trans;
+	unsigned char *trans_idx;
+
+	ttinfo  *type;
+	char    *timezone_abbr;
+
+	tlinfo  *leap_times;
+	unsigned char bc;
+	tlocinfo location;
+} timelib_tzinfo;
+
+typedef struct _timelib_rel_time {
+	timelib_sll y, m, d; /* Years, Months and Days */
+	timelib_sll h, i, s; /* Hours, mInutes and Seconds */
+	timelib_sll us;      /* Microseconds */
+
+	int weekday; /* Stores the day in 'next monday' */
+	int weekday_behavior; /* 0: the current day should *not* be counted when advancing forwards; 1: the current day *should* be counted */
+
+	int first_last_day_of;
+	int invert; /* Whether the difference should be inverted */
+	timelib_sll days; /* Contains the number of *days*, instead of Y-M-D differences */
+
+	struct {
+		unsigned int type;
+		timelib_sll amount;
+	} special;
+
+	unsigned int   have_weekday_relative, have_special_relative;
+} timelib_rel_time;
+
+typedef struct _timelib_time_offset {
+	int32_t      offset;
+	unsigned int leap_secs;
+	unsigned int is_dst;
+	char        *abbr;
+	timelib_sll  transition_time;
+} timelib_time_offset;
+
+typedef struct _timelib_time {
+	timelib_sll      y, m, d;     /* Year, Month, Day */
+	timelib_sll      h, i, s;     /* Hour, mInute, Second */
+	timelib_sll      us;          /* Microseconds */
+	int              z;           /* UTC offset in seconds */
+	char            *tz_abbr;     /* Timezone abbreviation (display only) */
+	timelib_tzinfo  *tz_info;     /* Timezone structure */
+	signed int       dst;         /* Flag if we were parsing a DST zone */
+	timelib_rel_time relative;
+
+	timelib_sll      sse;         /* Seconds since epoch */
+
+	unsigned int   have_time, have_date, have_zone, have_relative, have_weeknr_day;
+
+	unsigned int   sse_uptodate; /* !0 if the sse member is up to date with the date/time members */
+	unsigned int   tim_uptodate; /* !0 if the date/time members are up to date with the sse member */
+	unsigned int   is_localtime; /*  1 if the current struct represents localtime, 0 if it is in GMT */
+	unsigned int   zone_type;    /*  1 time offset,
+	                              *  3 TimeZone identifier,
+	                              *  2 TimeZone abbreviation */
+} timelib_time;
+
+typedef struct _timelib_abbr_info {
+	timelib_sll  utc_offset;
+	char        *abbr;
+	int          dst;
+} timelib_abbr_info;
+
+#define TIMELIB_WARN_MASK                      0x1ff
+#define TIMELIB_ERR_MASK                       0x2ff
+
+#define TIMELIB_WARN_DOUBLE_TZ                 0x101
+#define TIMELIB_WARN_INVALID_TIME              0x102
+#define TIMELIB_WARN_INVALID_DATE              0x103
+#define TIMELIB_WARN_TRAILING_DATA             0x11a
+
+#define TIMELIB_ERR_DOUBLE_TZ                  0x201
+#define TIMELIB_ERR_TZID_NOT_FOUND             0x202
+#define TIMELIB_ERR_DOUBLE_TIME                0x203
+#define TIMELIB_ERR_DOUBLE_DATE                0x204
+#define TIMELIB_ERR_UNEXPECTED_CHARACTER       0x205
+#define TIMELIB_ERR_EMPTY_STRING               0x206
+#define TIMELIB_ERR_UNEXPECTED_DATA            0x207
+#define TIMELIB_ERR_NO_TEXTUAL_DAY             0x208
+#define TIMELIB_ERR_NO_TWO_DIGIT_DAY           0x209
+#define TIMELIB_ERR_NO_THREE_DIGIT_DAY_OF_YEAR 0x20a
+#define TIMELIB_ERR_NO_TWO_DIGIT_MONTH         0x20b
+#define TIMELIB_ERR_NO_TEXTUAL_MONTH           0x20c
+#define TIMELIB_ERR_NO_TWO_DIGIT_YEAR          0x20d
+#define TIMELIB_ERR_NO_FOUR_DIGIT_YEAR         0x20e
+#define TIMELIB_ERR_NO_TWO_DIGIT_HOUR          0x20f
+#define TIMELIB_ERR_HOUR_LARGER_THAN_12        0x210
+#define TIMELIB_ERR_MERIDIAN_BEFORE_HOUR       0x211
+#define TIMELIB_ERR_NO_MERIDIAN                0x212
+#define TIMELIB_ERR_NO_TWO_DIGIT_MINUTE        0x213
+#define TIMELIB_ERR_NO_TWO_DIGIT_SECOND        0x214
+#define TIMELIB_ERR_NO_SIX_DIGIT_MICROSECOND   0x215
+#define TIMELIB_ERR_NO_SEP_SYMBOL              0x216
+#define TIMELIB_ERR_EXPECTED_ESCAPE_CHAR       0x217
+#define TIMELIB_ERR_NO_ESCAPED_CHAR            0x218
+#define TIMELIB_ERR_WRONG_FORMAT_SEP           0x219
+#define TIMELIB_ERR_TRAILING_DATA              0x21a
+#define TIMELIB_ERR_DATA_MISSING               0x21b
+#define TIMELIB_ERR_NO_THREE_DIGIT_MILLISECOND 0x21c
+#define TIMELIB_ERR_NO_FOUR_DIGIT_YEAR_ISO     0x21d
+#define TIMELIB_ERR_NO_TWO_DIGIT_WEEK          0x21e
+#define TIMELIB_ERR_INVALID_WEEK               0x21f
+#define TIMELIB_ERR_NO_DAY_OF_WEEK             0x220
+#define TIMELIB_ERR_INVALID_DAY_OF_WEEK        0x221
+#define TIMELIB_ERR_INVALID_SPECIFIER          0x222
+#define TIMELIB_ERR_INVALID_TZ_OFFSET          0x223
+#define TIMELIB_ERR_FORMAT_LITERAL_MISMATCH    0x224
+#define TIMELIB_ERR_MIX_ISO_WITH_NATURAL       0x225
+
+#define TIMELIB_ZONETYPE_OFFSET 1
+#define TIMELIB_ZONETYPE_ABBR   2
+#define TIMELIB_ZONETYPE_ID     3
+
+typedef struct _timelib_error_message {
+	int         error_code;
+	int         position;
+	char        character;
+	char       *message;
+} timelib_error_message;
+
+typedef struct _timelib_error_container {
+	timelib_error_message *error_messages;
+	timelib_error_message *warning_messages;
+	int                    error_count;
+	int                    warning_count;
+} timelib_error_container;
+
+typedef struct _timelib_tz_lookup_table {
+	char       *name;
+	int         type;
+	float       gmtoffset;
+	char       *full_tz_name;
+} timelib_tz_lookup_table;
+
+typedef struct _timelib_tzdb_index_entry {
+	char *id;
+	unsigned int pos;
+} timelib_tzdb_index_entry;
+
+typedef struct _timelib_tzdb {
+	char                           *version;
+	int                             index_size;
+	const timelib_tzdb_index_entry *index;
+	const unsigned char            *data;
+} timelib_tzdb;
+
+#ifndef timelib_malloc
+# define timelib_malloc  malloc
+# define timelib_realloc realloc
+# define timelib_calloc  calloc
+# define timelib_strdup  strdup
+# define timelib_free    free
+#endif
+
+#define TIMELIB_VERSION 201802
+#define TIMELIB_EXTENDED_VERSION 20180201
+#define TIMELIB_ASCII_VERSION "2018.02"
+
+#define TIMELIB_NONE             0x00
+#define TIMELIB_OVERRIDE_TIME    0x01
+#define TIMELIB_NO_CLONE         0x02
+
+#define TIMELIB_UNSET   -99999
+
+/* An entry for each of these error codes is also in the
+ * timelib_error_messages array in timelib.c */
+#define TIMELIB_ERROR_NO_ERROR                            0x00
+#define TIMELIB_ERROR_CANNOT_ALLOCATE                     0x01
+#define TIMELIB_ERROR_CORRUPT_TRANSITIONS_DONT_INCREASE   0x02
+#define TIMELIB_ERROR_CORRUPT_NO_64BIT_PREAMBLE           0x03
+#define TIMELIB_ERROR_CORRUPT_NO_ABBREVIATION             0x04
+#define TIMELIB_ERROR_UNSUPPORTED_VERSION                 0x05
+#define TIMELIB_ERROR_NO_SUCH_TIMEZONE                    0x06
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum _timelib_format_specifier_code {
+	TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS = 0,
+	TIMELIB_FORMAT_ANY_SEPARATOR,
+	TIMELIB_FORMAT_DAY_TWO_DIGIT,
+	TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED,
+	TIMELIB_FORMAT_DAY_OF_WEEK_ISO,
+	TIMELIB_FORMAT_DAY_OF_WEEK,
+	TIMELIB_FORMAT_DAY_OF_YEAR,
+	TIMELIB_FORMAT_DAY_SUFFIX,
+	TIMELIB_FORMAT_END,
+	TIMELIB_FORMAT_EPOCH_SECONDS,
+	TIMELIB_FORMAT_ESCAPE,
+	TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX,
+	TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED,
+	TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX,
+	TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED,
+	TIMELIB_FORMAT_LITERAL,
+	TIMELIB_FORMAT_MERIDIAN,
+	TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT,
+	TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT,
+	TIMELIB_FORMAT_MINUTE_TWO_DIGIT,
+	TIMELIB_FORMAT_MONTH_TWO_DIGIT,
+	TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED,
+	TIMELIB_FORMAT_RANDOM_CHAR,
+	TIMELIB_FORMAT_RESET_ALL,
+	TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET,
+	TIMELIB_FORMAT_SECOND_TWO_DIGIT,
+	TIMELIB_FORMAT_SEPARATOR,
+	TIMELIB_FORMAT_SKIP_TO_SEPARATOR,
+	TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER,
+	TIMELIB_FORMAT_TEXTUAL_DAY_FULL,
+	TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER,
+	TIMELIB_FORMAT_TEXTUAL_MONTH_FULL,
+	TIMELIB_FORMAT_TIMEZONE_OFFSET,
+	TIMELIB_FORMAT_TIMEZONE_OFFSET_MINUTES,
+	TIMELIB_FORMAT_WEEK_OF_YEAR_ISO,
+	TIMELIB_FORMAT_WEEK_OF_YEAR,
+	TIMELIB_FORMAT_WHITESPACE,
+	TIMELIB_FORMAT_YEAR_TWO_DIGIT,
+	TIMELIB_FORMAT_YEAR_FOUR_DIGIT,
+	TIMELIB_FORMAT_YEAR_ISO
+} timelib_format_specifier_code;
+
+typedef struct _timelib_format_specifier {
+	char                          specifier;
+	timelib_format_specifier_code code;
+} timelib_format_specifier;
+
+typedef struct _timelib_format_config {
+	const timelib_format_specifier *format_map;
+	/* Format speciifiers must be preceded by 'prefix_char' if not '\0'. */
+	char                            prefix_char;
+} timelib_format_config;
+
+/* Function pointers */
+typedef timelib_tzinfo* (*timelib_tz_get_wrapper)(char *tzname, const timelib_tzdb *tzdb, int *error_code);
+
+/* From dow.c */
+/* Calculates the day of the week from y, m, and d. 0=Sunday..6=Saturday */
+timelib_sll timelib_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d);
+
+/* Calculates the day of the ISO week from y, m, and d. 1=Monday, 7=Sunday */
+timelib_sll timelib_iso_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d);
+
+/* Calculates the day of the year according to y-m-d. 0=Jan 1st..364/365=Dec
+ * 31st */
+timelib_sll timelib_day_of_year(timelib_sll y, timelib_sll m, timelib_sll d);
+
+/* Calculates the day of the year according to y-w-dow. 0..364/365 */
+timelib_sll timelib_daynr_from_weeknr(timelib_sll iy, timelib_sll iw, timelib_sll id);
+
+/* Calculates the number of days in month m for year y. 28..31 */
+timelib_sll timelib_days_in_month(timelib_sll y, timelib_sll m);
+
+/* Calculates the ISO year and week from y, m, and d, into iw and iy */
+void timelib_isoweek_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iw, timelib_sll *iy);
+
+/* Calculates the ISO year, week, and day of week from y, m, and d, into iy,
+ * iw, and id */
+void timelib_isodate_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iy, timelib_sll *iw, timelib_sll *id);
+
+/* Calculates the year, month, and day from iy, iw, and iw, into y, m, and d */
+void timelib_date_from_isodate(timelib_sll iy, timelib_sll iw, timelib_sll id, timelib_sll *y, timelib_sll *m, timelib_sll *d);
+
+/* Returns true if h, i and s fit in the range 00:00:00..23:59:59, false
+ * otherwise */
+int timelib_valid_time(timelib_sll h, timelib_sll i, timelib_sll s);
+
+/* Returns true if m fits in the range 1..12, and d fits in the range
+ * 1..<days-in-month> for year y */
+int timelib_valid_date(timelib_sll y, timelib_sll m, timelib_sll d);
+
+/* From parse_date.re */
+
+/* Parses the date/time string in 's' with length 'len' into the constituent
+ * parts of timelib_time*.
+ *
+ * Depending on the contents of the string 's', not all elements might be
+ * filled. You can check whether a specific element has been parsed by
+ * comparing with the TIMELIB_UNSET define.
+ *
+ * If errors occur, this function keeps already parsed elements in the
+ * returned timelib_time* value.
+ *
+ * If the **errors points to a timelib_error_container variable, warnings
+ * and errors will be recorded. You are responsible for freeing the stored
+ * information with timelib_error_container_dtor(). To see whether errors have
+ * occurred, inspect errors->errors_count. To see whether warnings have occurred,
+ * inspect errors->warnings_count.
+ *
+ * The returned timelib_time* value is dynamically allocated and should be
+ * freed with timelib_time_dtor().
+ */
+timelib_time *timelib_strtotime(char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper);
+
+/* Parses the date/time string in 's' with length 'len' into the constituent
+ * parts of timelib_time* according to the format in 'format'.
+ *
+ * Depending on the contents of the string 's', not all elements might be
+ * filled. You can check whether a specific element has been parsed by
+ * comparing with the TIMELIB_UNSET define.
+ *
+ * If errors occur, this function keeps already parsed elements in the
+ * returned timelib_time* value.
+ *
+ * If the **errors points to a timelib_error_container variable, warnings
+ * and errors will be recorded. You are responsible for freeing the stored
+ * information with timelib_error_container_dtor(). To see whether errors have
+ * occurred, inspect errors->errors_count. To see whether warnings have occurred,
+ * inspect errors->warnings_count.
+ *
+ * The returned timelib_time* value is dynamically allocated and should be
+ * freed with timelib_time_dtor().
+ */
+timelib_time *timelib_parse_from_format(char *format, char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper);
+
+/* Parses the date/time string in 's' with length 'len' into the constituent
+ * parts of timelib_time* according to the format in 'format' with format
+ * specifier configuration 'format_config'.
+ *
+ * 'format_map' is an array of pairs, with the first element being the format
+ * specifier as a character and the second element corresponds to the
+ * representation of the specifier from the enum list
+ * 'timelib_format_specifier_code'.
+ *
+ * Note: 'format_map' must be terminated with specifier '\0' to indicate to the
+ * parser that there are no more format specifiers in the list.
+ */
+timelib_time *timelib_parse_from_format_with_map(char *format, char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper, const timelib_format_config* format_config);
+
+/* Fills the gaps in the parsed timelib_time with information from the reference date/time in 'now'
+ *
+ * If any of the 'parsed' y, m, d, h, i or s parameters is unset (TIMELIB_UNSET):
+ * - if microtime (us) is unset, then the us of the parsed time is set to 0.
+ * - else if microtime (us) is unset and 'now'->'us' is set, use it, otherwise use 0.
+ *
+ * For either of the 'parsed' y, m, d, h, i, s, z (utc offset in seconds) or
+ * dst is unset, set it to the corresponding value in 'now' if set, otherwise
+ * set it to 0.
+ *
+ * It duplicates tz_abbr if unset in 'parsed' but set in 'now'.
+ *
+ * It duplicates tz_info if unset in 'parsed', but set in 'now' unless
+ * TIMELIB_NO_CLONE is passed, in which case only the pointer in 'parsed' is
+ * set to 'now'.
+ *
+ * If the option TIMELIB_OVERRIDE_TIME is passed and the parsed date/time has
+ * no time portion, the function will ignore the time aspect in 'now' and
+ * instead fill it with zeros.
+ */
+void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options);
+
+/* Tries to convert a time zone abbreviation, gmtoffset and/or isdst flag
+ * combination to a time zone identifier.
+ *
+ * If 'abbr' is either 'utc' or 'gmt' (case insensitve) then "UTC" is
+ * returned.
+ *
+ * It first uses the data in the timezonemap.h file to find a matching
+ * abbreviation/GMT offset combination. If not found, it uses the data in
+ * fallbackmap.h to match only the GMT offset/isdst flag to try to find a
+ * match. If nothing is found, NULL is returned.
+ *
+ * The returned char* is not duplicated, and should not be freed.
+ */
+char *timelib_timezone_id_from_abbr(const char *abbr, timelib_long gmtoffset, int isdst);
+
+/* Returns an array of known time zone abbreviations
+ *
+ * This file is generated from the time zone database through the
+ * gettzmapping.php scripts, which requires that an up-to-date time zone
+ * database is used with the PHP binary that runs the script.
+ *
+ * Each item in the returned list contains the abbreviation, a flag whether
+ * it's an abbreviation used with DST, the UTC offset in seconds, and the name
+ * of the time zone identifier that this abbreviation belongs to.
+ *
+ * The order for each specific abbreviation is controlled through the
+ * preference list in the gettzmapping.php script. Time zones that match the
+ * pattern ±\d{2,4} are excluded
+ */
+const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void);
+
+/**
+ * DEPRECATED, but still used by PHP.
+ */
+timelib_long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper);
+
+/* From parse_iso_intervals.re */
+
+/**
+ * Parses a subset of an ISO 8601 intervals specification string into its
+ * constituent parts.
+ *
+ * If the **errors points to a timelib_error_container variable, warnings
+ * and errors will be recorded. You are responsible for freeing the stored
+ * information with timelib_error_container_dtor(). To see whether errors have
+ * occurred, inspect errors->errors_count. To see whether warnings have occurred,
+ * inspect errors->warnings_count.
+ */
+void timelib_strtointerval(char *s, size_t len,
+                           timelib_time **begin, timelib_time **end,
+                           timelib_rel_time **period, int *recurrences,
+                           timelib_error_container **errors);
+
+
+/* From tm2unixtime.c */
+
+/**
+ * Uses the y/m/d/h/i/s fields to calculate and store the equivalent timestamp
+ * in the sse field.
+ *
+ * It uses the time zone information associated with 'time' to account for the
+ * right UTC offset and/or DST rules. You can associate time zone information
+ * with the timelib_set_timezone_* functions (see below).
+ *
+ * If the type is 'TIMELIB_ZONETYPE_ID' and there is no associated tzinfo, it
+ * will use the second argument 'tzi' to provide the rules necessary to
+ * calculate the right timestamp.
+ */
+void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi);
+
+/**
+ * Takes the information from the y/m/d/h/i/s fields and makes sure their
+ * values are in the right range.
+ *
+ * If a value under- or overflows it will adjust the larger measure up (or
+ * down). It also takes into account leap days.
+ */
+void timelib_do_normalize(timelib_time *base);
+
+/**
+ * Takes the information from the y/m/d/h/i/s fields of 'rt' and makes sure
+ * their values are in the right range.
+ *
+ * If a value under- or overflows it will adjust the larger measure up (or
+ * down). As this function operates on a *relative date/time*, it also takes
+ * into account leap days and correctly accounts for the difference depending
+ * on the base date/time in 'base'.
+ */
+void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt);
+
+/* From unixtime2tm.c */
+
+/**
+ * Takes the unix timestamp in seconds from 'ts' and populates the y/m/d/h/i/s
+ * fields of 'tm' without taking time zones into account
+ */
+void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts);
+
+/**
+ * Takes the Unix timestamp from 'ts', and calculates the y/m/d/h/i/s fields
+ * according to the time zone information attached to 'tm'.
+ */
+void timelib_unixtime2local(timelib_time *tm, timelib_sll ts);
+
+/**
+ * Takes the Unix timestamp stored in 'tm', and calculates the y/m/d/h/i/s
+ * fields according to the time zone information attached to 'tm'.
+ */
+void timelib_update_from_sse(timelib_time *tm);
+
+/**
+ * Attaches the UTC offset as time zone information to 't'.
+ *
+ * 'utc_offset' is in seconds East of UTC.
+ */
+void timelib_set_timezone_from_offset(timelib_time *t, timelib_sll utc_offset);
+
+/**
+ * Attaches the information from 'abbr_info' as time zone information to 't'.
+ *
+ * The timelib_abbr_info struct contains an abbreviation ('abbr') which string
+ * value is duplicated, as well as a 'utc_offset' and 'dst' flag. It only
+ * supports a 'dst' change over of 1 hour.
+ */
+void timelib_set_timezone_from_abbr(timelib_time *t, timelib_abbr_info abbr_info);
+
+/**
+ * Attaches the time zone information in 'tz' to to 't'.
+ *
+ * It fetches the right UTC offset that is currently stored in the time
+ * stamp field in 't' ('sse'), and assigns that to the 'z' field and 'dst'
+ * field (whether DST is in effect at the time). It also sets the current
+ * abbreviation to the 'tz_addr' field, making sure that if a value was already
+ * set it was freed.
+ *
+ * The time zone information in 'tz' is *not* duplicated into the 't' field so
+ * it should not be freed until all timelib_time* variables have been freed as
+ * well.
+ */
+void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz);
+
+/* From parse_tz.c */
+
+/**
+ * Returns whether the time zone ID 'timezone' is available in the time zone
+ * database as pointed to be 'tzdb'.
+ */
+int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb);
+
+/**
+ * Converts the binary stored time zone information from 'tzdb' for the time
+ * zone 'timeozne' into a structure the library can use for calculations.
+ *
+ * The function can be used on both timelib_builtin_db as well as a time zone
+ * db as opened by timelib_zoneinfo.
+ * The function will return null upon failure, and also set an error code
+ * through 'error_code'. 'error_code' must not be a null pointer. The error
+ * code is one of the TIMELIB_ERROR_* constants as listed above. These error
+ * constants can be converted into a string by timelib_get_error_message.
+ *
+ * This function allocates memory for the new time zone structure, which must
+ * be freed after use. Although it is recommended that a cache of each used
+ * time zone is kept.
+ */
+timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb, int *error_code);
+
+/**
+ * Frees up the resources allocated by 'timelib_parse_tzfile'.
+ */
+void timelib_tzinfo_dtor(timelib_tzinfo *tz);
+
+/**
+ * Deep-clones a timelib_tzinfo structure.
+ *
+ * This allocates resources that need to be freed with 'timelib_tzinfo_dtor'
+ */
+timelib_tzinfo* timelib_tzinfo_clone(timelib_tzinfo *tz);
+
+/**
+ * Returns whether DST is active with time zone 'tz' for the time stamp 'ts'.
+ *
+ * Returns 0 if DST is not active, 1 if DST is active, or -1 if no transitions
+ * were available through 'tz'.
+ */
+int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz);
+
+/**
+ * Returns offset information with time zone 'tz' for the time stamp 'ts'.
+ *
+ * The returned information contains: the offset in seconds East of UTC (in
+ * 'offset'), whether DST is active ('is_dst'), what the current time zone
+ * abbreviation is ('abbr') and the transition time that got to this state (in
+ * 'transition_time');
+ */
+timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz);
+
+/**
+ * Returns the UTC offset currently applicable for the information stored in 't'.
+ *
+ * The value returned is the UTC offset in seconds East.
+ */
+timelib_sll timelib_get_current_offset(timelib_time *t);
+
+/**
+ * Displays debugging information about the time zone information in 'tz'.
+ */
+void timelib_dump_tzinfo(timelib_tzinfo *tz);
+
+/**
+ * Returns a pointer to the built-in time zone database.
+ *
+ * You must *not* free the returned pointer as it is part of the text segment.
+ */
+const timelib_tzdb *timelib_builtin_db(void);
+
+/**
+ * Returns a pointer to the start of an array containing a list of timezone identifiers.
+ *
+ * The amount of entries in the array is returned through the 'count' OUT parameter.
+ *
+ * Each entry contains the time zone ID ('id' field), and the position within the time zone
+ * information ('pos' field). The pos field should not be used.
+ */
+const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(const timelib_tzdb *tzdb, int *count);
+
+/* From parse_zoneinfo.c */
+
+/**
+ * Scans the directory and subdirectories of 'directory' for valid time zone files and builds
+ * a time zone database out of these files.
+ *
+ * Typically, the directory should point to '/usr/share/zoneinfo'.
+ *
+ * Unlike 'timelib_builtin_db', the return value of this function must be freed
+ * with the 'timelib_zoneinfo_dtor' function.
+ */
+timelib_tzdb *timelib_zoneinfo(char *directory);
+
+/**
+ * Frees up the resources as created through 'timelib_zoneinfo'.
+ *
+ * This function must be used to free up all the resources that have been
+ * allocated while calling 'timelib_zoneinfo'.
+ */
+void timelib_zoneinfo_dtor(timelib_tzdb *tzdb);
+
+/* From timelib.c */
+
+/**
+ * Returns a static string containing an error message belonging to a specific
+ * error code.
+ */
+const char *timelib_get_error_message(int error_code);
+
+/**
+ * Allocates resources for the relative time structure.
+ *
+ * Must be freed with 'timelib_rel_time_dtor'.
+ */
+timelib_rel_time* timelib_rel_time_ctor(void);
+
+/**
+ * Frees up the resources as allocated through 'timelib_rel_time_ctor'.
+ */
+void timelib_rel_time_dtor(timelib_rel_time* t);
+
+/**
+ * Creates a new timelib_rel_time resource and copies over the information
+ * from 'tz'.
+ *
+ * Must be freed with 'timelib_rel_time_dtor'.
+ */
+timelib_rel_time* timelib_rel_time_clone(timelib_rel_time *tz);
+
+/**
+ * Allocates resources for the time structure.
+ *
+ * Must be freed with 'timelib_time_dtor'.
+ */
+timelib_time* timelib_time_ctor(void);
+
+/**
+ * Frees up the resources as allocated through 'timelib_time_ctor'.
+ */
+void timelib_time_dtor(timelib_time* t);
+
+/**
+ * Creates a new timelib_time resource and copies over the information
+ * from 'orig'.
+ *
+ * Must be freed with 'timelib_time_dtor'.
+ */
+timelib_time* timelib_time_clone(timelib_time* orig);
+
+/**
+ * Compares two timelib_time structures and returns which one is earlier in
+ * time.
+ *
+ * To decide which comes earlier it uses the 'sse' (Seconds Since Epoch) and
+ * 'us' (microseconds) fields.
+ *
+ * Returns -1 if t1 < t2, 0 if t1 == t2, and -1 if t1 > t2.
+ */
+int timelib_time_compare(timelib_time *t1, timelib_time *t2);
+
+/**
+ * Allocates resources for the time offset structure.
+ *
+ * Must be freed with 'timelib_time_offset_dtor'.
+ */
+timelib_time_offset* timelib_time_offset_ctor(void);
+
+/**
+ * Frees up the resources as allocated through 'timelib_time_offset_ctor'.
+ */
+void timelib_time_offset_dtor(timelib_time_offset* t);
+
+/**
+ * Frees up the resources allocated while converting strings to timelib_time
+ * structures with the timelib_strtotime and timelib_strtointerval functions.
+ */
+void timelib_error_container_dtor(timelib_error_container *errors);
+
+/**
+ * Converts the 'sse' value of 'd' to a timelib_long type.
+ *
+ * If the value fits in the TIMELIB_LONG_MIN and TIMELIB_LONG_MAX range, the
+ * value is cast to (timelib_long) and returned. If *error is not a NULL
+ * pointer, it will be set to 0.
+ *
+ * If the value does *not* fit in the range, the function returns 0 and if
+ * *error is not a NULL pointer, it will be set to 1.
+ *
+ * timelib_long is a 32 bit signed long integer on 32 bit platforms, and a 64
+ * bit signed long long integer on 64 bit platforms. In other words, it makes
+ * sure that the value in 'sse' (which is always a signed long long 64 bit
+ * integer) can be used safely outside of the library.
+ */
+timelib_long timelib_date_to_int(timelib_time *d, int *error);
+
+/**
+ * Displays debugging information about the date/time information stored in 'd'.
+ *
+ * 'options' is a bit field, where:
+ * - 1 controls whether the relative time portion is shown.
+ * - 2 controls whether the zone type is shown.
+ */
+void timelib_dump_date(timelib_time *d, int options);
+
+/**
+ * Displays debugging information about the relative time information stored
+ * in 'd'.
+ */
+void timelib_dump_rel_time(timelib_rel_time *d);
+
+/**
+ * Converts a decimal hour into hour/min/sec components
+ */
+void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec);
+
+/**
+ * Converts hour/min/sec values into a decimal hour
+ */
+void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h);
+
+/* from astro.c */
+
+/**
+ * Converts the Unix Epoch time stamp 'ts' to a Julian Day
+ *
+ * The value returned is the number of whole days since -4714-11-24T12:00:00 UTC
+ * (in the proleptic Gregorian calendar):
+ * https://en.wikipedia.org/wiki/Julian_day
+ */
+double timelib_ts_to_julianday(timelib_sll ts);
+
+/**
+ * Converts the Unix Epoch time stamp 'ts' to the J2000 epoch
+ *
+ * The value returned is the number of whole days since 2000-01-01T12:00:00
+ * UTC: https://en.wikipedia.org/wiki/Epoch_(astronomy)#Julian_years_and_J2000
+ */
+double timelib_ts_to_j2000(timelib_sll ts);
+
+/**
+ * Calculates when the Sun is above a certain latitude.
+ *
+ * Parameters:
+ * - time: A timelib_time time describing that needs to specific midnight for a
+ *         specific day.
+ * - lon: The longitude of the observer (East positive, West negative).
+ * - lat: The latitude of the observer (North positive, South negative).
+ * - altit: The altitude. Set to -35/60 for rise/set, -6 for civil twilight,
+ *          -12 for nautical, and -18 for astronomical twilight.
+ * - upper_limb: set to non-zero for rise/set calculations, and 0 for twilight
+ *               calculations.
+ *
+ * Out Parameters:
+ * - h_rise: The decimal hour when the Sun rises
+ * - h_set: The decimal hour when the Sun sets
+ * - ts_rise: The Unix timestamp of the Sun rising
+ * - ts_set: The Unix timestamp of the Sun setting
+ * - ts_transit: The Unix timestmap of the Sun transitting through South
+ *
+ * Return Values:
+ * - 0: The Sun rises and sets.
+ * - +1: The Sun is always above the horizon. (ts_rise is set to ts_transit -
+ *       (12 * 3600); ts_set is set to ts_transit + (12 * 3600).
+ * - -1: The Sun is awlays below the horizon. (ts_rise and ts_set are set
+ *       to ts_transit)
+ */
+int timelib_astro_rise_set_altitude(timelib_time *time, double lon, double lat, double altit, int upper_limb, double *h_rise, double *h_set, timelib_sll *ts_rise, timelib_sll *ts_set, timelib_sll *ts_transit);
+
+/* from interval.c */
+
+/**
+ * Calculates the difference between two times
+ *
+ * The result is a timelib_rel_time structure that describes how you can
+ * convert from 'one' to 'two' with 'timelib_add'. This does *not* necessarily
+ * mean that you can go from 'two' to 'one' by using 'timelib_sub' due to the
+ * way months and days are calculated.
+ */
+timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two);
+
+/**
+ * Adds the relative time information 'interval' to the base time 't'.
+ *
+ * This can be a relative time as created by 'timelib_diff', but also by more
+ * complex statements such as "next workday".
+ */
+timelib_time *timelib_add(timelib_time *t, timelib_rel_time *interval);
+
+/**
+ * Subtracts the relative time information 'interval' to the base time 't'.
+ *
+ * This can be a relative time as created by 'timelib_diff'. Unlike with
+ * 'timelib_add', this does not support more complex statements such as "next
+ * workday".
+ */
+timelib_time *timelib_sub(timelib_time *t, timelib_rel_time *interval);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif

+ 140 - 0
timelib/timelib_private.h

@@ -0,0 +1,140 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+#ifndef __TIMELIB_PRIVATE_H__
+#define __TIMELIB_PRIVATE_H__
+
+#ifdef HAVE_TIMELIB_CONFIG_H
+# include "timelib_config.h"
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef _WIN32
+# ifdef HAVE_WINSOCK2_H
+#  include <winsock2.h>
+# endif
+#endif
+
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if defined(HAVE_STDINT_H)
+# include <stdint.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_IO_H
+# include <io.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#endif
+
+#include <stdio.h>
+#include <limits.h>
+
+#define TIMELIB_SECOND   1
+#define TIMELIB_MINUTE   2
+#define TIMELIB_HOUR     3
+#define TIMELIB_DAY      4
+#define TIMELIB_MONTH    5
+#define TIMELIB_YEAR     6
+#define TIMELIB_WEEKDAY  7
+#define TIMELIB_SPECIAL  8
+#define TIMELIB_MICROSEC 9
+
+#define TIMELIB_SPECIAL_WEEKDAY                   0x01
+#define TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH      0x02
+#define TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH 0x03
+
+#define TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH        0x01
+#define TIMELIB_SPECIAL_LAST_DAY_OF_MONTH         0x02
+
+#define SECS_PER_ERA   TIMELIB_LL_CONST(12622780800)
+#define SECS_PER_DAY   86400
+#define DAYS_PER_YEAR    365
+#define DAYS_PER_LYEAR   366
+/* 400*365 days + 97 leap days */
+#define DAYS_PER_LYEAR_PERIOD 146097
+#define YEARS_PER_LYEAR_PERIOD 400
+
+#define TIMELIB_TZINFO_PHP       0x01
+#define TIMELIB_TZINFO_ZONEINFO  0x02
+
+#define timelib_is_leap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
+
+#define TIMELIB_DEBUG(s)  if (0) { s }
+
+#define TIMELIB_TIME_FREE(m)    \
+	if (m) {        \
+		timelib_free(m);    \
+		m = NULL;   \
+	}
+
+struct _ttinfo
+{
+	int32_t      offset;
+	int          isdst;
+	unsigned int abbr_idx;
+
+	unsigned int isstdcnt;
+	unsigned int isgmtcnt;
+};
+
+struct _tlinfo
+{
+	int64_t  trans;
+	int32_t  offset;
+};
+
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647L
+#endif
+
+#ifndef LONG_MIN
+#define LONG_MIN (- LONG_MAX - 1)
+#endif
+
+/* From unixtime2tm.c */
+int timelib_apply_localtime(timelib_time *t, unsigned int localtime);
+
+/* From parse_tz.c */
+void timelib_time_tz_abbr_update(timelib_time* tm, char* tz_abbr);
+
+/* From timelib.c */
+int timelib_strcasecmp(const char *s1, const char *s2);
+int timelib_strncasecmp(const char *s1, const char *s2, size_t n);
+
+#endif

File diff suppressed because it is too large
+ 47483 - 0
timelib/timezonedb.h


File diff suppressed because it is too large
+ 1127 - 0
timelib/timezonemap.h


+ 539 - 0
timelib/tm2unixtime.c

@@ -0,0 +1,539 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+
+/*                                    jan  feb  mrt  apr  may  jun  jul  aug  sep  oct  nov  dec */
+static int month_tab_leap[12]     = {  -1,  30,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334 };
+static int month_tab[12]          = {   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+/*                                    dec  jan  feb  mrt  apr  may  jun  jul  aug  sep  oct  nov  dec */
+static int days_in_month_leap[13] = {  31,  31,  29,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
+static int days_in_month[13]      = {  31,  31,  28,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
+
+static void do_range_limit_fraction(timelib_sll *fraction, timelib_sll *seconds)
+{
+	if (*fraction < 0) {
+		*fraction += 1000000;
+		*seconds -= 1;
+	}
+	if (*fraction >= 1000000) {
+		*fraction -= 1000000;
+		*seconds += 1;
+	}
+}
+
+static void do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
+{
+	if (*a < start) {
+		*b -= (start - *a - 1) / adj + 1;
+		*a += adj * ((start - *a - 1) / adj + 1);
+	}
+	if (*a >= end) {
+		*b += *a / adj;
+		*a -= adj * (*a / adj);
+	}
+}
+
+static void inc_month(timelib_sll *y, timelib_sll *m)
+{
+	(*m)++;
+	if (*m > 12) {
+		*m -= 12;
+		(*y)++;
+	}
+}
+
+static void dec_month(timelib_sll *y, timelib_sll *m)
+{
+	(*m)--;
+	if (*m < 1) {
+		*m += 12;
+		(*y)--;
+	}
+}
+
+static void do_range_limit_days_relative(timelib_sll *base_y, timelib_sll *base_m, timelib_sll *y, timelib_sll *m, timelib_sll *d, timelib_sll invert)
+{
+	timelib_sll leapyear;
+	timelib_sll month, year;
+	timelib_sll days;
+
+	do_range_limit(1, 13, 12, base_m, base_y);
+
+	year = *base_y;
+	month = *base_m;
+
+/*
+	printf( "S: Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days);
+*/
+	if (!invert) {
+		while (*d < 0) {
+			dec_month(&year, &month);
+			leapyear = timelib_is_leap(year);
+			days = leapyear ? days_in_month_leap[month] : days_in_month[month];
+
+			/* printf( "I  Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days); */
+
+			*d += days;
+			(*m)--;
+		}
+	} else {
+		while (*d < 0) {
+			leapyear = timelib_is_leap(year);
+			days = leapyear ? days_in_month_leap[month] : days_in_month[month];
+
+			/* printf( "I  Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days); */
+
+			*d += days;
+			(*m)--;
+			inc_month(&year, &month);
+		}
+	}
+	/*
+	printf( "E: Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days);
+	*/
+}
+
+static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d)
+{
+	timelib_sll leapyear;
+	timelib_sll days_this_month;
+	timelib_sll last_month, last_year;
+	timelib_sll days_last_month;
+
+	/* can jump an entire leap year period quickly */
+	if (*d >= DAYS_PER_LYEAR_PERIOD || *d <= -DAYS_PER_LYEAR_PERIOD) {
+		*y += YEARS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
+		*d -= DAYS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
+	}
+
+	do_range_limit(1, 13, 12, m, y);
+
+	leapyear = timelib_is_leap(*y);
+	days_this_month = leapyear ? days_in_month_leap[*m] : days_in_month[*m];
+	last_month = (*m) - 1;
+
+	if (last_month < 1) {
+		last_month += 12;
+		last_year = (*y) - 1;
+	} else {
+		last_year = (*y);
+	}
+	leapyear = timelib_is_leap(last_year);
+	days_last_month = leapyear ? days_in_month_leap[last_month] : days_in_month[last_month];
+
+	if (*d <= 0) {
+		*d += days_last_month;
+		(*m)--;
+		return 1;
+	}
+	if (*d > days_this_month) {
+		*d -= days_this_month;
+		(*m)++;
+		return 1;
+	}
+	return 0;
+}
+
+static void do_adjust_for_weekday(timelib_time* time)
+{
+	timelib_sll current_dow, difference;
+
+	current_dow = timelib_day_of_week(time->y, time->m, time->d);
+	if (time->relative.weekday_behavior == 2)
+	{
+		/* To make "this week" work, where the current DOW is a "sunday" */
+		if (current_dow == 0 && time->relative.weekday != 0) {
+			time->relative.weekday -= 7;
+		}
+
+		/* To make "sunday this week" work, where the current DOW is not a
+		 * "sunday" */
+		if (time->relative.weekday == 0 && current_dow != 0) {
+			time->relative.weekday = 7;
+		}
+
+		time->d -= current_dow;
+		time->d += time->relative.weekday;
+		return;
+	}
+	difference = time->relative.weekday - current_dow;
+	if ((time->relative.d < 0 && difference < 0) || (time->relative.d >= 0 && difference <= -time->relative.weekday_behavior)) {
+		difference += 7;
+	}
+	if (time->relative.weekday >= 0) {
+		time->d += difference;
+	} else {
+		time->d -= (7 - (abs(time->relative.weekday) - current_dow));
+	}
+	time->relative.have_weekday_relative = 0;
+}
+
+void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt)
+{
+	do_range_limit_fraction(&rt->us, &rt->s);
+	do_range_limit(0, 60, 60, &rt->s, &rt->i);
+	do_range_limit(0, 60, 60, &rt->i, &rt->h);
+	do_range_limit(0, 24, 24, &rt->h, &rt->d);
+	do_range_limit(0, 12, 12, &rt->m, &rt->y);
+
+	do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d, rt->invert);
+	do_range_limit(0, 12, 12, &rt->m, &rt->y);
+}
+
+#define EPOCH_DAY 719468
+
+static void magic_date_calc(timelib_time *time)
+{
+	timelib_sll y, ddd, mi, mm, dd, g;
+
+	/* The algorithm doesn't work before the year 1 */
+	if (time->d < -719498) {
+		return;
+	}
+
+	g = time->d + EPOCH_DAY - 1;
+
+	y = (10000 * g + 14780) / 3652425;
+	ddd = g - ((365*y) + (y/4) - (y/100) + (y/400));
+	if (ddd < 0) {
+		y--;
+		ddd = g - ((365*y) + (y/4) - (y/100) + (y/400));
+	}
+	mi = (100 * ddd + 52) / 3060;
+	mm = ((mi + 2) % 12) + 1;
+	y = y + (mi + 2) / 12;
+	dd = ddd - ((mi * 306 + 5) / 10) + 1;
+	time->y = y;
+	time->m = mm;
+	time->d = dd;
+}
+
+void timelib_do_normalize(timelib_time* time)
+{
+	if (time->us != TIMELIB_UNSET) do_range_limit_fraction(&time->us, &time->s);
+	if (time->s != TIMELIB_UNSET) do_range_limit(0, 60, 60, &time->s, &time->i);
+	if (time->s != TIMELIB_UNSET) do_range_limit(0, 60, 60, &time->i, &time->h);
+	if (time->s != TIMELIB_UNSET) do_range_limit(0, 24, 24, &time->h, &time->d);
+	do_range_limit(1, 13, 12, &time->m, &time->y);
+
+	/* Short cut if we're doing things against the Epoch */
+	if (time->y == 1970 && time->m == 1 && time->d != 1) {
+		magic_date_calc(time);
+	}
+
+	do {} while (do_range_limit_days(&time->y, &time->m, &time->d));
+	do_range_limit(1, 13, 12, &time->m, &time->y);
+}
+
+static void do_adjust_relative(timelib_time* time)
+{
+	if (time->relative.have_weekday_relative) {
+		do_adjust_for_weekday(time);
+	}
+	timelib_do_normalize(time);
+
+	if (time->have_relative) {
+		time->us += time->relative.us;
+
+		time->s += time->relative.s;
+		time->i += time->relative.i;
+		time->h += time->relative.h;
+
+		time->d += time->relative.d;
+		time->m += time->relative.m;
+		time->y += time->relative.y;
+	}
+
+	switch (time->relative.first_last_day_of) {
+		case TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH: /* first */
+			time->d = 1;
+			break;
+		case TIMELIB_SPECIAL_LAST_DAY_OF_MONTH: /* last */
+			time->d = 0;
+			time->m++;
+			break;
+	}
+
+	timelib_do_normalize(time);
+}
+
+static void do_adjust_special_weekday(timelib_time* time)
+{
+	timelib_sll count, dow, rem;
+
+	count = time->relative.special.amount;
+	dow = timelib_day_of_week(time->y, time->m, time->d);
+
+	/* Add increments of 5 weekdays as a week, leaving the DOW unchanged. */
+	time->d += (count / 5) * 7;
+
+	/* Deal with the remainder. */
+	rem = (count % 5);
+
+	if (count > 0) {
+		if (rem == 0) {
+			/* Head back to Friday if we stop on the weekend. */
+			if (dow == 0) {
+				time->d -= 2;
+			} else if (dow == 6) {
+				time->d -= 1;
+			}
+		} else if (dow == 6) {
+			/* We ended up on Saturday, but there's still work to do, so move
+			 * to Sunday and continue from there. */
+			time->d += 1;
+		} else if (dow + rem > 5) {
+			/* We're on a weekday, but we're going past Friday, so skip right
+			 * over the weekend. */
+			time->d += 2;
+		}
+	} else {
+		/* Completely mirror the forward direction. This also covers the 0
+		 * case, since if we start on the weekend, we want to move forward as
+		 * if we stopped there while going backwards. */
+		if (rem == 0) {
+			if (dow == 6) {
+				time->d += 2;
+			} else if (dow == 0) {
+				time->d += 1;
+			}
+		} else if (dow == 0) {
+			time->d -= 1;
+		} else if (dow + rem < 1) {
+			time->d -= 2;
+		}
+	}
+
+	time->d += rem;
+}
+
+static void do_adjust_special(timelib_time* time)
+{
+	if (time->relative.have_special_relative) {
+		switch (time->relative.special.type) {
+			case TIMELIB_SPECIAL_WEEKDAY:
+				do_adjust_special_weekday(time);
+				break;
+		}
+	}
+	timelib_do_normalize(time);
+	memset(&(time->relative.special), 0, sizeof(time->relative.special));
+}
+
+static void do_adjust_special_early(timelib_time* time)
+{
+	if (time->relative.have_special_relative) {
+		switch (time->relative.special.type) {
+			case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH:
+				time->d = 1;
+				time->m += time->relative.m;
+				time->relative.m = 0;
+				break;
+			case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH:
+				time->d = 1;
+				time->m += time->relative.m + 1;
+				time->relative.m = 0;
+				break;
+		}
+	}
+	switch (time->relative.first_last_day_of) {
+		case TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH: /* first */
+			time->d = 1;
+			break;
+		case TIMELIB_SPECIAL_LAST_DAY_OF_MONTH: /* last */
+			time->d = 0;
+			time->m++;
+			break;
+	}
+	timelib_do_normalize(time);
+}
+
+static timelib_sll do_years(timelib_sll year)
+{
+	timelib_sll i;
+	timelib_sll res = 0;
+	timelib_sll eras;
+
+	eras = (year - 1970) / 40000;
+	if (eras != 0) {
+		year = year - (eras * 40000);
+		res += (SECS_PER_ERA * eras * 100);
+	}
+
+	if (year >= 1970) {
+		for (i = year - 1; i >= 1970; i--) {
+			if (timelib_is_leap(i)) {
+				res += (DAYS_PER_LYEAR * SECS_PER_DAY);
+			} else {
+				res += (DAYS_PER_YEAR * SECS_PER_DAY);
+			}
+		}
+	} else {
+		for (i = 1969; i >= year; i--) {
+			if (timelib_is_leap(i)) {
+				res -= (DAYS_PER_LYEAR * SECS_PER_DAY);
+			} else {
+				res -= (DAYS_PER_YEAR * SECS_PER_DAY);
+			}
+		}
+	}
+	return res;
+}
+
+static timelib_sll do_months(timelib_ull month, timelib_sll year)
+{
+	if (timelib_is_leap(year)) {
+		return ((month_tab_leap[month - 1] + 1) * SECS_PER_DAY);
+	} else {
+		return ((month_tab[month - 1]) * SECS_PER_DAY);
+	}
+}
+
+static timelib_sll do_days(timelib_ull day)
+{
+	return ((day - 1) * SECS_PER_DAY);
+}
+
+static timelib_sll do_time(timelib_ull hour, timelib_ull minute, timelib_ull second)
+{
+	timelib_sll res = 0;
+
+	res += hour * 3600;
+	res += minute * 60;
+	res += second;
+	return res;
+}
+
+static timelib_sll do_adjust_timezone(timelib_time *tz, timelib_tzinfo *tzi)
+{
+	switch (tz->zone_type) {
+		case TIMELIB_ZONETYPE_OFFSET:
+
+			tz->is_localtime = 1;
+			return -tz->z;
+			break;
+
+		case TIMELIB_ZONETYPE_ABBR: {
+			timelib_sll tmp;
+
+			tz->is_localtime = 1;
+			tmp = -tz->z;
+			tmp -= tz->dst * 3600;
+			return tmp;
+			}
+			break;
+
+		case TIMELIB_ZONETYPE_ID:
+			tzi = tz->tz_info;
+			/* Break intentionally missing */
+
+		default:
+			/* No timezone in struct, fallback to reference if possible */
+			if (tzi) {
+				timelib_time_offset *before, *after;
+				timelib_sll          tmp;
+				int                  in_transition;
+
+				tz->is_localtime = 1;
+				before = timelib_get_time_zone_info(tz->sse, tzi);
+				after = timelib_get_time_zone_info(tz->sse - before->offset, tzi);
+				timelib_set_timezone(tz, tzi);
+
+				in_transition = (
+					((tz->sse - after->offset) >= (after->transition_time + (before->offset - after->offset))) &&
+					((tz->sse - after->offset) < after->transition_time)
+				);
+
+				if ((before->offset != after->offset) && !in_transition) {
+					tmp = -after->offset;
+				} else {
+					tmp = -tz->z;
+				}
+				timelib_time_offset_dtor(before);
+				timelib_time_offset_dtor(after);
+
+				{
+					timelib_time_offset *gmt_offset;
+
+					gmt_offset = timelib_get_time_zone_info(tz->sse + tmp, tzi);
+					tz->z = gmt_offset->offset;
+
+					tz->dst = gmt_offset->is_dst;
+					if (tz->tz_abbr) {
+						timelib_free(tz->tz_abbr);
+					}
+					tz->tz_abbr = timelib_strdup(gmt_offset->abbr);
+					timelib_time_offset_dtor(gmt_offset);
+				}
+				return tmp;
+			}
+	}
+	return 0;
+}
+
+void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi)
+{
+	timelib_sll res = 0;
+
+	do_adjust_special_early(time);
+	do_adjust_relative(time);
+	do_adjust_special(time);
+	res += do_years(time->y);
+	res += do_months(time->m, time->y);
+	res += do_days(time->d);
+	res += do_time(time->h, time->i, time->s);
+	time->sse = res;
+
+	res += do_adjust_timezone(time, tzi);
+	time->sse = res;
+
+	time->sse_uptodate = 1;
+	time->have_relative = time->relative.have_weekday_relative = time->relative.have_special_relative = time->relative.first_last_day_of = 0;
+}
+
+#if 0
+int main(void)
+{
+	timelib_sll res;
+	timelib_time time;
+
+	time = timelib_strtotime("10 Feb 2005 06:07:03 PM CET"); /* 1108055223 */
+	printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
+		time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
+	if (time.have_relative) {
+		printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
+			time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
+	}
+	if (time.have_weekday_relative) {
+		printf (" / %d", time.relative.weekday);
+	}
+	res = time2unixtime(&time);
+	printf("%Ld\n", res);
+
+	return 0;
+}
+#endif

+ 275 - 0
timelib/unixtime2tm.c

@@ -0,0 +1,275 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2019 Derick Rethans
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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 IN
+ * THE SOFTWARE.
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+
+static int month_tab_leap[12] = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+static int month_tab[12] =      { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
+
+/* Converts a Unix timestamp value into broken down time, in GMT */
+void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts)
+{
+	timelib_sll days, remainder, tmp_days;
+	timelib_sll cur_year = 1970;
+	timelib_sll i;
+	timelib_sll hours, minutes, seconds;
+	int *months;
+
+	days = ts / SECS_PER_DAY;
+	remainder = ts - (days * SECS_PER_DAY);
+	if (ts < 0 && remainder == 0) {
+		days++;
+		remainder -= SECS_PER_DAY;
+	}
+	TIMELIB_DEBUG(printf("days=%lld, rem=%lld\n", days, remainder););
+
+	if (ts >= 0) {
+		tmp_days = days + 1;
+	} else {
+		tmp_days = days;
+	}
+
+	if (tmp_days > DAYS_PER_LYEAR_PERIOD || tmp_days <= -DAYS_PER_LYEAR_PERIOD) {
+		cur_year += YEARS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
+		tmp_days -= DAYS_PER_LYEAR_PERIOD * (tmp_days / DAYS_PER_LYEAR_PERIOD);
+	}
+	TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
+
+	if (ts >= 0) {
+		while (tmp_days >= DAYS_PER_LYEAR) {
+			cur_year++;
+			if (timelib_is_leap(cur_year)) {
+				tmp_days -= DAYS_PER_LYEAR;
+			} else {
+				tmp_days -= DAYS_PER_YEAR;
+			}
+			TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
+		}
+	} else {
+		while (tmp_days <= 0) {
+			cur_year--;
+			if (timelib_is_leap(cur_year)) {
+				tmp_days += DAYS_PER_LYEAR;
+			} else {
+				tmp_days += DAYS_PER_YEAR;
+			}
+			TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
+		}
+		remainder += SECS_PER_DAY;
+	}
+	TIMELIB_DEBUG(printf("tmp_days=%lld, year=%lld\n", tmp_days, cur_year););
+
+	months = timelib_is_leap(cur_year) ? month_tab_leap : month_tab;
+	if (timelib_is_leap(cur_year) && cur_year < 1970) {
+		tmp_days--;
+	}
+	i = 11;
+	while (i > 0) {
+		TIMELIB_DEBUG(printf("month=%lld (%d)\n", i, months[i]););
+		if (tmp_days > months[i]) {
+			break;
+		}
+		i--;
+	}
+	TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, cur_year, i + 1, tmp_days - months[i]););
+
+	/* That was the date, now we do the time */
+	hours = remainder / 3600;
+	minutes = (remainder - hours * 3600) / 60;
+	seconds = remainder % 60;
+	TIMELIB_DEBUG(printf(" hour=%lld, minute=%lld, second=%lld\n", hours, minutes, seconds););
+
+	tm->y = cur_year;
+	tm->m = i + 1;
+	tm->d = tmp_days - months[i];
+	tm->h = hours;
+	tm->i = minutes;
+	tm->s = seconds;
+	tm->z = 0;
+	tm->dst = 0;
+	tm->sse = ts;
+	tm->sse_uptodate = 1;
+	tm->tim_uptodate = 1;
+	tm->is_localtime = 0;
+}
+
+void timelib_update_from_sse(timelib_time *tm)
+{
+	timelib_sll sse;
+	int z = tm->z;
+	signed int dst = tm->dst;
+
+	sse = tm->sse;
+
+	switch (tm->zone_type) {
+		case TIMELIB_ZONETYPE_ABBR:
+		case TIMELIB_ZONETYPE_OFFSET: {
+			timelib_unixtime2gmt(tm, tm->sse + tm->z + (tm->dst * 3600));
+
+			goto cleanup;
+		}
+
+		case TIMELIB_ZONETYPE_ID: {
+			timelib_time_offset *gmt_offset;
+
+			gmt_offset = timelib_get_time_zone_info(tm->sse, tm->tz_info);
+			timelib_unixtime2gmt(tm, tm->sse + gmt_offset->offset);
+			timelib_time_offset_dtor(gmt_offset);
+
+			goto cleanup;
+		}
+
+		default:
+			timelib_unixtime2gmt(tm, tm->sse);
+			goto cleanup;
+	}
+cleanup:
+	tm->sse = sse;
+	tm->is_localtime = 1;
+	tm->have_zone = 1;
+	tm->z = z;
+	tm->dst = dst;
+}
+
+void timelib_unixtime2local(timelib_time *tm, timelib_sll ts)
+{
+	timelib_time_offset *gmt_offset;
+	timelib_tzinfo      *tz = tm->tz_info;
+
+	switch (tm->zone_type) {
+		case TIMELIB_ZONETYPE_ABBR:
+		case TIMELIB_ZONETYPE_OFFSET: {
+			int z = tm->z;
+			signed int dst = tm->dst;
+
+			timelib_unixtime2gmt(tm, ts + tm->z + (tm->dst * 3600));
+
+			tm->sse = ts;
+			tm->z = z;
+			tm->dst = dst;
+			break;
+		}
+
+		case TIMELIB_ZONETYPE_ID:
+			gmt_offset = timelib_get_time_zone_info(ts, tz);
+			timelib_unixtime2gmt(tm, ts + gmt_offset->offset);
+
+			/* we need to reset the sse here as unixtime2gmt modifies it */
+			tm->sse = ts;
+			tm->dst = gmt_offset->is_dst;
+			tm->z = gmt_offset->offset;
+			tm->tz_info = tz;
+
+			timelib_time_tz_abbr_update(tm, gmt_offset->abbr);
+			timelib_time_offset_dtor(gmt_offset);
+			break;
+
+		default:
+			tm->is_localtime = 0;
+			tm->have_zone = 0;
+			return;
+	}
+
+	tm->is_localtime = 1;
+	tm->have_zone = 1;
+}
+
+void timelib_set_timezone_from_offset(timelib_time *t, timelib_sll utc_offset)
+{
+	if (t->tz_abbr) {
+		timelib_free(t->tz_abbr);
+	}
+	t->tz_abbr = NULL;
+
+	t->z = utc_offset;
+	t->have_zone = 1;
+	t->zone_type = TIMELIB_ZONETYPE_OFFSET;
+	t->dst = 0;
+	t->tz_info = NULL;
+}
+
+void timelib_set_timezone_from_abbr(timelib_time *t, timelib_abbr_info abbr_info)
+{
+	if (t->tz_abbr) {
+		timelib_free(t->tz_abbr);
+	}
+	t->tz_abbr = timelib_strdup(abbr_info.abbr);
+
+	t->z = abbr_info.utc_offset;
+	t->have_zone = 1;
+	t->zone_type = TIMELIB_ZONETYPE_ABBR;
+	t->dst = abbr_info.dst;
+	t->tz_info = NULL;
+}
+
+void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz)
+{
+	timelib_time_offset *gmt_offset;
+
+	gmt_offset = timelib_get_time_zone_info(t->sse, tz);
+	t->z = gmt_offset->offset;
+/*
+	if (t->dst != gmt_offset->is_dst) {
+		printf("ERROR (%d, %d)\n", t->dst, gmt_offset->is_dst);
+		exit(1);
+	}
+*/
+	t->dst = gmt_offset->is_dst;
+	t->tz_info = tz;
+	if (t->tz_abbr) {
+		timelib_free(t->tz_abbr);
+	}
+	t->tz_abbr = timelib_strdup(gmt_offset->abbr);
+	timelib_time_offset_dtor(gmt_offset);
+
+	t->have_zone = 1;
+	t->zone_type = TIMELIB_ZONETYPE_ID;
+}
+
+/* Converts the time stored in the struct to localtime if localtime = true,
+ * otherwise it converts it to gmttime. This is only done when necessary
+ * of course. */
+int timelib_apply_localtime(timelib_time *t, unsigned int localtime)
+{
+	if (localtime) {
+		/* Converting from GMT time to local time */
+		TIMELIB_DEBUG(printf("Converting from GMT time to local time\n"););
+
+		/* Check if TZ is set */
+		if (!t->tz_info) {
+			TIMELIB_DEBUG(printf("E: No timezone configured, can't switch to local time\n"););
+			return -1;
+		}
+
+		timelib_unixtime2local(t, t->sse);
+	} else {
+		/* Converting from local time to GMT time */
+		TIMELIB_DEBUG(printf("Converting from local time to GMT time\n"););
+
+		timelib_unixtime2gmt(t, t->sse);
+	}
+	return 0;
+}

+ 189 - 0
url/url.c

@@ -0,0 +1,189 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2018 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Jim Winstead <jimw@php.net>                                  |
+   +----------------------------------------------------------------------+
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+char *replace_controlchars_ex(char *str, size_t len)
+{
+	unsigned char *s = (unsigned char *)str;
+	unsigned char *e = (unsigned char *)str + len;
+
+	if (!str) {
+		return (NULL);
+	}
+
+	while (s < e) {
+
+		if (iscntrl(*s)) {
+			*s='_';
+		}
+		s++;
+	}
+
+	return (str);
+}
+
+char *replace_controlchars(char *str)
+{
+	return replace_controlchars_ex(str, strlen(str));
+}
+
+
+static int htoi(char *s)
+{
+	int value;
+	int c;
+
+	c = ((unsigned char *)s)[0];
+	if (isupper(c))
+		c = tolower(c);
+	value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
+
+	c = ((unsigned char *)s)[1];
+	if (isupper(c))
+		c = tolower(c);
+	value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
+
+	return (value);
+}
+
+/* rfc1738:
+
+   ...The characters ";",
+   "/", "?", ":", "@", "=" and "&" are the characters which may be
+   reserved for special meaning within a scheme...
+
+   ...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
+   reserved characters used for their reserved purposes may be used
+   unencoded within a URL...
+
+   For added safety, we only leave -_. unencoded.
+ */
+
+static unsigned char hexchars[] = "0123456789ABCDEF";
+
+void url_encode(char const *s, size_t len, char *out, size_t *out_len)
+{
+	register unsigned char c;
+	unsigned char *to;
+	unsigned char const *from, *end;
+
+	from = (unsigned char *)s;
+	end = (unsigned char *)s + len;
+	to = (unsigned char *)out;
+
+	while (from < end) {
+		c = *from++;
+
+		if (c == ' ') {
+			*to++ = '+';
+		} else if ((c < '0' && c != '-' && c != '.') ||
+				   (c < 'A' && c > '9') ||
+				   (c > 'Z' && c < 'a' && c != '_') ||
+				   (c > 'z')) {
+			to[0] = '%';
+			to[1] = hexchars[c >> 4];
+			to[2] = hexchars[c & 15];
+			to += 3;
+		} else {
+			*to++ = c;
+		}
+	}
+	*to = '\0';
+	*out_len = (char *)to - out;
+}
+
+void url_decode(char *str, size_t len, char *out, size_t *out_len)
+{
+	char *data = str;
+	char *dest = out;
+
+	while (len--) {
+		if (*data == '+') {
+			*dest = ' ';
+		}
+		else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
+				 && isxdigit((int) *(data + 2))) {
+			*dest = (char) htoi(data + 1);
+			data += 2;
+			len -= 2;
+		} else {
+			*dest = *data;
+		}
+		data++;
+		dest++;
+	}
+	*dest = '\0';
+	*out_len = dest - out;
+}
+
+void raw_url_encode(char const *s, size_t len, char *out, size_t *out_len)
+{
+	register size_t x, y;
+	char *ret = out;
+
+	for (x = 0, y = 0; len--; x++, y++) {
+		char c = s[x];
+
+		ret[y] = c;
+		if ((c < '0' && c != '-' &&  c != '.') ||
+			(c < 'A' && c > '9') ||
+			(c > 'Z' && c < 'a' && c != '_') ||
+			(c > 'z' && c != '~')) {
+			ret[y++] = '%';
+			ret[y++] = hexchars[(unsigned char) c >> 4];
+			ret[y] = hexchars[(unsigned char) c & 15];
+		}
+	}
+	ret[y] = '\0';
+	*out_len = ret - out;
+}
+
+void raw_url_decode(char *str, size_t len, char *out, size_t *out_len)
+{
+	char *dest = out;
+	char *data = str;
+
+	while (len--) {
+		if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
+			&& isxdigit((int) *(data + 2))) {
+			*dest = (char) htoi(data + 1);
+			data += 2;
+			len -= 2;
+		} else {
+			*dest = *data;
+		}
+		data++;
+		dest++;
+	}
+	*dest = '\0';
+	*out_len = dest - out;
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */

+ 47 - 0
url/url.h

@@ -0,0 +1,47 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2018 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Jim Winstead <jimw@php.net>                                  |
+   +----------------------------------------------------------------------+
+ */
+
+#ifndef URL_H
+#define URL_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void url_encode(char const *s, size_t len, char *out, size_t *out_len);
+void url_decode(char *str, size_t len, char *out, size_t *out_len); /* return value: length of decoded string */
+void raw_url_encode(char const *s, size_t len, char *out, size_t *out_len);
+void raw_url_decode(char *str, size_t len, char *out, size_t *out_len); /* return value: length of decoded string */
+char *replace_controlchars_ex(char *str, size_t len);
+
+#define PHP_QUERY_RFC1738 1
+#define PHP_QUERY_RFC3986 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* URL_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */