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