]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_json_object.c
hash keys have to be escaped like regular strings
[OpenSRF.git] / src / libopensrf / osrf_json_object.c
1 /*
2 Copyright (C) 2006  Georgia Public Library Service 
3 Bill Erickson <billserickson@gmail.com>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 */
15
16 #include <stdlib.h>
17 #include <ctype.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include <opensrf/log.h>
21 #include <opensrf/osrf_json.h>
22 #include <opensrf/osrf_json_utils.h>
23 #include <opensrf/osrf_utf8.h>
24
25 /* cleans up an object if it is morphing another object, also
26  * verifies that the appropriate storage container exists where appropriate */
27 #define JSON_INIT_CLEAR(_obj_, newtype)         \
28         if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
29                 osrfHashFree(_obj_->value.h);                   \
30                 _obj_->value.h = NULL;                                  \
31 } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) {       \
32                 osrfListFree(_obj_->value.l);                   \
33                 _obj_->value.l = NULL;                                  \
34 } else if( _obj_->type == JSON_STRING || _obj_->type == JSON_NUMBER ) { \
35                 free(_obj_->value.s);                                           \
36                 _obj_->value.s = NULL;                                  \
37 } \
38         _obj_->type = newtype;\
39         if( newtype == JSON_HASH && _obj_->value.h == NULL ) {  \
40                 _obj_->value.h = osrfNewHash();         \
41                 osrfHashSetCallback( _obj_->value.h, _jsonFreeHashItem ); \
42 } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) {  \
43                 _obj_->value.l = osrfNewList();         \
44                 _obj_->value.l->freeItem = _jsonFreeListItem;\
45 }
46
47 static int unusedObjCapture = 0;
48 static int unusedObjRelease = 0;
49 static int mallocObjCreate = 0;
50 static int currentListLen = 0;
51
52 union unusedObjUnion{
53
54         union unusedObjUnion* next;
55         jsonObject obj;
56 };
57 typedef union unusedObjUnion unusedObj;
58
59 // We maintain a free list of jsonObjects that are available
60 // for use, in order to reduce the churning through
61 // malloc() and free().
62
63 static unusedObj* freeObjList = NULL;
64
65 static void add_json_to_buffer( const jsonObject* obj,
66         growing_buffer * buf, int do_classname, int second_pass );
67
68 /**
69  * Return all unused jsonObjects to the heap
70  * @return Nothing
71  */
72 void jsonObjectFreeUnused( void ) {
73
74         unusedObj* temp;
75         while( freeObjList ) {
76                 temp = freeObjList->next;
77                 free( freeObjList );
78                 freeObjList = temp;
79         }
80 }
81
82 jsonObject* jsonNewObject(const char* data) {
83
84         jsonObject* o;
85
86         if( freeObjList ) {
87                 o = (jsonObject*) freeObjList;
88                 freeObjList = freeObjList->next;
89         unusedObjRelease++;
90         currentListLen--;
91         } else {
92                 OSRF_MALLOC( o, sizeof(jsonObject) );
93         mallocObjCreate++;
94     }
95
96         o->size = 0;
97         o->classname = NULL;
98         o->parent = NULL;
99
100         if(data) {
101                 o->type = JSON_STRING;
102                 o->value.s = strdup(data);
103         } else {
104                 o->type = JSON_NULL;
105                 o->value.s = NULL;
106         }
107
108         return o;
109 }
110
111 jsonObject* jsonNewObjectFmt(const char* data, ...) {
112
113         jsonObject* o;
114
115         if( freeObjList ) {
116                 o = (jsonObject*) freeObjList;
117                 freeObjList = freeObjList->next;
118         unusedObjRelease++;
119         currentListLen--;
120         } else {
121                 OSRF_MALLOC( o, sizeof(jsonObject) );
122         mallocObjCreate++;
123     }
124
125         o->size = 0;
126         o->classname = NULL;
127         o->parent = NULL;
128
129         if(data) {
130                 VA_LIST_TO_STRING(data);
131                 o->type = JSON_STRING;
132                 o->value.s = strdup(VA_BUF);
133         }
134         else {
135                 o->type = JSON_NULL;
136                 o->value.s = NULL;
137         }
138         
139         return o;
140 }
141
142 jsonObject* jsonNewNumberObject( double num ) {
143         jsonObject* o = jsonNewObject(NULL);
144         o->type = JSON_NUMBER;
145         o->value.s = doubleToString( num );
146         return o;
147 }
148
149 /**
150  * Creates a new number object from a numeric string
151  */
152 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
153         if( !numstr )
154                 numstr = "0";
155         else if( !jsonIsNumeric( numstr ) )
156                 return NULL;
157
158         jsonObject* o = jsonNewObject(NULL);
159         o->type = JSON_NUMBER;
160         o->value.s = strdup( numstr );
161         return o;
162 }
163
164 jsonObject* jsonNewBoolObject(int val) {
165     jsonObject* o = jsonNewObject(NULL);
166     o->type = JSON_BOOL;
167     jsonSetBool(o, val);
168     return o;
169 }
170
171 jsonObject* jsonNewObjectType(int type) {
172         jsonObject* o = jsonNewObject(NULL);
173         o->type = type;
174         return o;
175 }
176
177 void jsonObjectFree( jsonObject* o ) {
178
179         if(!o || o->parent) return;
180         free(o->classname);
181
182         switch(o->type) {
183                 case JSON_HASH          : osrfHashFree(o->value.h); break;
184                 case JSON_ARRAY : osrfListFree(o->value.l); break;
185                 case JSON_STRING        : free(o->value.s); break;
186                 case JSON_NUMBER        : free(o->value.s); break;
187         }
188
189         // Stick the old jsonObject onto a free list
190         // for potential reuse
191         
192         unusedObj* unused = (unusedObj*) o;
193         unused->next = freeObjList;
194         freeObjList = unused;
195
196     unusedObjCapture++;
197     currentListLen++;
198     if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
199         osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, "
200                         "Reusable objects captured: %d, Objects reused: %d, "
201                         "Current List Length: %d",
202                         mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
203 }
204
205 static void _jsonFreeHashItem(char* key, void* item){
206         if(!item) return;
207         jsonObject* o = (jsonObject*) item;
208         o->parent = NULL; /* detach the item */
209         jsonObjectFree(o);
210 }
211 static void _jsonFreeListItem(void* item){
212         if(!item) return;
213         jsonObject* o = (jsonObject*) item;
214         o->parent = NULL; /* detach the item */
215         jsonObjectFree(o);
216 }
217
218 void jsonSetBool(jsonObject* bl, int val) {
219     if(!bl) return;
220     JSON_INIT_CLEAR(bl, JSON_BOOL);
221     bl->value.b = val;
222 }
223
224 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
225     if(!o) return -1;
226     if(!newo) newo = jsonNewObject(NULL);
227         JSON_INIT_CLEAR(o, JSON_ARRAY);
228         newo->parent = o;
229         osrfListPush( o->value.l, newo );
230         o->size = o->value.l->size;
231         return o->size;
232 }
233
234 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
235     if(!dest) return -1;
236     if(!newObj) newObj = jsonNewObject(NULL);
237         JSON_INIT_CLEAR(dest, JSON_ARRAY);
238         newObj->parent = dest;
239         osrfListSet( dest->value.l, newObj, index );
240         dest->size = dest->value.l->size;
241         return dest->value.l->size;
242 }
243
244 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
245     if(!o) return -1;
246     if(!newo) newo = jsonNewObject(NULL);
247         JSON_INIT_CLEAR(o, JSON_HASH);
248         newo->parent = o;
249         osrfHashSet( o->value.h, newo, key );
250         o->size = osrfHashGetCount(o->value.h);
251         return o->size;
252 }
253
254 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
255         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
256         return osrfHashGet( obj->value.h, key);
257 }
258
259 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
260         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
261         return osrfHashGet( obj->value.h, key);
262 }
263
264 /**
265  * Recursively traverse a jsonObject, formatting it into a JSON string.
266  *
267  * The last two parameters are booleans.
268  *
269  * If do_classname is true, examine each node for a classname, and if you
270  * find one, pretend that the node is under an extra layer of JSON_HASH, with
271  * JSON_CLASS_KEY and JSON_DATA_KEY as keys.
272  *
273  * second_pass should always be false except for some recursive calls.  It
274  * is used when expanding classnames, to distinguish between the first and
275  * second passes through a given node.
276  *
277  * @return Nothing
278  */
279 static void add_json_to_buffer( const jsonObject* obj,
280         growing_buffer * buf, int do_classname, int second_pass ) {
281
282     if(NULL == obj) {
283         OSRF_BUFFER_ADD(buf, "null");
284         return;
285     }
286
287         if( obj->classname && do_classname )
288         {
289                 if( second_pass )
290                         second_pass = 0;
291                 else
292                 {
293                         // Pretend we see an extra layer of JSON_HASH
294                         
295                         OSRF_BUFFER_ADD( buf, "{\"" );
296                         OSRF_BUFFER_ADD( buf, JSON_CLASS_KEY );
297                         OSRF_BUFFER_ADD( buf, "\":\"" );
298                         OSRF_BUFFER_ADD( buf, obj->classname );
299                         OSRF_BUFFER_ADD( buf, "\",\"" );
300                         OSRF_BUFFER_ADD( buf, JSON_DATA_KEY );
301                         OSRF_BUFFER_ADD( buf, "\":" );
302                         add_json_to_buffer( obj, buf, 1, 1 );
303                         buffer_add_char( buf, '}' );
304                         return;
305                 }
306         }
307
308         switch(obj->type) {
309
310                 case JSON_BOOL :
311                         if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); 
312                         else OSRF_BUFFER_ADD(buf, "false"); 
313                         break;
314
315         case JSON_NUMBER: {
316             if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
317             else OSRF_BUFFER_ADD_CHAR( buf, '0' );
318             break;
319         }
320
321                 case JSON_NULL:
322                         OSRF_BUFFER_ADD(buf, "null");
323                         break;
324
325                 case JSON_STRING:
326                         OSRF_BUFFER_ADD_CHAR(buf, '"');
327                         buffer_append_utf8(buf, obj->value.s);
328                         OSRF_BUFFER_ADD_CHAR(buf, '"');
329                         break;
330                         
331                 case JSON_ARRAY: {
332                         OSRF_BUFFER_ADD_CHAR(buf, '[');
333                         if( obj->value.l ) {
334                                 int i;
335                                 for( i = 0; i != obj->value.l->size; i++ ) {
336                                         if(i > 0) OSRF_BUFFER_ADD(buf, ",");
337                                         add_json_to_buffer(
338                                                 OSRF_LIST_GET_INDEX(obj->value.l, i), buf, do_classname, second_pass );
339                                 }
340                         }
341                         OSRF_BUFFER_ADD_CHAR(buf, ']');
342                         break;
343                 }
344
345                 case JSON_HASH: {
346         
347                         OSRF_BUFFER_ADD_CHAR(buf, '{');
348                         osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
349                         jsonObject* item;
350                         int i = 0;
351
352                         while( (item = osrfHashIteratorNext(itr)) ) {
353                                 if(i++ > 0) OSRF_BUFFER_ADD_CHAR(buf, ',');
354                                 OSRF_BUFFER_ADD_CHAR(buf, '"');
355                 buffer_append_utf8(buf, osrfHashIteratorKey(itr));
356                                 OSRF_BUFFER_ADD(buf, "\":");
357                                 add_json_to_buffer( item, buf, do_classname, second_pass );
358                         }
359
360                         osrfHashIteratorFree(itr);
361                         OSRF_BUFFER_ADD_CHAR(buf, '}');
362                         break;
363                 }
364         }
365 }
366
367 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
368         if(!obj) return NULL;
369         growing_buffer* buf = buffer_init(32);
370         add_json_to_buffer( obj, buf, 0, 0 );
371         return buffer_release( buf );
372 }
373
374 char* jsonObjectToJSON( const jsonObject* obj ) {
375         if(!obj) return NULL;
376         growing_buffer* buf = buffer_init(32);
377         add_json_to_buffer( obj, buf, 1, 0 );
378         return buffer_release( buf );
379 }
380
381 jsonIterator* jsonNewIterator(const jsonObject* obj) {
382         if(!obj) return NULL;
383         jsonIterator* itr;
384         OSRF_MALLOC(itr, sizeof(jsonIterator));
385
386         itr->obj    = (jsonObject*) obj;
387         itr->index  = 0;
388         itr->key    = NULL;
389
390         if( obj->type == JSON_HASH )
391                 itr->hashItr = osrfNewHashIterator(obj->value.h);
392         else
393                 itr->hashItr = NULL;
394         
395         return itr;
396 }
397
398 void jsonIteratorFree(jsonIterator* itr) {
399         if(!itr) return;
400         free(itr->key);
401         osrfHashIteratorFree(itr->hashItr);
402         free(itr);
403 }
404
405 jsonObject* jsonIteratorNext(jsonIterator* itr) {
406         if(!(itr && itr->obj)) return NULL;
407         if( itr->obj->type == JSON_HASH ) {
408                 if(!itr->hashItr) return NULL;
409                 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
410         if(!item) return NULL;
411                 free(itr->key);
412                 itr->key = strdup( osrfHashIteratorKey(itr->hashItr) );
413                 return item;
414         } else {
415                 return jsonObjectGetIndex( itr->obj, itr->index++ );
416         }
417 }
418
419 int jsonIteratorHasNext(const jsonIterator* itr) {
420         if(!(itr && itr->obj)) return 0;
421         if( itr->obj->type == JSON_HASH )
422                 return osrfHashIteratorHasNext( itr->hashItr );
423         return (itr->index < itr->obj->size) ? 1 : 0;
424 }
425
426 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
427         if(!obj) return NULL;
428         return (obj->type == JSON_ARRAY) ? 
429         (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
430 }
431
432
433
434 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
435         if( dest && dest->type == JSON_ARRAY ) {
436                 osrfListRemove(dest->value.l, index);
437                 return dest->value.l->size;
438         }
439         return -1;
440 }
441
442
443 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
444         if( dest && key && dest->type == JSON_HASH ) {
445                 osrfHashRemove(dest->value.h, key);
446                 return 1;
447         }
448         return -1;
449 }
450
451 /**
452  Allocate a buffer and format a specified numeric value into it.
453  Caller is responsible for freeing the buffer.
454 **/
455 char* doubleToString( double num ) {
456         
457         char buf[ 64 ];
458         size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
459         if( len < sizeof( buf ) )
460                 return strdup( buf );
461         else
462         {
463                 // Need a bigger buffer (should never be necessary)
464                 
465                 char* bigger_buff = safe_malloc( len + 1 );
466                 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
467                 return bigger_buff;
468         }
469 }
470
471 char* jsonObjectGetString(const jsonObject* obj) {
472         if(obj)
473         {
474                 if( obj->type == JSON_STRING )
475                         return obj->value.s;
476                 else if( obj->type == JSON_NUMBER )
477                         return obj->value.s ? obj->value.s : "0";
478                 else
479                         return NULL;
480         }
481         else
482                 return NULL;
483 }
484
485 double jsonObjectGetNumber( const jsonObject* obj ) {
486         return (obj && obj->type == JSON_NUMBER && obj->value.s)
487                         ? strtod( obj->value.s, NULL ) : 0;
488 }
489
490 void jsonObjectSetString(jsonObject* dest, const char* string) {
491         if(!(dest && string)) return;
492         JSON_INIT_CLEAR(dest, JSON_STRING);
493         dest->value.s = strdup(string);
494 }
495
496 /**
497  Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
498  a specified numeric string in it.  If the string is not numeric,
499  store the equivalent of zero, and return an error status.
500 **/
501 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
502         if(!(dest && string)) return -1;
503         JSON_INIT_CLEAR(dest, JSON_NUMBER);
504
505         if( jsonIsNumeric( string ) ) {
506                 dest->value.s = strdup(string);
507                 return 0;
508         }
509         else {
510                 dest->value.s = NULL;  // equivalent to zero
511                 return -1;
512         }
513 }
514
515 void jsonObjectSetNumber(jsonObject* dest, double num) {
516         if(!dest) return;
517         JSON_INIT_CLEAR(dest, JSON_NUMBER);
518         dest->value.s = doubleToString( num );
519 }
520
521 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
522         if(!(dest && classname)) return;
523         free(dest->classname);
524         dest->classname = strdup(classname);
525 }
526 const char* jsonObjectGetClass(const jsonObject* dest) {
527     if(!dest) return NULL;
528     return dest->classname;
529 }
530
531 jsonObject* jsonObjectClone( const jsonObject* o ) {
532     if(!o) return jsonNewObject(NULL);
533
534     int i;
535     jsonObject* arr; 
536     jsonObject* hash; 
537     jsonIterator* itr;
538     jsonObject* tmp;
539     jsonObject* result = NULL;
540
541     switch(o->type) {
542         case JSON_NULL:
543             result = jsonNewObject(NULL);
544             break;
545         case JSON_STRING:
546             result = jsonNewObject(jsonObjectGetString(o));
547             break;
548         case JSON_NUMBER:
549                         result = jsonNewObject( o->value.s );
550                         result->type = JSON_NUMBER;
551             break;
552         case JSON_BOOL:
553             result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
554             break;
555         case JSON_ARRAY:
556             arr = jsonNewObject(NULL);
557             arr->type = JSON_ARRAY;
558             for(i=0; i < o->size; i++) 
559                 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
560             result = arr;
561             break;
562         case JSON_HASH:
563             hash = jsonNewObject(NULL);
564             hash->type = JSON_HASH;
565             itr = jsonNewIterator(o);
566             while( (tmp = jsonIteratorNext(itr)) )
567                 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
568             jsonIteratorFree(itr);
569             result = hash;
570             break;
571     }
572
573     jsonObjectSetClass(result, jsonObjectGetClass(o));
574     return result;
575 }
576
577 int jsonBoolIsTrue( const jsonObject* boolObj ) {
578     if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
579         return 1;
580     return 0;
581 }
582
583
584 char* jsonObjectToSimpleString( const jsonObject* o ) {
585         if(!o) return NULL;
586
587         char* value = NULL;
588
589         switch( o->type ) {
590
591                 case JSON_NUMBER:
592                         value = strdup( o->value.s ? o->value.s : "0" );
593                         break;
594
595                 case JSON_STRING:
596                         value = strdup(o->value.s);
597         }
598
599         return value;
600 }
601
602 /**
603  Return 1 if the string is numeric, otherwise return 0.
604  This validation follows the rules defined by the grammar at:
605  http://www.json.org/
606  **/
607 int jsonIsNumeric( const char* s ) {
608
609         if( !s || !*s ) return 0;
610
611         const char* p = s;
612
613         // skip leading minus sign, if present (leading plus sign not allowed)
614
615         if( '-' == *p )
616                 ++p;
617
618         // There must be at least one digit to the left of the decimal
619
620         if( isdigit( (unsigned char) *p ) ) {
621                 if( '0' == *p++ ) {
622
623                         // If the first digit is zero, it must be the
624                         // only digit to the lerft of the decimal
625
626                         if( isdigit( (unsigned char) *p ) )
627                                 return 0;
628                 }
629                 else {
630
631                         // Skip oer the following digits
632
633                         while( isdigit( (unsigned char) *p ) ) ++p;
634                 }
635         }
636         else
637                 return 0;
638
639         if( !*p )
640                 return 1;             // integer
641
642         if( '.' == *p ) {
643
644                 ++p;
645
646                 // If there is a decimal point, there must be
647                 // at least one digit to the right of it
648
649                 if( isdigit( (unsigned char) *p ) )
650                         ++p;
651                 else
652                         return 0;
653
654                 // skip over contiguous digits
655
656                 while( isdigit( (unsigned char) *p ) ) ++p;
657         }
658
659         if( ! *p )
660                 return 1;  // decimal fraction, no exponent
661         else if( *p != 'e' && *p != 'E' )
662                 return 0;  // extra junk, no exponent
663         else
664                 ++p;
665
666         // If we get this far, we have the beginnings of an exponent.
667         // Skip over optional sign of exponent.
668
669         if( '-' == *p || '+' == *p )
670                 ++p;
671
672         // There must be at least one digit in the exponent
673         
674         if( isdigit( (unsigned char) *p ) )
675                 ++p;
676         else
677                 return 0;
678
679         // skip over contiguous digits
680
681         while( isdigit( (unsigned char) *p ) ) ++p;
682
683         if( *p )
684                 return 0;  // extra junk
685         else
686                 return 1;  // number with exponent
687 }
688
689 /**
690  Allocate and reformat a numeric string into one that is valid
691  by JSON rules.  If the string is not numeric, return NULL.
692  Caller is responsible for freeing the buffer.
693  **/
694 char* jsonScrubNumber( const char* s ) {
695         if( !s || !*s ) return NULL;
696
697         growing_buffer* buf = buffer_init( 64 );
698
699         // Skip leading white space, if present
700
701         while( isspace( (unsigned char) *s ) ) ++s;
702
703         // Skip leading plus sign, if present, but keep a minus
704
705         if( '-' == *s )
706         {
707                 buffer_add_char( buf, '-' );
708                 ++s;
709         }
710         else if( '+' == *s )
711                 ++s;
712
713         if( '\0' == *s ) {
714                 // No digits found
715
716                 buffer_free( buf );
717                 return NULL;
718         }
719         // Skip any leading zeros
720
721         while( '0' == *s ) ++s;
722
723         // Capture digits to the left of the decimal,
724         // and note whether there are any.
725
726         int left_digit = 0;  // boolean
727
728         if( isdigit( (unsigned char) *s ) ) {
729                 buffer_add_char( buf, *s++ );
730                 left_digit = 1;
731         }
732         
733         while( isdigit( (unsigned char) *s  ) )
734                 buffer_add_char( buf, *s++ );
735
736         // Now we expect to see a decimal point,
737         // an exponent, or end-of-string.
738
739         switch( *s )
740         {
741                 case '\0' :
742                         break;
743                 case '.' :
744                 {
745                         // Add a single leading zero, if we need to
746
747                         if( ! left_digit )
748                                 buffer_add_char( buf, '0' );
749                         buffer_add_char( buf, '.' );
750                         ++s;
751
752                         if( ! left_digit && ! isdigit( (unsigned char) *s ) )
753                         {
754                                 // No digits on either side of decimal
755
756                                 buffer_free( buf );
757                                 return NULL;
758                         }
759
760                         // Collect digits to right of decimal
761
762                         while( isdigit( (unsigned char) *s ) )
763                                 buffer_add_char( buf, *s++ );
764
765                         break;
766                 }
767                 case 'e' :
768                 case 'E' :
769
770                         // Exponent; we'll deal with it later, but
771                         // meanwhile make sure we have something
772                         // to its left
773
774                         if( ! left_digit )
775                                 buffer_add_char( buf, '1' );
776                         break;
777                 default :
778
779                         // Unexpected character; bail out
780
781                         buffer_free( buf );
782                         return NULL;
783         }
784
785         if( '\0' == *s )    // Are we done yet?
786                 return buffer_release( buf );
787
788         if( 'e' != *s && 'E' != *s ) {
789
790                 // Unexpected character: bail out
791
792                 buffer_free( buf );
793                 return NULL;
794         }
795
796         // We have an exponent.  Load the e or E,
797         // and the sign if there is one.
798
799         buffer_add_char( buf, *s++ );
800
801         if( '+' == *s || '-' == *s )
802                 buffer_add_char( buf, *s++ );
803
804         // Collect digits of the exponent
805
806         while( isdigit( (unsigned char) *s ) )
807                 buffer_add_char( buf, *s++ );
808
809         // There better not be anything left
810
811         if( *s ) {
812                 buffer_free( buf );
813                 return NULL;
814         }
815
816         return buffer_release( buf );
817 }