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.