#import <RCString.h>
/*
    Copyright (C) 1992. Bruce Ediger.

      This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Library General Public License.
*/

@implementation RCString

// Initialize the internal string rep, but not the
// internal string itself.
// Assumes that "self" is already allocated.
- init
{
	[super init];
	if (p = (struct srep *) malloc(sizeof(struct srep))) {
		p->s = NULL;
		p->l = 0;
		p->n = 1;
	}
	yCaseSensitive = YES;
	return self;
}

// construct an object with zero-length string internal string rep
+ new
{
	RCString *oNew = [[RCString alloc] init];
	if (oNew) {
		oNew->p->s = malloc(1);
		*oNew->p->s = '\0';
		oNew->p->l = 1;
		oNew->p->n = 1;
	}
	return oNew;
}

// Construct an object surrounding a given ASCII string.
// Should this method make a copy of the ASCIIZ argument?
// Since the object is copy-on-write, we could maybe get
// away with just keeping around the pointer.  No.  Then
// anything could change the string out from underneath the
// object.
+ newFromString:(char *)aString
{
	RCString       *oNewString;
	if (aString) {
		oNewString = [[RCString alloc] init];
		if (oNewString) {
			oNewString->p->l = strlen(aString) + 1;
			if ((oNewString->p->s = malloc(oNewString->p->l))) {
				bcopy(aString, oNewString->p->s, oNewString->p->l);
				oNewString->p->n = 1;
			} else
				oNewString->p->l = 0;  // malloc() failed, retain consistency
		}
		oNewString->yCaseSensitive = YES;
	} else {		// null argument string 
		oNewString = [RCString new];
	}

	return oNewString;
}

// Factory Method copy constructor analog: an object that
// references another object's internal string rep.
+ newFromObject:(RCString *) oString
{
	RCString       *oNewString;
	if (oString && (oNewString = [RCString alloc])) {
		// RCString objects will share internal string rep until a write.
		oNewString->p = oString->p;
		oNewString->p->n++;
		oNewString->yCaseSensitive = oString->yCaseSensitive;
	} else {
		 // passed a null object pointer: fill in a zero-length
		 // internal string rep
		oNewString = [RCString new];
	}
	return oNewString;
}

// Copy constructor analog: an object that references this
// object's internal string rep.
- newFromObject
{
	RCString       *oNewString;
	if ((oNewString = [RCString alloc]))
	{
		// RCString objects will share internal string rep until a write.
		oNewString->p = p;
		oNewString->yCaseSensitive = yCaseSensitive;
		if (p) p->n++;
	}
	return oNewString;
}

+ newFilledWith: (int) bCharacter size: (int) iNumber
{
	RCString       *oNewString;
	char           *bpTmp;

	if (bCharacter && iNumber > 0 && (bpTmp = malloc(iNumber + 1))) {
		int icCount;
		for (icCount = 0; icCount < iNumber + 1; ++icCount)
			bpTmp[icCount] = (char )bCharacter;
		bpTmp[iNumber] = '\0';
		oNewString = [RCString newFromString:bpTmp];
	} else {
		// args specifiy null/zero-length string or malloc() failure
		oNewString = [RCString new];
	}

	return oNewString;
}

- free
{
	// should the test be "==" or "<=" ? 
	// <= would probably catch some mistakes.
	if (p && --p->n == 0) {
		// reference count's gone to zero, free internal string rep 
		if (p->s)
			free(p->s);
		free(p);
	}
	return [super free];
}

// private use method. 
// causes the RCString object that this message is sent to,
// to copy it's internal string representation. 
// typically called before modifying internal string rep 
// in order to provide copy-on-write behavior. 
- copyReference
{
	struct srep *psNewSrep = (struct srep *)malloc(sizeof(struct srep));
	if (psNewSrep) {
		if (p) {
			if (p->s) {
				psNewSrep->s = malloc(p->l);
				if (psNewSrep->s) {
					bcopy(p->s, psNewSrep->s, p->l);
					psNewSrep->l = p->l;
				}
			} else  {
				psNewSrep->s = NULL;
				psNewSrep->l = 0;
			}
			// check reference count on previous internal rep
			if (--p->n == 0) {
				free(p->s);
				free(p);
			}
			psNewSrep->n = 1;
			p = psNewSrep;
		} else {
			// Object didn't have an internal string rep for some
			// reason. Give it it's own NULL string 
			psNewSrep->s = NULL;
			psNewSrep->n = 1;
			psNewSrep->l = 0;
			p = psNewSrep;
		}
	}  // what if malloc() of struct srep fails?
	return self;
}

