This is COWSStringNode.m in view mode; [Download] [Up]
/* Copyright (C) 1994 Sean Luke COWSStringNode.m Version 1.5a_mj Code mangled beyond recognition by Michal Jaegermann */ #import "COWSStringNode.h" #import "COWSInterpreter.h" #import <string.h> #import <stdlib.h> @implementation COWSStringNode #define CPREC 12 /* fixed precision for conversion of doubles to strings */ #define WBUFSZ 128 /* size of buffer - should be big enough to hold results of any conversion */ #define CVFORMAT "%.*e" static char wbuf[WBUFSZ]; /* work buffer for number output formatting */ /* * This function is doing what really "%g" sprintf() is supposed * to do, but it is not doing on NeXT machines, because is plainly * broken and NeXT does not want to be bothered to fix it. * * For simplicity we will use here a fixed precision * * Returns a number of characters written to 'wbuf' without * counting a trailing 0. * * This code is based on a code for sgfmt() from gawk sources. */ static int dtostring(double val) { int precision, count; char *pos, *dot; char cvfmt[] = CVFORMAT; /* first we will try this */ extern int atoi(const char *); extern char *strrchr(const char*, int); if (val == 0.0) { wbuf[0] = '0'; wbuf[1] = '\0'; return 1; } precision = CPREC - 1; count = sprintf(wbuf, cvfmt, precision, val); if ((pos = strrchr(wbuf, 'e')) != NULL) { /*exponent starts here */ int exp = atoi(pos + 1); if (exp >= -4 && exp <= precision) { /* try again in 'f' format */ *(cvfmt + (sizeof(cvfmt) - 2)) = 'f'; precision -= exp; count = sprintf(wbuf, cvfmt, precision, val); pos = wbuf + count; /* a bit of paranoia, but play it safe */ while (*--pos == ' ') count -= 1; pos += 1; } /* remove trailing zeros */ if (NULL != (dot = strrchr(wbuf, '.'))) { while ( pos > dot && *--pos == '0') precision -= 1; if (dot == pos) precision -= 1; if (precision < 0) precision = 0; count = sprintf(wbuf, cvfmt, precision, val); } else { *pos = '\0'; } } return count; } - init { id returnval = [super init]; string = NULL; len = 0; value_flags = COWSSTRINGNODE_STORED_STRING; return returnval; } - free { if (string != NULL) { [self setString :NULL size:0]; // on purpose - internals may change } return[super free]; } - (const char *)string { if (!STORED_STRING(value_flags)) { if (!STORED_NUMBER(value_flags)) { [self setString :NULL size:0]; // should not happen } else { /* number but not a string - we need to convert */ size_t dvlen = dtostring(double_value); [self setString :wbuf size:dvlen]; SN_SET(value_flags, COWSSTRINGNODE_STORED_STRING); } } // used to be: return (const char*)string; // changed to by Sean so NULLs are never returned: return (string==NULL) ? "" : (const char*) string; } - setString:(const char *)this { size_t this_size; this_size = (this == NULL ? 0 : strlen(this)); return [self setString :this size:this_size]; } // passing a NULL string here will cause freeing of 'string' storage - setString:(const char *)this size:(size_t)this_size { size_t newlen = 0; SN_SET(value_flags, COWSSTRINGNODE_STORED_STRING); SN_CLEAR(value_flags, COWSSTRINGNODE_STORED_NUMBER); if (this == NULL) { if (string != NULL) { /* freeing NULL value should be OK - sigh!! */ free(string); string = NULL; } } else { newlen = this_size + 1; if (newlen != len) { /* grow or shrink */ /* realloc should really do that itself, but is often buggy */ string = ((string == NULL) ? malloc (newlen) : realloc(string, newlen)); } } if (string != NULL) { /* don't try to copy if allocation failed */ strncpy(string, this, this_size); string[this_size] = '\0'; len = newlen; } else { len = 0; } return self; } - setDoubleVal:(double)this { SN_SET(value_flags, COWSSTRINGNODE_STORED_NUMBER); SN_CLEAR(value_flags, COWSSTRINGNODE_STORED_STRING); double_value = this; return self; } - (double)doubleVal { if (!STORED_NUMBER(value_flags)) { double result; if (STORED_STRING(value_flags) && (NULL != string)) { char *eptr; result = strtod((const char *)string, &eptr); if (0.0 != result) { /* * check if what we converted is a "pure" number * taking into account that strtod on NeXT is broken; * this test accepts ".1", but rejects "1." */ while(NXIsSpace(*eptr) || ('\0' == *eptr)) { eptr -= 1; // eptr will not drop below string; we converted something, // hence some digits are in (string..eptr) interval } if (!NXIsDigit(*eptr)) result = 0.0; } } else { result = 0.0; } SN_SET(value_flags, COWSSTRINGNODE_STORED_NUMBER); double_value = result; } return double_value; } // the following four routines just a syntactic sugar // although some rounding may be forced as a result - setFloatVal:(float)this { return [self setDoubleVal :(double)this]; } - (float)floatVal { return (float)[self doubleVal]; } - setIntVal:(int)this { return [self setDoubleVal :(double)this]; } - (int)intVal { return (int)[self doubleVal]; } - setBooleanVal:(BOOL)this { if (this) [self setString:"t" size:(sizeof("t") - 1)]; else [self setString:"" size:(sizeof("") - 1)]; return self; } - (BOOL)booleanVal { [self string]; // force conversion if needed return (len > 1); // i.e. "" string and NULL give false, other // strings need more storage than one char. } - (BOOL)isCanonicallyTrue { const char *this = [self string]; return ((len == 2) && ('t' == *this)); } - (BOOL)isCanonicallyFalse { [self string]; return (len <= 1); } // who really use this? should not this be private? - (int)value_state { return value_flags; } - (int) state { if (STORED_STRING(value_flags)) return COWSSTRINGNODE_STORED_STRING; if (STORED_NUMBER(value_flags)) return COWSSTRINGNODE_STORED_NUMBER; return 0; } - (size_t) allocated // this IS private, do not advertise { return len; } - copyValue:(COWSStringNode *) from_this { int vflags; size_t vlen; vflags = [from_this value_state]; if (STORED_STRING(vflags)) { vlen = [from_this allocated]; [self setString:[from_this string] size:(vlen - 1)]; } if (STORED_NUMBER(vflags)) { double_value = [from_this doubleVal]; } value_flags = vflags; return self; } #if 0 // attach string 'this' to a value stored in some COWSStringNode; // forces string representation as a side effect - (const char *)catString :(const char *)this { size_t newlen, start; char *old_string = (char *)[self string]; if ((NULL == this) || ('\0' == *this)) { return old_string; } newlen = strlen(this); if (NULL == old_string) { newlen += 1; old_string = malloc(newlen + 1); start = 0; } else { start = len; newlen += start; /* increase by a length of a storage for the previous string plus a space for '\0' */ start -= 1; old_string = realloc(old_string, newlen); } if (NULL != old_string) string = strcpy(old_string + start, this); else newlen = 0; len = newlen; // whatever number was stored here is is now invalid SN_CLEAR(value_flags, COWSSTRINGNODE_STORED_NUMBER); return (const char*)string; } // Add 'this' to a value n some COWSStringNode; // forces floating point representation as a side effect - (double)addToVal :(double)this { double old_val = [self doubleVal]; if (0.0 == this) { return old_val; } double_value = old_val + this; SN_CLEAR(value_flags, COWSSTRINGNODE_STORED_STRING); return double_value; } #endif /* 0 */ - setError:(BOOL)true_or_false { if (true_or_false) { SN_SET(value_flags, COWSSTRINGNODE_STORED_ERROR); } else { SN_CLEAR(value_flags, COWSSTRINGNODE_STORED_ERROR); } return self; } - (BOOL)error { return (SN_CHECK(value_flags, COWSSTRINGNODE_STORED_ERROR) ? 1 : 0); } // debugging - printContents { printf("\t#%s#\n", [self string]); // force conversion if needed return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.