ISSUE ID:- RICAPP-216
[ric-app/bouncer.git] / Bouncer / e2sm_kpm / lib / GeneralizedTime.c
1 /*-
2  * Copyright (c) 2003-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
3  * Redistribution and modifications are permitted subject to BSD license.
4  */
5 #define _POSIX_PTHREAD_SEMANTICS        /* for Sun */
6 #define _REENTRANT                      /* for Sun */
7 #define __EXTENSIONS__                  /* for Sun */
8 #ifndef _BSD_SOURCE
9 #define _BSD_SOURCE     /* for timegm(3) */
10 #endif
11 #include <asn_internal.h>
12 #include <GeneralizedTime.h>
13
14 #ifdef  __CYGWIN__
15 #include "/usr/include/time.h"
16 #else
17 #include <time.h>
18 #endif  /* __CYGWIN__ */
19
20 #include <stdio.h>
21 #include <errno.h>
22
23 #if     defined(_WIN32)
24 #pragma message( "PLEASE STOP AND READ!")
25 #pragma message( "  localtime_r is implemented via localtime(), which may be not thread-safe.")
26 #pragma message( "  gmtime_r is implemented via gmtime(), which may be not thread-safe.")
27 #pragma message( "  ")
28 #pragma message( "  You must fix the code by inserting appropriate locking")
29 #pragma message( "  if you want to use asn_GT2time() or asn_UT2time().")
30 #pragma message( "PLEASE STOP AND READ!")
31
32 static struct tm *localtime_r(const time_t *tloc, struct tm *result) {
33         struct tm *tm;
34         if((tm = localtime(tloc)))
35                 return memcpy(result, tm, sizeof(struct tm));
36         return 0;
37 }
38
39 static struct tm *gmtime_r(const time_t *tloc, struct tm *result) {
40         struct tm *tm;
41         if((tm = gmtime(tloc)))
42                 return memcpy(result, tm, sizeof(struct tm));
43         return 0;
44 }
45
46 #define tzset() _tzset()
47 #define putenv(c)       _putenv(c)
48 #define _EMULATE_TIMEGM
49
50 #endif  /* _WIN32 */
51
52 #if     defined(sun) || defined(__sun) || defined(__solaris__)
53 #define _EMULATE_TIMEGM
54 #endif
55
56 /*
57  * Where to look for offset from GMT, Phase I.
58  * Several platforms are known.
59  */
60 #if defined(__FreeBSD__)                                \
61         || (defined(__GNUC__) && defined(__APPLE_CC__)) \
62         || (defined __GLIBC__ && __GLIBC__ >= 2)
63 #undef  HAVE_TM_GMTOFF
64 #define HAVE_TM_GMTOFF
65 #endif  /* BSDs and newer glibc */
66
67 /*
68  * Where to look for offset from GMT, Phase II.
69  */
70 #ifdef  HAVE_TM_GMTOFF
71 #define GMTOFF(tm)      ((tm).tm_gmtoff)
72 #else   /* HAVE_TM_GMTOFF */
73 #define GMTOFF(tm)      (-timezone)
74 #endif  /* HAVE_TM_GMTOFF */
75
76 #if     defined(_WIN32)
77 #pragma message( "PLEASE STOP AND READ!")
78 #pragma message( "  timegm() is implemented via getenv(\"TZ\")/setenv(\"TZ\"), which may be not thread-safe.")
79 #pragma message( "  ")
80 #pragma message( "  You must fix the code by inserting appropriate locking")
81 #pragma message( "  if you want to use asn_GT2time() or asn_UT2time().")
82 #pragma message( "PLEASE STOP AND READ!")
83 #else
84 #if     (defined(_EMULATE_TIMEGM) || !defined(HAVE_TM_GMTOFF))
85 #warning "PLEASE STOP AND READ!"
86 #warning "  timegm() is implemented via getenv(\"TZ\")/setenv(\"TZ\"), which may be not thread-safe."
87 #warning "  "
88 #warning "  You must fix the code by inserting appropriate locking"
89 #warning "  if you want to use asn_GT2time() or asn_UT2time()."
90 #warning "PLEASE STOP AND READ!"
91 #endif  /* _EMULATE_TIMEGM */
92 #endif
93
94 /*
95  * Override our GMTOFF decision for other known platforms.
96  */
97 #ifdef __CYGWIN__
98 #undef  GMTOFF
99 static long GMTOFF(struct tm a){
100         struct tm *lt;
101         time_t local_time, gmt_time;
102         long zone;
103
104         tzset();
105         gmt_time = time (NULL);
106
107         lt = gmtime(&gmt_time);
108
109         local_time = mktime(lt);
110         return (gmt_time - local_time);
111 }
112 #define _EMULATE_TIMEGM
113
114 #endif  /* __CYGWIN__ */
115
116 #define ATZVARS do {                                                    \
117         char tzoldbuf[64];                                              \
118         char *tzold
119 #define ATZSAVETZ do {                                                  \
120         tzold = getenv("TZ");                                           \
121         if(tzold) {                                                     \
122                 size_t tzlen = strlen(tzold);                           \
123                 if(tzlen < sizeof(tzoldbuf)) {                          \
124                         tzold = memcpy(tzoldbuf, tzold, tzlen + 1);     \
125                 } else {                                                \
126                         char *dupptr = tzold;                           \
127                         tzold = MALLOC(tzlen + 1);                      \
128                         if(tzold) memcpy(tzold, dupptr, tzlen + 1);     \
129                 }                                                       \
130                 setenv("TZ", "UTC", 1);                                 \
131         }                                                               \
132         tzset();                                                        \
133 } while(0)
134 #define ATZOLDTZ do {                                                   \
135         if (tzold) {                                                    \
136                 setenv("TZ", tzold, 1);                                 \
137                 *tzoldbuf = 0;                                          \
138                 if(tzold != tzoldbuf)                                   \
139                         FREEMEM(tzold);                                 \
140         } else {                                                        \
141                 unsetenv("TZ");                                         \
142         }                                                               \
143         tzset();                                                        \
144 } while(0); } while(0);
145
146 #ifndef HAVE_TIMEGM
147 #ifdef  _EMULATE_TIMEGM
148 #include <stdlib.h>
149 static time_t timegm(struct tm *tm) {
150         time_t tloc;
151         ATZVARS;
152         ATZSAVETZ;
153         tloc = mktime(tm);
154         ATZOLDTZ;
155         return tloc;
156 }
157 #endif  /* _EMULATE_TIMEGM */
158 #endif
159
160
161 #ifndef ASN___INTERNAL_TEST_MODE
162
163 /*
164  * GeneralizedTime basic type description.
165  */
166 static const ber_tlv_tag_t asn_DEF_GeneralizedTime_tags[] = {
167         (ASN_TAG_CLASS_UNIVERSAL | (24 << 2)),  /* [UNIVERSAL 24] IMPLICIT ...*/
168         (ASN_TAG_CLASS_UNIVERSAL | (26 << 2)),  /* [UNIVERSAL 26] IMPLICIT ...*/
169         (ASN_TAG_CLASS_UNIVERSAL | (4 << 2))    /* ... OCTET STRING */
170 };
171 static asn_per_constraints_t asn_DEF_GeneralizedTime_per_constraints = {
172         { APC_CONSTRAINED, 7, 7, 0x20, 0x7e },  /* Value */
173         { APC_SEMI_CONSTRAINED, -1, -1, 0, 0 }, /* Size */
174         0, 0
175 };
176 asn_TYPE_operation_t asn_OP_GeneralizedTime = {
177         OCTET_STRING_free,
178         GeneralizedTime_print,
179         GeneralizedTime_compare,
180         OCTET_STRING_decode_ber,    /* Implemented in terms of OCTET STRING */
181         GeneralizedTime_encode_der,
182         OCTET_STRING_decode_xer_utf8,
183         GeneralizedTime_encode_xer,
184 #ifdef  ASN_DISABLE_OER_SUPPORT
185         0,
186         0,
187 #else
188         OCTET_STRING_decode_oer,
189         OCTET_STRING_encode_oer,
190 #endif  /* ASN_DISABLE_OER_SUPPORT */
191 #ifdef  ASN_DISABLE_PER_SUPPORT
192         0,
193         0,
194         0,
195         0,
196 #else
197         OCTET_STRING_decode_uper,
198         OCTET_STRING_encode_uper,
199         OCTET_STRING_decode_aper,
200         OCTET_STRING_encode_aper,
201 #endif  /* ASN_DISABLE_PER_SUPPORT */
202         GeneralizedTime_random_fill,
203         0       /* Use generic outmost tag fetcher */
204 };
205 asn_TYPE_descriptor_t asn_DEF_GeneralizedTime = {
206         "GeneralizedTime",
207         "GeneralizedTime",
208         &asn_OP_GeneralizedTime,
209         asn_DEF_GeneralizedTime_tags,
210         sizeof(asn_DEF_GeneralizedTime_tags)
211           / sizeof(asn_DEF_GeneralizedTime_tags[0]) - 2,
212         asn_DEF_GeneralizedTime_tags,
213         sizeof(asn_DEF_GeneralizedTime_tags)
214           / sizeof(asn_DEF_GeneralizedTime_tags[0]),
215         { 0, &asn_DEF_GeneralizedTime_per_constraints, GeneralizedTime_constraint },
216         0, 0,   /* No members */
217         0       /* No specifics */
218 };
219
220 #endif  /* ASN___INTERNAL_TEST_MODE */
221
222 /*
223  * Check that the time looks like the time.
224  */
225 int
226 GeneralizedTime_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
227                            asn_app_constraint_failed_f *ctfailcb,
228                            void *app_key) {
229     const GeneralizedTime_t *st = (const GeneralizedTime_t *)sptr;
230         time_t tloc;
231
232         errno = EPERM;                  /* Just an unlikely error code */
233         tloc = asn_GT2time(st, 0, 0);
234         if(tloc == -1 && errno != EPERM) {
235                 ASN__CTFAIL(app_key, td, sptr,
236                         "%s: Invalid time format: %s (%s:%d)",
237                         td->name, strerror(errno), __FILE__, __LINE__);
238                 return -1;
239         }
240
241         return 0;
242 }
243
244 asn_enc_rval_t
245 GeneralizedTime_encode_der(const asn_TYPE_descriptor_t *td, const void *sptr,
246                            int tag_mode, ber_tlv_tag_t tag,
247                            asn_app_consume_bytes_f *cb, void *app_key) {
248     GeneralizedTime_t *st;
249         asn_enc_rval_t erval = {0,0,0};
250         int fv, fd;     /* seconds fraction value and number of digits */
251         struct tm tm;
252         time_t tloc;
253
254         /*
255          * Encode as a canonical DER.
256          */
257     errno = EPERM;
258     tloc = asn_GT2time_frac((const GeneralizedTime_t *)sptr, &fv, &fd, &tm,
259                             1); /* Recognize time */
260     if(tloc == -1 && errno != EPERM) {
261         /* Failed to recognize time. Fail completely. */
262                 ASN__ENCODE_FAILED;
263     }
264
265     st = asn_time2GT_frac(0, &tm, fv, fd, 1); /* Save time canonically */
266     if(!st) ASN__ENCODE_FAILED;               /* Memory allocation failure. */
267
268     erval = OCTET_STRING_encode_der(td, st, tag_mode, tag, cb, app_key);
269
270     ASN_STRUCT_FREE(*td, st);
271
272     return erval;
273 }
274
275 #ifndef ASN___INTERNAL_TEST_MODE
276
277 asn_enc_rval_t
278 GeneralizedTime_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr,
279                            int ilevel, enum xer_encoder_flags_e flags,
280                            asn_app_consume_bytes_f *cb, void *app_key) {
281     if(flags & XER_F_CANONICAL) {
282                 GeneralizedTime_t *gt;
283                 asn_enc_rval_t rv;
284                 int fv, fd;             /* fractional parts */
285                 struct tm tm;
286
287                 errno = EPERM;
288                 if(asn_GT2time_frac((const GeneralizedTime_t *)sptr,
289                                         &fv, &fd, &tm, 1) == -1
290                                 && errno != EPERM)
291                         ASN__ENCODE_FAILED;
292
293                 gt = asn_time2GT_frac(0, &tm, fv, fd, 1);
294                 if(!gt) ASN__ENCODE_FAILED;
295         
296                 rv = OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
297                         cb, app_key);
298                 ASN_STRUCT_FREE(asn_DEF_GeneralizedTime, gt);
299                 return rv;
300         } else {
301                 return OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
302                         cb, app_key);
303         }
304 }
305
306 #endif  /* ASN___INTERNAL_TEST_MODE */
307
308 int
309 GeneralizedTime_print(const asn_TYPE_descriptor_t *td, const void *sptr,
310                       int ilevel, asn_app_consume_bytes_f *cb, void *app_key) {
311     const GeneralizedTime_t *st = (const GeneralizedTime_t *)sptr;
312
313         (void)td;       /* Unused argument */
314         (void)ilevel;   /* Unused argument */
315
316         if(st && st->buf) {
317                 char buf[32];
318                 struct tm tm;
319                 int ret;
320
321                 errno = EPERM;
322                 if(asn_GT2time(st, &tm, 1) == -1 && errno != EPERM)
323                         return (cb("<bad-value>", 11, app_key) < 0) ? -1 : 0;
324
325                 ret = snprintf(buf, sizeof(buf),
326                         "%04d-%02d-%02d %02d:%02d:%02d (GMT)",
327                         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
328                         tm.tm_hour, tm.tm_min, tm.tm_sec);
329                 assert(ret > 0 && ret < (int)sizeof(buf));
330                 return (cb(buf, ret, app_key) < 0) ? -1 : 0;
331         } else {
332                 return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
333         }
334 }
335
336 time_t
337 asn_GT2time(const GeneralizedTime_t *st, struct tm *ret_tm, int as_gmt) {
338         return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
339 }
340
341 time_t
342 asn_GT2time_prec(const GeneralizedTime_t *st, int *frac_value, int frac_digits, struct tm *ret_tm, int as_gmt) {
343         time_t tloc;
344         int fv, fd = 0;
345
346         if(frac_value)
347                 tloc = asn_GT2time_frac(st, &fv, &fd, ret_tm, as_gmt);
348         else
349                 return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
350         if(fd == 0 || frac_digits <= 0) {
351                 *frac_value = 0;
352         } else {
353                 while(fd > frac_digits)
354                         fv /= 10, fd--;
355                 while(fd < frac_digits) {
356                         if(fv < INT_MAX / 10) {
357                                 fv *= 10;
358                                 fd++;
359                         } else {
360                                 /* Too long precision request */
361                                 fv = 0;
362                                 break;
363                         }
364                 }
365
366                 *frac_value = fv;
367         }
368
369         return tloc;
370 }
371
372 time_t
373 asn_GT2time_frac(const GeneralizedTime_t *st, int *frac_value, int *frac_digits, struct tm *ret_tm, int as_gmt) {
374         struct tm tm_s;
375         uint8_t *buf;
376         uint8_t *end;
377         int gmtoff_h = 0;
378         int gmtoff_m = 0;
379         int gmtoff = 0; /* h + m */
380         int offset_specified = 0;
381         int fvalue = 0;
382         int fdigits = 0;
383         time_t tloc;
384
385         if(!st || !st->buf) {
386                 errno = EINVAL;
387                 return -1;
388         } else {
389                 buf = st->buf;
390                 end = buf + st->size;
391         }
392
393         if(st->size < 10) {
394                 errno = EINVAL;
395                 return -1;
396         }
397
398         /*
399          * Decode first 10 bytes: "AAAAMMJJhh"
400          */
401         memset(&tm_s, 0, sizeof(tm_s));
402 #undef  B2F
403 #undef  B2T
404 #define B2F(var)        do {                                    \
405                 unsigned ch = *buf;                             \
406                 if(ch < 0x30 || ch > 0x39) {                    \
407                         errno = EINVAL;                         \
408                         return -1;                              \
409                 } else {                                        \
410                         var = var * 10 + (ch - 0x30);           \
411                         buf++;                                  \
412                 }                                               \
413         } while(0)
414 #define B2T(var)        B2F(tm_s.var)
415
416         B2T(tm_year);   /* 1: A */
417         B2T(tm_year);   /* 2: A */
418         B2T(tm_year);   /* 3: A */
419         B2T(tm_year);   /* 4: A */
420         B2T(tm_mon);    /* 5: M */
421         B2T(tm_mon);    /* 6: M */
422         B2T(tm_mday);   /* 7: J */
423         B2T(tm_mday);   /* 8: J */
424         B2T(tm_hour);   /* 9: h */
425         B2T(tm_hour);   /* 0: h */
426
427         if(buf == end) goto local_finish;
428
429         /*
430          * Parse [mm[ss[(.|,)ffff]]]
431          *        ^^
432          */
433         switch(*buf) {
434         case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
435         case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
436                 tm_s.tm_min = (*buf++) - 0x30;
437                 if(buf == end) { errno = EINVAL; return -1; }
438                 B2T(tm_min);
439                 break;
440         case 0x2B: case 0x2D:   /* +, - */
441                 goto offset;
442         case 0x5A:              /* Z */
443                 goto utc_finish;
444         default:
445                 errno = EINVAL;
446                 return -1;
447         }
448
449         if(buf == end) goto local_finish;
450
451         /*
452          * Parse [mm[ss[(.|,)ffff]]]
453          *           ^^
454          */
455         switch(*buf) {
456         case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
457         case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
458                 tm_s.tm_sec = (*buf++) - 0x30;
459                 if(buf == end) { errno = EINVAL; return -1; }
460                 B2T(tm_sec);
461                 break;
462         case 0x2B: case 0x2D:   /* +, - */
463                 goto offset;
464         case 0x5A:              /* Z */
465                 goto utc_finish;
466         default:
467                 errno = EINVAL;
468                 return -1;
469         }
470
471         if(buf == end) goto local_finish;
472
473         /*
474          * Parse [mm[ss[(.|,)ffff]]]
475          *               ^ ^
476          */
477         switch(*buf) {
478         case 0x2C: case 0x2E: /* (.|,) */
479                 /*
480                  * Process fractions of seconds.
481                  */
482                 for(buf++; buf < end; buf++) {
483                         int v = *buf;
484                         /* GCC 4.x is being too smart without volatile */
485                         switch(v) {
486                         case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
487                         case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
488                                 if(fvalue < INT_MAX/10) {
489                                         fvalue = fvalue * 10 + (v - 0x30);
490                                         fdigits++;
491                                 } else {
492                                         /* Not enough precision, ignore */
493                                 }
494                                 continue;
495                         default:
496                                 break;
497                         }
498                         break;
499                 }
500         }
501
502         if(buf == end) goto local_finish;
503
504         switch(*buf) {
505         case 0x2B: case 0x2D:   /* +, - */
506                 goto offset;
507         case 0x5A:              /* Z */
508                 goto utc_finish;
509         default:
510                 errno = EINVAL;
511                 return -1;
512         }
513
514
515 offset:
516
517         if(end - buf < 3) {
518                 errno = EINVAL;
519                 return -1;
520         }
521         buf++;
522         B2F(gmtoff_h);
523         B2F(gmtoff_h);
524         if(buf[-3] == 0x2D)     /* Negative */
525                 gmtoff = -1;
526         else
527                 gmtoff = 1;
528
529         if((end - buf) == 2) {
530                 B2F(gmtoff_m);
531                 B2F(gmtoff_m);
532         } else if(end != buf) {
533                 errno = EINVAL;
534                 return -1;
535         }
536
537         gmtoff = gmtoff * (3600 * gmtoff_h + 60 * gmtoff_m);
538
539         /* Fall through */
540 utc_finish:
541
542         offset_specified = 1;
543
544         /* Fall through */
545 local_finish:
546
547         /*
548          * Validation.
549          */
550         if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1)
551         || (tm_s.tm_mday > 31 || tm_s.tm_mday < 1)
552         || (tm_s.tm_hour > 23)
553         || (tm_s.tm_sec > 60)
554         ) {
555                 errno = EINVAL;
556                 return -1;
557         }
558
559         /* Canonicalize */
560         tm_s.tm_mon -= 1;       /* 0 - 11 */
561         tm_s.tm_year -= 1900;
562         tm_s.tm_isdst = -1;
563
564         tm_s.tm_sec -= gmtoff;
565
566         /*** AT THIS POINT tm_s is either GMT or local (unknown) ****/
567
568         if(offset_specified) {
569                 tloc = timegm(&tm_s);
570         } else {
571                 /*
572                  * Without an offset (or "Z"),
573                  * we can only guess that it is a local zone.
574                  * Interpret it in this fashion.
575                  */
576                 tloc = mktime(&tm_s);
577         }
578         if(tloc == -1) {
579                 errno = EINVAL;
580                 return -1;
581         }
582
583         if(ret_tm) {
584                 if(as_gmt) {
585                         if(offset_specified) {
586                                 *ret_tm = tm_s;
587                         } else {
588                                 if(gmtime_r(&tloc, ret_tm) == 0) {
589                                         errno = EINVAL;
590                                         return -1;
591                                 }
592                         }
593                 } else {
594                         if(localtime_r(&tloc, ret_tm) == 0) {
595                                 errno = EINVAL;
596                                 return -1;
597                         }
598                 }
599         }
600
601         /* Fractions of seconds */
602         if(frac_value) *frac_value = fvalue;
603         if(frac_digits) *frac_digits = fdigits;
604
605         return tloc;
606 }
607
608 GeneralizedTime_t *
609 asn_time2GT(GeneralizedTime_t *opt_gt, const struct tm *tm, int force_gmt) {
610         return asn_time2GT_frac(opt_gt, tm, 0, 0, force_gmt);
611 }
612
613 GeneralizedTime_t *
614 asn_time2GT_frac(GeneralizedTime_t *opt_gt, const struct tm *tm, int frac_value, int frac_digits, int force_gmt) {
615         struct tm tm_s;
616         long gmtoff = 0;
617         const unsigned int buf_size =
618                 4 + 2 + 2       /* yyyymmdd */
619                 + 2 + 2 + 2     /* hhmmss */
620                 + 1 + 9         /* .fffffffff */
621                 + 1 + 4         /* +hhmm */
622                 + 1             /* '\0' */
623                 ;
624         char *buf = NULL;
625         char *p = NULL;
626         int size = 0;
627
628         /* Check arguments */
629         if(!tm) {
630                 errno = EINVAL;
631                 return 0;
632         }
633
634         /* Pre-allocate a buffer of sufficient yet small length */
635         buf = (char *)MALLOC(buf_size);
636         if(!buf) return 0;
637
638         gmtoff = GMTOFF(*tm);
639
640         if(force_gmt && gmtoff) {
641                 tm_s = *tm;
642                 tm_s.tm_sec -= gmtoff;
643                 timegm(&tm_s);  /* Fix the time */
644                 tm = &tm_s;
645 #ifdef  HAVE_TM_GMTOFF
646                 assert(!GMTOFF(tm_s));  /* Will fix itself */
647 #else   /* !HAVE_TM_GMTOFF */
648                 gmtoff = 0;
649 #endif
650         }
651
652         size = snprintf(buf, buf_size, "%04d%02d%02d%02d%02d%02d",
653                 tm->tm_year + 1900,
654                 tm->tm_mon + 1,
655                 tm->tm_mday,
656                 tm->tm_hour,
657                 tm->tm_min,
658                 tm->tm_sec
659         );
660         if(size != 14) {
661                 /* Could be assert(size == 14); */
662                 FREEMEM(buf);
663                 errno = EINVAL;
664                 return 0;
665         }
666
667         p = buf + size;
668
669         /*
670          * Deal with fractions.
671          */
672         if(frac_value > 0 && frac_digits > 0) {
673                 char *end = p + 1 + 9;  /* '.' + maximum 9 digits */
674                 char *z = p;
675                 long fbase;
676                 *z++ = '.';
677
678                 /* Place bounds on precision */
679                 while(frac_digits-- > 9)
680                         frac_value /= 10;
681
682                 /* emulate fbase = pow(10, frac_digits) */
683                 for(fbase = 1; frac_digits--;)
684                         fbase *= 10;
685
686                 do {
687                         int digit = frac_value / fbase;
688                         if(digit > 9) { z = 0; break; }
689                         *z++ = digit + 0x30;
690                         frac_value %= fbase;
691                         fbase /= 10;
692                 } while(fbase > 0 && frac_value > 0 && z < end);
693                 if(z) {
694                         for(--z; *z == 0x30; --z);      /* Strip zeroes */
695                         p = z + (*z != '.');
696                         size = p - buf;
697                 }
698         }
699
700         if(force_gmt) {
701                 *p++ = 0x5a;    /* "Z" */
702                 *p++ = 0;
703                 size++;
704         } else {
705                 int ret;
706                 gmtoff %= 86400;
707                 ret = snprintf(p, buf_size - size, "%+03ld%02ld",
708                         gmtoff / 3600, labs(gmtoff % 3600) / 60);
709                 if(ret != 5) {
710                         FREEMEM(buf);
711                         errno = EINVAL;
712                         return 0;
713                 }
714                 size += ret;
715         }
716
717         if(opt_gt) {
718                 if(opt_gt->buf)
719                         FREEMEM(opt_gt->buf);
720         } else {
721                 opt_gt = (GeneralizedTime_t *)CALLOC(1, sizeof *opt_gt);
722                 if(!opt_gt) { FREEMEM(buf); return 0; }
723         }
724
725         opt_gt->buf = (unsigned char *)buf;
726         opt_gt->size = size;
727
728         return opt_gt;
729 }
730
731 asn_random_fill_result_t
732 GeneralizedTime_random_fill(const asn_TYPE_descriptor_t *td, void **sptr,
733                               const asn_encoding_constraints_t *constraints,
734                               size_t max_length) {
735     asn_random_fill_result_t result_ok = {ARFILL_OK, 1};
736     asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0};
737     asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0};
738     static const char *values[] = {
739         "19700101000000",    "19700101000000-0000",   "19700101000000+0000",
740         "19700101000000Z",   "19700101000000.3Z",     "19821106210623.3",
741         "19821106210629.3Z", "19691106210827.3-0500", "19821106210629.456",
742     };
743     size_t rnd = asn_random_between(0, sizeof(values)/sizeof(values[0])-1);
744
745     (void)constraints;
746
747     if(max_length < sizeof("yyyymmddhhmmss") && !*sptr) {
748         return result_skipped;
749     }
750
751     if(*sptr) {
752         if(OCTET_STRING_fromBuf(*sptr, values[rnd], -1) != 0) {
753             if(!sptr) return result_failed;
754         }
755     } else {
756         *sptr = OCTET_STRING_new_fromBuf(td, values[rnd], -1);
757         if(!sptr) return result_failed;
758     }
759
760     return result_ok;
761 }
762
763 int
764 GeneralizedTime_compare(const asn_TYPE_descriptor_t *td, const void *aptr,
765                         const void *bptr) {
766     const GeneralizedTime_t *a = aptr;
767     const GeneralizedTime_t *b = bptr;
768
769     (void)td;
770
771     if(a && b) {
772         int afrac_value, afrac_digits;
773         int bfrac_value, bfrac_digits;
774         int aerr, berr;
775         time_t at, bt;
776
777         errno = EPERM;
778         at = asn_GT2time_frac(a, &afrac_value, &afrac_digits, 0, 0);
779         aerr = errno;
780         errno = EPERM;
781         bt = asn_GT2time_frac(b, &bfrac_value, &bfrac_digits, 0, 0);
782         berr = errno;
783
784         if(at == -1 && aerr != EPERM) {
785             if(bt == -1 && berr != EPERM) {
786                 return OCTET_STRING_compare(td, aptr, bptr);
787             } else {
788                 return -1;
789             }
790         } else if(bt == -1 && berr != EPERM) {
791             return 1;
792         } else {
793             /* Both values are valid. */
794         }
795
796         if(at < bt) {
797             return -1;
798         } else if(at > bt) {
799             return 1;
800         } else if(afrac_digits == bfrac_digits) {
801             if(afrac_value == bfrac_value) {
802                 return 0;
803             }
804             if(afrac_value < bfrac_value) {
805                 return -1;
806             } else {
807                 return 1;
808             }
809         } else if(afrac_digits == 0) {
810             return -1;
811         } else if(bfrac_digits == 0) {
812             return 1;
813         } else {
814             double afrac = (double)afrac_value / afrac_digits;
815             double bfrac = (double)bfrac_value / bfrac_digits;
816             if(afrac < bfrac) {
817                 return -1;
818             } else if(afrac > bfrac) {
819                 return 1;
820             } else {
821                 return 0;
822             }
823         }
824     } else if(!a && !b) {
825         return 0;
826     } else if(!a) {
827         return -1;
828     } else {
829         return 1;
830     }
831
832 }
833