- (BOOL) isNull
{
	if (!p && (!p->s || p->l <= 1))	// ignore trailing ASCII NULL
		return YES;
	return NO;
}

// returns "strlen" of internal representation.
// that is, length without trailing NULL.
- (unsigned)length
{
	if (p && p->l)
		return(p->l - 1);
	return 0;
}

- (char *)data
{
	return p ? p->s : NULL;
}

- (int)references
{
	return p ? p->n : 0;
}


- (struct srep *)internal
{
	return p;
}

@end

@implementation RCString(Archiving)

// Archiving methods borrowed from John Hassey's String
// object.  Try to maintain compatibility.
- storeOn: (int) aFd
{
#ifndef NeXT
  [super storeOn: aFd];
#endif

  if (write(aFd, &p->l, sizeof(p->l)) == -1)
    {
    [self error: "storeOn: write error"];
    }
  if (write(aFd, "\"", 1) == -1)
    {
    [self error: "storeOn: write error"];
    }
  if (write(aFd, [self data], [self length]) == -1)
    {
    [self error: "storeOn: write error"];
    }
  if (write(aFd, "\"", 1) == -1)
    {
    [self error: "storeOn: write error"];
    }
  return self;
}

- readFrom: (int) aFd
{
  char c;

#ifndef NeXT
  [super readFrom: aFd];
#endif

  if (p == NULL) {
	p = (struct srep *)malloc(sizeof(struct srep));
  } else {
	if (p->n == 1)
		if (p->s) free(p->s);
	else {
		// Come up with a new internal rep struct.  It
		// will be completely redone anyway.
		--p->n;
		p = (struct srep *)malloc(sizeof(struct srep));
	}
  }

  if (read(aFd, &p->l, sizeof(p->l)) == -1)
    [self error: "readFrom: read error"];
  p->s = malloc(p->l);
  if (read(aFd, &c, 1) == -1)
      [self error: "readFrom: read error"];
  if (c != '"')
    [self error: "readFrom: format error"];
  if (read(aFd, p->s, p->l - 1) == -1)
    [self error: "readFrom: read error"];
  if (read(aFd, &c, 1) == -1)
      [self error: "readFrom: read error"];
  if (c != '"')
    [self error: "readFrom: format error"];

  // null terminate string

  p->s[p->l - 1] = 0;

  return self;
}

#ifdef NeXT
- write:(NXTypedStream *)aStream
{
	[super write:aStream];
	if (p)
	{
		int iFakeN = 1;
		NXWriteType(aStream, "i", &p->l);  // length first, so read: can
											// allocate memory for string.
		if (p->s)
			NXWriteType(aStream, "*", &p->s);
		else {
			char *bpTmp = "";
			NXWriteType(aStream, "*", &bpTmp);
		}
		NXWriteType(aStream, "i", &iFakeN);
		NXWriteType(aStream, "c", &yCaseSensitive);
	} else {
		// Fake the whole internal rep as a zero-length string.
		char *bpTmp = "";
		int   iTmpL = 1;
		int   iTmpN = 1;
		BOOL  yTmpCS = TRUE;
		NXWriteType(aStream, "i", &iTmpL);
		NXWriteType(aStream, "*", &bpTmp);
		NXWriteType(aStream, "i", &iTmpN);
		NXWriteType(aStream, "c", &yTmpCS);
	}
	
	return self;
}

- read:(NXTypedStream *)aStream
{
	[super read:aStream];

	// prepare internal string rep
	if (p) {
		if (p->n == 1) {
			if (p->s) free(p->s);
		} else {
			--p->n;
			p = (struct srep *)malloc(sizeof(struct srep));
		}
	} else {
		p = (struct srep *)malloc(sizeof(struct srep));
	}
	NXReadType(aStream, "i", (void *)&p->l);
	NXReadType(aStream, "*", (void *)&p->s);
	NXReadType(aStream, "i", (void *)&p->n);
	if (p->n != 1) [self error:"read: problem, ref count not 1"];
	NXReadType(aStream, "c", (void *)&yCaseSensitive);
	return self;
}
#endif
@end

