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