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