ISSUE ID:- RICAPP-216
[ric-app/bouncer.git] / Bouncer / e2sm_kpm / lib / GeneralizedTime.c
diff --git a/Bouncer/e2sm_kpm/lib/GeneralizedTime.c b/Bouncer/e2sm_kpm/lib/GeneralizedTime.c
new file mode 100644 (file)
index 0000000..f1095a2
--- /dev/null
@@ -0,0 +1,833 @@
+/*-
+ * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
+ * Redistribution and modifications are permitted subject to BSD license.
+ */
+#define        _POSIX_PTHREAD_SEMANTICS        /* for Sun */
+#define        _REENTRANT                      /* for Sun */
+#define __EXTENSIONS__                  /* for Sun */
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE     /* for timegm(3) */
+#endif
+#include <asn_internal.h>
+#include <GeneralizedTime.h>
+
+#ifdef __CYGWIN__
+#include "/usr/include/time.h"
+#else
+#include <time.h>
+#endif /* __CYGWIN__ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#if    defined(_WIN32)
+#pragma message( "PLEASE STOP AND READ!")
+#pragma message( "  localtime_r is implemented via localtime(), which may be not thread-safe.")
+#pragma message( "  gmtime_r is implemented via gmtime(), which may be not thread-safe.")
+#pragma message( "  ")
+#pragma message( "  You must fix the code by inserting appropriate locking")
+#pragma message( "  if you want to use asn_GT2time() or asn_UT2time().")
+#pragma message( "PLEASE STOP AND READ!")
+
+static struct tm *localtime_r(const time_t *tloc, struct tm *result) {
+       struct tm *tm;
+       if((tm = localtime(tloc)))
+               return memcpy(result, tm, sizeof(struct tm));
+       return 0;
+}
+
+static struct tm *gmtime_r(const time_t *tloc, struct tm *result) {
+       struct tm *tm;
+       if((tm = gmtime(tloc)))
+               return memcpy(result, tm, sizeof(struct tm));
+       return 0;
+}
+
+#define        tzset() _tzset()
+#define        putenv(c)       _putenv(c)
+#define        _EMULATE_TIMEGM
+
+#endif /* _WIN32 */
+
+#if    defined(sun) || defined(__sun) || defined(__solaris__)
+#define        _EMULATE_TIMEGM
+#endif
+
+/*
+ * Where to look for offset from GMT, Phase I.
+ * Several platforms are known.
+ */
+#if defined(__FreeBSD__)                               \
+       || (defined(__GNUC__) && defined(__APPLE_CC__)) \
+       || (defined __GLIBC__ && __GLIBC__ >= 2)
+#undef HAVE_TM_GMTOFF
+#define        HAVE_TM_GMTOFF
+#endif /* BSDs and newer glibc */
+
+/*
+ * Where to look for offset from GMT, Phase II.
+ */
+#ifdef HAVE_TM_GMTOFF
+#define        GMTOFF(tm)      ((tm).tm_gmtoff)
+#else  /* HAVE_TM_GMTOFF */
+#define        GMTOFF(tm)      (-timezone)
+#endif /* HAVE_TM_GMTOFF */
+
+#if    defined(_WIN32)
+#pragma message( "PLEASE STOP AND READ!")
+#pragma message( "  timegm() is implemented via getenv(\"TZ\")/setenv(\"TZ\"), which may be not thread-safe.")
+#pragma message( "  ")
+#pragma message( "  You must fix the code by inserting appropriate locking")
+#pragma message( "  if you want to use asn_GT2time() or asn_UT2time().")
+#pragma message( "PLEASE STOP AND READ!")
+#else
+#if    (defined(_EMULATE_TIMEGM) || !defined(HAVE_TM_GMTOFF))
+#warning "PLEASE STOP AND READ!"
+#warning "  timegm() is implemented via getenv(\"TZ\")/setenv(\"TZ\"), which may be not thread-safe."
+#warning "  "
+#warning "  You must fix the code by inserting appropriate locking"
+#warning "  if you want to use asn_GT2time() or asn_UT2time()."
+#warning "PLEASE STOP AND READ!"
+#endif /* _EMULATE_TIMEGM */
+#endif
+
+/*
+ * Override our GMTOFF decision for other known platforms.
+ */
+#ifdef __CYGWIN__
+#undef GMTOFF
+static long GMTOFF(struct tm a){
+       struct tm *lt;
+       time_t local_time, gmt_time;
+       long zone;
+
+       tzset();
+       gmt_time = time (NULL);
+
+       lt = gmtime(&gmt_time);
+
+       local_time = mktime(lt);
+       return (gmt_time - local_time);
+}
+#define        _EMULATE_TIMEGM
+
+#endif /* __CYGWIN__ */
+
+#define        ATZVARS do {                                                    \
+       char tzoldbuf[64];                                              \
+       char *tzold
+#define        ATZSAVETZ do {                                                  \
+       tzold = getenv("TZ");                                           \
+       if(tzold) {                                                     \
+               size_t tzlen = strlen(tzold);                           \
+               if(tzlen < sizeof(tzoldbuf)) {                          \
+                       tzold = memcpy(tzoldbuf, tzold, tzlen + 1);     \
+               } else {                                                \
+                       char *dupptr = tzold;                           \
+                       tzold = MALLOC(tzlen + 1);                      \
+                       if(tzold) memcpy(tzold, dupptr, tzlen + 1);     \
+               }                                                       \
+               setenv("TZ", "UTC", 1);                                 \
+       }                                                               \
+       tzset();                                                        \
+} while(0)
+#define        ATZOLDTZ do {                                                   \
+       if (tzold) {                                                    \
+               setenv("TZ", tzold, 1);                                 \
+               *tzoldbuf = 0;                                          \
+               if(tzold != tzoldbuf)                                   \
+                       FREEMEM(tzold);                                 \
+       } else {                                                        \
+               unsetenv("TZ");                                         \
+       }                                                               \
+       tzset();                                                        \
+} while(0); } while(0);
+
+#ifndef HAVE_TIMEGM
+#ifdef _EMULATE_TIMEGM
+#include <stdlib.h>
+static time_t timegm(struct tm *tm) {
+       time_t tloc;
+       ATZVARS;
+       ATZSAVETZ;
+       tloc = mktime(tm);
+       ATZOLDTZ;
+       return tloc;
+}
+#endif /* _EMULATE_TIMEGM */
+#endif
+
+
+#ifndef        ASN___INTERNAL_TEST_MODE
+
+/*
+ * GeneralizedTime basic type description.
+ */
+static const ber_tlv_tag_t asn_DEF_GeneralizedTime_tags[] = {
+       (ASN_TAG_CLASS_UNIVERSAL | (24 << 2)),  /* [UNIVERSAL 24] IMPLICIT ...*/
+       (ASN_TAG_CLASS_UNIVERSAL | (26 << 2)),  /* [UNIVERSAL 26] IMPLICIT ...*/
+       (ASN_TAG_CLASS_UNIVERSAL | (4 << 2))    /* ... OCTET STRING */
+};
+static asn_per_constraints_t asn_DEF_GeneralizedTime_per_constraints = {
+       { APC_CONSTRAINED, 7, 7, 0x20, 0x7e },  /* Value */
+       { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, /* Size */
+       0, 0
+};
+asn_TYPE_operation_t asn_OP_GeneralizedTime = {
+       OCTET_STRING_free,
+       GeneralizedTime_print,
+       GeneralizedTime_compare,
+       OCTET_STRING_decode_ber,    /* Implemented in terms of OCTET STRING */
+       GeneralizedTime_encode_der,
+       OCTET_STRING_decode_xer_utf8,
+       GeneralizedTime_encode_xer,
+#ifdef ASN_DISABLE_OER_SUPPORT
+       0,
+       0,
+#else
+       OCTET_STRING_decode_oer,
+       OCTET_STRING_encode_oer,
+#endif  /* ASN_DISABLE_OER_SUPPORT */
+#ifdef ASN_DISABLE_PER_SUPPORT
+       0,
+       0,
+       0,
+       0,
+#else
+       OCTET_STRING_decode_uper,
+       OCTET_STRING_encode_uper,
+       OCTET_STRING_decode_aper,
+       OCTET_STRING_encode_aper,
+#endif /* ASN_DISABLE_PER_SUPPORT */
+       GeneralizedTime_random_fill,
+       0       /* Use generic outmost tag fetcher */
+};
+asn_TYPE_descriptor_t asn_DEF_GeneralizedTime = {
+       "GeneralizedTime",
+       "GeneralizedTime",
+       &asn_OP_GeneralizedTime,
+       asn_DEF_GeneralizedTime_tags,
+       sizeof(asn_DEF_GeneralizedTime_tags)
+         / sizeof(asn_DEF_GeneralizedTime_tags[0]) - 2,
+       asn_DEF_GeneralizedTime_tags,
+       sizeof(asn_DEF_GeneralizedTime_tags)
+         / sizeof(asn_DEF_GeneralizedTime_tags[0]),
+       { 0, &asn_DEF_GeneralizedTime_per_constraints, GeneralizedTime_constraint },
+       0, 0,   /* No members */
+       0       /* No specifics */
+};
+
+#endif /* ASN___INTERNAL_TEST_MODE */
+
+/*
+ * Check that the time looks like the time.
+ */
+int
+GeneralizedTime_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
+                           asn_app_constraint_failed_f *ctfailcb,
+                           void *app_key) {
+    const GeneralizedTime_t *st = (const GeneralizedTime_t *)sptr;
+       time_t tloc;
+
+       errno = EPERM;                  /* Just an unlikely error code */
+       tloc = asn_GT2time(st, 0, 0);
+       if(tloc == -1 && errno != EPERM) {
+               ASN__CTFAIL(app_key, td, sptr,
+                       "%s: Invalid time format: %s (%s:%d)",
+                       td->name, strerror(errno), __FILE__, __LINE__);
+               return -1;
+       }
+
+       return 0;
+}
+
+asn_enc_rval_t
+GeneralizedTime_encode_der(const asn_TYPE_descriptor_t *td, const void *sptr,
+                           int tag_mode, ber_tlv_tag_t tag,
+                           asn_app_consume_bytes_f *cb, void *app_key) {
+    GeneralizedTime_t *st;
+       asn_enc_rval_t erval = {0,0,0};
+       int fv, fd;     /* seconds fraction value and number of digits */
+       struct tm tm;
+       time_t tloc;
+
+       /*
+        * Encode as a canonical DER.
+        */
+    errno = EPERM;
+    tloc = asn_GT2time_frac((const GeneralizedTime_t *)sptr, &fv, &fd, &tm,
+                            1); /* Recognize time */
+    if(tloc == -1 && errno != EPERM) {
+        /* Failed to recognize time. Fail completely. */
+               ASN__ENCODE_FAILED;
+    }
+
+    st = asn_time2GT_frac(0, &tm, fv, fd, 1); /* Save time canonically */
+    if(!st) ASN__ENCODE_FAILED;               /* Memory allocation failure. */
+
+    erval = OCTET_STRING_encode_der(td, st, tag_mode, tag, cb, app_key);
+
+    ASN_STRUCT_FREE(*td, st);
+
+    return erval;
+}
+
+#ifndef        ASN___INTERNAL_TEST_MODE
+
+asn_enc_rval_t
+GeneralizedTime_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr,
+                           int ilevel, enum xer_encoder_flags_e flags,
+                           asn_app_consume_bytes_f *cb, void *app_key) {
+    if(flags & XER_F_CANONICAL) {
+               GeneralizedTime_t *gt;
+               asn_enc_rval_t rv;
+               int fv, fd;             /* fractional parts */
+               struct tm tm;
+
+               errno = EPERM;
+               if(asn_GT2time_frac((const GeneralizedTime_t *)sptr,
+                                       &fv, &fd, &tm, 1) == -1
+                               && errno != EPERM)
+                       ASN__ENCODE_FAILED;
+
+               gt = asn_time2GT_frac(0, &tm, fv, fd, 1);
+               if(!gt) ASN__ENCODE_FAILED;
+       
+               rv = OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
+                       cb, app_key);
+               ASN_STRUCT_FREE(asn_DEF_GeneralizedTime, gt);
+               return rv;
+       } else {
+               return OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
+                       cb, app_key);
+       }
+}
+
+#endif /* ASN___INTERNAL_TEST_MODE */
+
+int
+GeneralizedTime_print(const asn_TYPE_descriptor_t *td, const void *sptr,
+                      int ilevel, asn_app_consume_bytes_f *cb, void *app_key) {
+    const GeneralizedTime_t *st = (const GeneralizedTime_t *)sptr;
+
+       (void)td;       /* Unused argument */
+       (void)ilevel;   /* Unused argument */
+
+       if(st && st->buf) {
+               char buf[32];
+               struct tm tm;
+               int ret;
+
+               errno = EPERM;
+               if(asn_GT2time(st, &tm, 1) == -1 && errno != EPERM)
+                       return (cb("<bad-value>", 11, app_key) < 0) ? -1 : 0;
+
+               ret = snprintf(buf, sizeof(buf),
+                       "%04d-%02d-%02d %02d:%02d:%02d (GMT)",
+                       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+                       tm.tm_hour, tm.tm_min, tm.tm_sec);
+               assert(ret > 0 && ret < (int)sizeof(buf));
+               return (cb(buf, ret, app_key) < 0) ? -1 : 0;
+       } else {
+               return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
+       }
+}
+
+time_t
+asn_GT2time(const GeneralizedTime_t *st, struct tm *ret_tm, int as_gmt) {
+       return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
+}
+
+time_t
+asn_GT2time_prec(const GeneralizedTime_t *st, int *frac_value, int frac_digits, struct tm *ret_tm, int as_gmt) {
+       time_t tloc;
+       int fv, fd = 0;
+
+       if(frac_value)
+               tloc = asn_GT2time_frac(st, &fv, &fd, ret_tm, as_gmt);
+       else
+               return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
+       if(fd == 0 || frac_digits <= 0) {
+               *frac_value = 0;
+       } else {
+               while(fd > frac_digits)
+                       fv /= 10, fd--;
+               while(fd < frac_digits) {
+                       if(fv < INT_MAX / 10) {
+                               fv *= 10;
+                               fd++;
+                       } else {
+                               /* Too long precision request */
+                               fv = 0;
+                               break;
+                       }
+               }
+
+               *frac_value = fv;
+       }
+
+       return tloc;
+}
+
+time_t
+asn_GT2time_frac(const GeneralizedTime_t *st, int *frac_value, int *frac_digits, struct tm *ret_tm, int as_gmt) {
+       struct tm tm_s;
+       uint8_t *buf;
+       uint8_t *end;
+       int gmtoff_h = 0;
+       int gmtoff_m = 0;
+       int gmtoff = 0; /* h + m */
+       int offset_specified = 0;
+       int fvalue = 0;
+       int fdigits = 0;
+       time_t tloc;
+
+       if(!st || !st->buf) {
+               errno = EINVAL;
+               return -1;
+       } else {
+               buf = st->buf;
+               end = buf + st->size;
+       }
+
+       if(st->size < 10) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /*
+        * Decode first 10 bytes: "AAAAMMJJhh"
+        */
+       memset(&tm_s, 0, sizeof(tm_s));
+#undef B2F
+#undef B2T
+#define        B2F(var)        do {                                    \
+               unsigned ch = *buf;                             \
+               if(ch < 0x30 || ch > 0x39) {                    \
+                       errno = EINVAL;                         \
+                       return -1;                              \
+               } else {                                        \
+                       var = var * 10 + (ch - 0x30);           \
+                       buf++;                                  \
+               }                                               \
+       } while(0)
+#define        B2T(var)        B2F(tm_s.var)
+
+       B2T(tm_year);   /* 1: A */
+       B2T(tm_year);   /* 2: A */
+       B2T(tm_year);   /* 3: A */
+       B2T(tm_year);   /* 4: A */
+       B2T(tm_mon);    /* 5: M */
+       B2T(tm_mon);    /* 6: M */
+       B2T(tm_mday);   /* 7: J */
+       B2T(tm_mday);   /* 8: J */
+       B2T(tm_hour);   /* 9: h */
+       B2T(tm_hour);   /* 0: h */
+
+       if(buf == end) goto local_finish;
+
+       /*
+        * Parse [mm[ss[(.|,)ffff]]]
+        *        ^^
+        */
+       switch(*buf) {
+       case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+       case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+               tm_s.tm_min = (*buf++) - 0x30;
+               if(buf == end) { errno = EINVAL; return -1; }
+               B2T(tm_min);
+               break;
+       case 0x2B: case 0x2D:   /* +, - */
+               goto offset;
+       case 0x5A:              /* Z */
+               goto utc_finish;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+
+       if(buf == end) goto local_finish;
+
+       /*
+        * Parse [mm[ss[(.|,)ffff]]]
+        *           ^^
+        */
+       switch(*buf) {
+       case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+       case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+               tm_s.tm_sec = (*buf++) - 0x30;
+               if(buf == end) { errno = EINVAL; return -1; }
+               B2T(tm_sec);
+               break;
+       case 0x2B: case 0x2D:   /* +, - */
+               goto offset;
+       case 0x5A:              /* Z */
+               goto utc_finish;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+
+       if(buf == end) goto local_finish;
+
+       /*
+        * Parse [mm[ss[(.|,)ffff]]]
+        *               ^ ^
+        */
+       switch(*buf) {
+       case 0x2C: case 0x2E: /* (.|,) */
+               /*
+                * Process fractions of seconds.
+                */
+               for(buf++; buf < end; buf++) {
+                       int v = *buf;
+                       /* GCC 4.x is being too smart without volatile */
+                       switch(v) {
+                       case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
+                       case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
+                               if(fvalue < INT_MAX/10) {
+                                       fvalue = fvalue * 10 + (v - 0x30);
+                                       fdigits++;
+                               } else {
+                                       /* Not enough precision, ignore */
+                               }
+                               continue;
+                       default:
+                               break;
+                       }
+                       break;
+               }
+       }
+
+       if(buf == end) goto local_finish;
+
+       switch(*buf) {
+       case 0x2B: case 0x2D:   /* +, - */
+               goto offset;
+       case 0x5A:              /* Z */
+               goto utc_finish;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+
+
+offset:
+
+       if(end - buf < 3) {
+               errno = EINVAL;
+               return -1;
+       }
+       buf++;
+       B2F(gmtoff_h);
+       B2F(gmtoff_h);
+       if(buf[-3] == 0x2D)     /* Negative */
+               gmtoff = -1;
+       else
+               gmtoff = 1;
+
+       if((end - buf) == 2) {
+               B2F(gmtoff_m);
+               B2F(gmtoff_m);
+       } else if(end != buf) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       gmtoff = gmtoff * (3600 * gmtoff_h + 60 * gmtoff_m);
+
+       /* Fall through */
+utc_finish:
+
+       offset_specified = 1;
+
+       /* Fall through */
+local_finish:
+
+       /*
+        * Validation.
+        */
+       if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1)
+       || (tm_s.tm_mday > 31 || tm_s.tm_mday < 1)
+       || (tm_s.tm_hour > 23)
+       || (tm_s.tm_sec > 60)
+       ) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* Canonicalize */
+       tm_s.tm_mon -= 1;       /* 0 - 11 */
+       tm_s.tm_year -= 1900;
+       tm_s.tm_isdst = -1;
+
+       tm_s.tm_sec -= gmtoff;
+
+       /*** AT THIS POINT tm_s is either GMT or local (unknown) ****/
+
+       if(offset_specified) {
+               tloc = timegm(&tm_s);
+       } else {
+               /*
+                * Without an offset (or "Z"),
+                * we can only guess that it is a local zone.
+                * Interpret it in this fashion.
+                */
+               tloc = mktime(&tm_s);
+       }
+       if(tloc == -1) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if(ret_tm) {
+               if(as_gmt) {
+                       if(offset_specified) {
+                               *ret_tm = tm_s;
+                       } else {
+                               if(gmtime_r(&tloc, ret_tm) == 0) {
+                                       errno = EINVAL;
+                                       return -1;
+                               }
+                       }
+               } else {
+                       if(localtime_r(&tloc, ret_tm) == 0) {
+                               errno = EINVAL;
+                               return -1;
+                       }
+               }
+       }
+
+       /* Fractions of seconds */
+       if(frac_value) *frac_value = fvalue;
+       if(frac_digits) *frac_digits = fdigits;
+
+       return tloc;
+}
+
+GeneralizedTime_t *
+asn_time2GT(GeneralizedTime_t *opt_gt, const struct tm *tm, int force_gmt) {
+       return asn_time2GT_frac(opt_gt, tm, 0, 0, force_gmt);
+}
+
+GeneralizedTime_t *
+asn_time2GT_frac(GeneralizedTime_t *opt_gt, const struct tm *tm, int frac_value, int frac_digits, int force_gmt) {
+       struct tm tm_s;
+       long gmtoff = 0;
+       const unsigned int buf_size =
+               4 + 2 + 2       /* yyyymmdd */
+               + 2 + 2 + 2     /* hhmmss */
+               + 1 + 9         /* .fffffffff */
+               + 1 + 4         /* +hhmm */
+               + 1             /* '\0' */
+               ;
+       char *buf = NULL;
+       char *p = NULL;
+       int size = 0;
+
+       /* Check arguments */
+       if(!tm) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* Pre-allocate a buffer of sufficient yet small length */
+       buf = (char *)MALLOC(buf_size);
+       if(!buf) return 0;
+
+       gmtoff = GMTOFF(*tm);
+
+       if(force_gmt && gmtoff) {
+               tm_s = *tm;
+               tm_s.tm_sec -= gmtoff;
+               timegm(&tm_s);  /* Fix the time */
+               tm = &tm_s;
+#ifdef HAVE_TM_GMTOFF
+               assert(!GMTOFF(tm_s));  /* Will fix itself */
+#else  /* !HAVE_TM_GMTOFF */
+               gmtoff = 0;
+#endif
+       }
+
+       size = snprintf(buf, buf_size, "%04d%02d%02d%02d%02d%02d",
+               tm->tm_year + 1900,
+               tm->tm_mon + 1,
+               tm->tm_mday,
+               tm->tm_hour,
+               tm->tm_min,
+               tm->tm_sec
+       );
+       if(size != 14) {
+               /* Could be assert(size == 14); */
+               FREEMEM(buf);
+               errno = EINVAL;
+               return 0;
+       }
+
+       p = buf + size;
+
+       /*
+        * Deal with fractions.
+        */
+       if(frac_value > 0 && frac_digits > 0) {
+               char *end = p + 1 + 9;  /* '.' + maximum 9 digits */
+               char *z = p;
+               long fbase;
+               *z++ = '.';
+
+               /* Place bounds on precision */
+               while(frac_digits-- > 9)
+                       frac_value /= 10;
+
+               /* emulate fbase = pow(10, frac_digits) */
+               for(fbase = 1; frac_digits--;)
+                       fbase *= 10;
+
+               do {
+                       int digit = frac_value / fbase;
+                       if(digit > 9) { z = 0; break; }
+                       *z++ = digit + 0x30;
+                       frac_value %= fbase;
+                       fbase /= 10;
+               } while(fbase > 0 && frac_value > 0 && z < end);
+               if(z) {
+                       for(--z; *z == 0x30; --z);      /* Strip zeroes */
+                       p = z + (*z != '.');
+                       size = p - buf;
+               }
+       }
+
+       if(force_gmt) {
+               *p++ = 0x5a;    /* "Z" */
+               *p++ = 0;
+               size++;
+       } else {
+               int ret;
+               gmtoff %= 86400;
+               ret = snprintf(p, buf_size - size, "%+03ld%02ld",
+                       gmtoff / 3600, labs(gmtoff % 3600) / 60);
+               if(ret != 5) {
+                       FREEMEM(buf);
+                       errno = EINVAL;
+                       return 0;
+               }
+               size += ret;
+       }
+
+       if(opt_gt) {
+               if(opt_gt->buf)
+                       FREEMEM(opt_gt->buf);
+       } else {
+               opt_gt = (GeneralizedTime_t *)CALLOC(1, sizeof *opt_gt);
+               if(!opt_gt) { FREEMEM(buf); return 0; }
+       }
+
+       opt_gt->buf = (unsigned char *)buf;
+       opt_gt->size = size;
+
+       return opt_gt;
+}
+
+asn_random_fill_result_t
+GeneralizedTime_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
+                              const asn_encoding_constraints_t *constraints,
+                              size_t max_length) {
+    asn_random_fill_result_t result_ok = {ARFILL_OK, 1};
+    asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
+    asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
+    static const char *values[] = {
+        "19700101000000",    "19700101000000-0000",   "19700101000000+0000",
+        "19700101000000Z",   "19700101000000.3Z",     "19821106210623.3",
+        "19821106210629.3Z", "19691106210827.3-0500", "19821106210629.456",
+    };
+    size_t rnd = asn_random_between(0, sizeof(values)/sizeof(values[0])-1);
+
+    (void)constraints;
+
+    if(max_length < sizeof("yyyymmddhhmmss") && !*sptr) {
+        return result_skipped;
+    }
+
+    if(*sptr) {
+        if(OCTET_STRING_fromBuf(*sptr, values[rnd], -1) != 0) {
+            if(!sptr) return result_failed;
+        }
+    } else {
+        *sptr = OCTET_STRING_new_fromBuf(td, values[rnd], -1);
+        if(!sptr) return result_failed;
+    }
+
+    return result_ok;
+}
+
+int
+GeneralizedTime_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
+                        const void *bptr) {
+    const GeneralizedTime_t *a = aptr;
+    const GeneralizedTime_t *b = bptr;
+
+    (void)td;
+
+    if(a && b) {
+        int afrac_value, afrac_digits;
+        int bfrac_value, bfrac_digits;
+        int aerr, berr;
+        time_t at, bt;
+
+        errno = EPERM;
+        at = asn_GT2time_frac(a, &afrac_value, &afrac_digits, 0, 0);
+        aerr = errno;
+        errno = EPERM;
+        bt = asn_GT2time_frac(b, &bfrac_value, &bfrac_digits, 0, 0);
+        berr = errno;
+
+        if(at == -1 && aerr != EPERM) {
+            if(bt == -1 && berr != EPERM) {
+                return OCTET_STRING_compare(td, aptr, bptr);
+            } else {
+                return -1;
+            }
+        } else if(bt == -1 && berr != EPERM) {
+            return 1;
+        } else {
+            /* Both values are valid. */
+        }
+
+        if(at < bt) {
+            return -1;
+        } else if(at > bt) {
+            return 1;
+        } else if(afrac_digits == bfrac_digits) {
+            if(afrac_value == bfrac_value) {
+                return 0;
+            }
+            if(afrac_value < bfrac_value) {
+                return -1;
+            } else {
+                return 1;
+            }
+        } else if(afrac_digits == 0) {
+            return -1;
+        } else if(bfrac_digits == 0) {
+            return 1;
+        } else {
+            double afrac = (double)afrac_value / afrac_digits;
+            double bfrac = (double)bfrac_value / bfrac_digits;
+            if(afrac < bfrac) {
+                return -1;
+            } else if(afrac > bfrac) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    } else if(!a && !b) {
+        return 0;
+    } else if(!a) {
+        return -1;
+    } else {
+        return 1;
+    }
+
+}
+