mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-25 17:51:01 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@4853 72102866-910b-0410-8b05-ffd578937521
421 lines
11 KiB
Objective-C
421 lines
11 KiB
Objective-C
/* Implementation of GNU Objective-C text stream object for use serializing
|
||
Copyright (C) 1996 Free Software Foundation, Inc.
|
||
|
||
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
||
Written: Jan 1996
|
||
|
||
This file is part of the GNUstep Base Library.
|
||
|
||
This library is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU Library General Public
|
||
License as published by the Free Software Foundation; either
|
||
version 2 of the License, or (at your option) any later version.
|
||
|
||
This library is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
Library General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Library General Public
|
||
License along with this library; if not, write to the Free
|
||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
|
||
*/
|
||
|
||
#include <config.h>
|
||
#include <base/preface.h>
|
||
#include <base/TextCStream.h>
|
||
#include <base/NSString.h>
|
||
#include <base/StdioStream.h>
|
||
#include <Foundation/NSException.h>
|
||
|
||
#define DEFAULT_FORMAT_VERSION 0
|
||
|
||
static BOOL debug_textcoder = NO;
|
||
|
||
@implementation TextCStream
|
||
|
||
|
||
/* Encoding/decoding C values */
|
||
|
||
#define XSTR(s) STR(s)
|
||
#define STR(s) #s
|
||
|
||
#define ATXSTR(s) ATSTR(s)
|
||
#define ATSTR(s) @#s
|
||
|
||
#define ROUND(V, A) \
|
||
({ typeof(V) __v=(V); typeof(A) __a=(A); \
|
||
__a*((__v+__a-1)/__a); })
|
||
|
||
#define ENCODER_FORMAT(TYPE, CONVERSION) \
|
||
@"%*s<%s> (" ATXSTR(TYPE) @") = %" ATXSTR(CONVERSION) @"\n"
|
||
|
||
- (void) encodeValueOfCType: (const char*) type
|
||
at: (const void*) d
|
||
withName: (NSString*) name;
|
||
{
|
||
if (!type)
|
||
[NSException raise:NSInvalidArgumentException format:@"type is NULL"];
|
||
|
||
NSAssert(*type != '@', @"tried to encode an \"ObjC\" type");
|
||
NSAssert(*type != '^', @"tried to encode an \"ObjC\" type");
|
||
NSAssert(*type != ':', @"tried to encode an \"ObjC\" type");
|
||
|
||
if (!name || [name length] == 0)
|
||
name = @"Anonymous";
|
||
switch (*type)
|
||
{
|
||
case _C_LNG:
|
||
[stream writeFormat:@"%*s<%s> (long) = %ld\n",
|
||
indentation, "", [name cString], *(long*)d];
|
||
break;
|
||
case _C_ULNG:
|
||
[stream writeFormat:@"%*s<%s> (unsigned long) = %lu\n",
|
||
indentation, "", [name cString], *(unsigned long*)d];
|
||
break;
|
||
case _C_INT:
|
||
[stream writeFormat:@"%*s<%s> (int) = %d\n",
|
||
indentation, "", [name cString], *(int*)d];
|
||
break;
|
||
case _C_UINT:
|
||
[stream writeFormat:@"%*s<%s> (unsigned int) = %u\n",
|
||
indentation, "", [name cString], *(unsigned int*)d];
|
||
break;
|
||
case _C_SHT:
|
||
[stream writeFormat:@"%*s<%s> (short) = %d\n",
|
||
indentation, "", [name cString], (int)*(short*)d];
|
||
break;
|
||
case _C_USHT:
|
||
[stream writeFormat:@"%*s<%s> (unsigned short) = %u\n",
|
||
indentation, "", [name cString],
|
||
(unsigned)*(unsigned short*)d];
|
||
break;
|
||
case _C_CHR:
|
||
[stream writeFormat:@"%*s<%s> (char) = %c (0x%x)\n",
|
||
indentation, "", [name cString],
|
||
*(char*)d, (unsigned)*(char*)d];
|
||
break;
|
||
case _C_UCHR:
|
||
[stream writeFormat:@"%*s<%s> (unsigned char) = 0x%x\n",
|
||
indentation, "", [name cString],
|
||
(unsigned)*(unsigned char*)d];
|
||
break;
|
||
case _C_FLT:
|
||
[stream writeFormat:@"%*s<%s> (float) = %g\n",
|
||
indentation, "", [name cString], *(float*)d];
|
||
break;
|
||
case _C_DBL:
|
||
[stream writeFormat:@"%*s<%s> (double) = %g\n",
|
||
indentation, "", [name cString], *(double*)d];
|
||
break;
|
||
case _C_CHARPTR:
|
||
[stream writeFormat:@"%*s<%s> (char*) = \"%s\"\n",
|
||
indentation, "", [name cString], *(char**)d];
|
||
break;
|
||
case _C_ARY_B:
|
||
{
|
||
int len = atoi (type+1); /* xxx why +1 ? */
|
||
int offset;
|
||
|
||
while (isdigit(*++type));
|
||
offset = objc_sizeof_type(type);
|
||
[self encodeName:name];
|
||
[self encodeIndent];
|
||
while (len-- > 0)
|
||
{
|
||
/* Change this so we don't re-write type info every time. */
|
||
[self encodeValueOfCType:type
|
||
at:d
|
||
withName:@"array component"];
|
||
((char*)d) += offset;
|
||
}
|
||
[self encodeUnindent];
|
||
break;
|
||
}
|
||
case _C_STRUCT_B:
|
||
{
|
||
int acc_size = 0;
|
||
int align;
|
||
|
||
while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */
|
||
[self encodeName:name];
|
||
[self encodeIndent];
|
||
while (*type != _C_STRUCT_E)
|
||
{
|
||
align = objc_alignof_type (type); /* pad to alignment */
|
||
acc_size = ROUND (acc_size, align);
|
||
[self encodeValueOfCType:type
|
||
at:((char*)d)+acc_size
|
||
withName:@"structure component"];
|
||
acc_size += objc_sizeof_type (type); /* add component size */
|
||
type = objc_skip_typespec (type); /* skip component */
|
||
}
|
||
[self encodeUnindent];
|
||
break;
|
||
}
|
||
case _C_PTR:
|
||
[NSException raise: NSGenericException
|
||
format: @"Cannot encode pointers"];
|
||
break;
|
||
#if 0 /* No, don't know how far to recurse */
|
||
[self encodeValueOfObjCType:type+1 at:*(char**)d withName:name];
|
||
break;
|
||
#endif
|
||
default:
|
||
[NSException raise: NSGenericException
|
||
format: @"type %s not implemented", type];
|
||
}
|
||
}
|
||
|
||
#define DECODER_FORMAT(TYPE, CONVERSION) \
|
||
@" <%a[^>]> (" ATXSTR(TYPE) @") = %" ATXSTR(CONVERSION) @" \n"
|
||
|
||
#define DECODE_ERROR(TYPE) \
|
||
{ \
|
||
id line = [stream readLine]; \
|
||
[NSException raise: NSGenericException \
|
||
format: @"bad format decoding " ATXSTR(TYPE) @".\n" \
|
||
@"Looking at %s\n.", \
|
||
[line cString]]; \
|
||
}
|
||
|
||
|
||
#define DECODE_DEBUG(TYPE, CONVERSION) \
|
||
if (debug_textcoder) \
|
||
[[StdioStream standardError] writeFormat: \
|
||
@"got <%s> (%s) %" ATXSTR(CONVERSION) @"\n", \
|
||
tmpname, \
|
||
XSTR(TYPE), *(TYPE*)d];
|
||
|
||
- (void) decodeValueOfCType: (const char*) type
|
||
at: (void*) d
|
||
withName: (NSString* *) namePtr;
|
||
{
|
||
char *tmpname;
|
||
|
||
if (!type)
|
||
[NSException raise:NSInvalidArgumentException format:@"type is NULL"];
|
||
if (!d)
|
||
[NSException raise:NSInvalidArgumentException format:@"d is NULL"];
|
||
|
||
NSAssert(*type != '@', @"tried to decode an \"ObjC\" type");
|
||
NSAssert(*type != '^', @"tried to decode an \"ObjC\" type");
|
||
NSAssert(*type != ':', @"tried to decode an \"ObjC\" type");
|
||
|
||
switch (*type)
|
||
{
|
||
case _C_LNG:
|
||
if ([stream readFormat: DECODER_FORMAT(long,l),
|
||
&tmpname, (long*)d] != 2)
|
||
DECODE_ERROR(long);
|
||
DECODE_DEBUG(long, l);
|
||
break;
|
||
case _C_ULNG:
|
||
if ([stream readFormat:DECODER_FORMAT(unsigned long, lu),
|
||
&tmpname, (unsigned long*)d] != 2)
|
||
DECODE_ERROR(unsigned long);
|
||
DECODE_DEBUG(unsigned long, lu);
|
||
break;
|
||
case _C_INT:
|
||
if ([stream readFormat:DECODER_FORMAT(int, d),
|
||
&tmpname, (int*)d] != 2)
|
||
DECODE_ERROR(int);
|
||
DECODE_DEBUG(int, d);
|
||
break;
|
||
case _C_UINT:
|
||
if ([stream readFormat:DECODER_FORMAT(unsigned int,u),
|
||
&tmpname, (unsigned int*)d] != 2)
|
||
DECODE_ERROR(unsigned int);
|
||
DECODE_DEBUG(unsigned int, u);
|
||
break;
|
||
case _C_SHT:
|
||
if ([stream readFormat:DECODER_FORMAT(short,hd),
|
||
&tmpname, (short*)d] != 2)
|
||
DECODE_ERROR(short);
|
||
DECODE_DEBUG(short, d);
|
||
break;
|
||
case _C_USHT:
|
||
if ([stream readFormat:DECODER_FORMAT(unsigned short,hu),
|
||
&tmpname, (unsigned short*)d] != 2)
|
||
DECODE_ERROR(unsigned short);
|
||
DECODE_DEBUG(unsigned short, u);
|
||
break;
|
||
case _C_CHR:
|
||
{
|
||
unsigned tmp;
|
||
if ([stream readFormat:@" <%a[^>]> (char) = %*c (%x) \n",
|
||
&tmpname, &tmp] != 2)
|
||
DECODE_ERROR(char);
|
||
*(char*)d = (char)tmp;
|
||
DECODE_DEBUG(char, c);
|
||
break;
|
||
}
|
||
case _C_UCHR:
|
||
{
|
||
unsigned tmp;
|
||
if ([stream readFormat:DECODER_FORMAT(unsigned char,x),
|
||
&tmpname, &tmp] != 2)
|
||
DECODE_ERROR(unsigned char);
|
||
*(unsigned char*)d = (unsigned char)tmp;
|
||
DECODE_DEBUG(unsigned char, c);
|
||
break;
|
||
}
|
||
case _C_FLT:
|
||
if ([stream readFormat:DECODER_FORMAT(float,f),
|
||
&tmpname, (float*)d] != 2)
|
||
DECODE_ERROR(float);
|
||
DECODE_DEBUG(float, f);
|
||
break;
|
||
case _C_DBL:
|
||
if ([stream readFormat:DECODER_FORMAT(double,lf),
|
||
&tmpname, (double*)d] != 2)
|
||
DECODE_ERROR(double);
|
||
DECODE_DEBUG(double, f);
|
||
break;
|
||
case _C_CHARPTR:
|
||
if ([stream readFormat:@" <%a[^>]> (char*) = \"%a[^\"]\" \n",
|
||
&tmpname, (char**)d] != 2)
|
||
DECODE_ERROR(char*);
|
||
DECODE_DEBUG(char*, s);
|
||
break;
|
||
case _C_ARY_B:
|
||
{
|
||
/* xxx Do we need to allocate space, just like _C_CHARPTR ? */
|
||
int len = atoi(type+1);
|
||
int offset;
|
||
[self decodeName:namePtr];
|
||
[self decodeIndent];
|
||
while (isdigit(*++type));
|
||
offset = objc_sizeof_type(type);
|
||
while (len-- > 0)
|
||
{
|
||
[self decodeValueOfCType:type
|
||
at:d
|
||
withName:namePtr];
|
||
((char*)d) += offset;
|
||
}
|
||
[self decodeUnindent];
|
||
break;
|
||
}
|
||
case _C_STRUCT_B:
|
||
{
|
||
/* xxx Do we need to allocate space just like char* ? No. */
|
||
int acc_size = 0;
|
||
int align;
|
||
while (*type != _C_STRUCT_E && *type++ != '='); /* skip "<name>=" */
|
||
[self decodeName:namePtr];
|
||
[self decodeIndent]; /* xxx insert [self decodeName:] */
|
||
while (*type != _C_STRUCT_E)
|
||
{
|
||
align = objc_alignof_type (type); /* pad to alignment */
|
||
acc_size = ROUND (acc_size, align);
|
||
[self decodeValueOfCType:type
|
||
at:((char*)d)+acc_size
|
||
withName:namePtr];
|
||
acc_size += objc_sizeof_type (type); /* add component size */
|
||
type = objc_skip_typespec (type); /* skip component */
|
||
}
|
||
[self decodeUnindent];
|
||
break;
|
||
}
|
||
case _C_PTR:
|
||
[NSException raise: NSGenericException
|
||
format: @"Cannot decode pointers"];
|
||
break;
|
||
#if 0 /* No, don't know how far to recurse */
|
||
OBJC_MALLOC(*(void**)d, void*, 1);
|
||
[self decodeValueOfObjCType:type+1 at:*(char**)d withName:namePtr];
|
||
break;
|
||
#endif
|
||
default:
|
||
[NSException raise: NSGenericException
|
||
format: @"type %s not yet implemented", type];
|
||
}
|
||
if (namePtr)
|
||
*namePtr = [[[NSString alloc] initWithCStringNoCopy: tmpname
|
||
length: strlen (tmpname)
|
||
freeWhenDone: YES]
|
||
autorelease];
|
||
else
|
||
objc_free (tmpname);
|
||
}
|
||
|
||
|
||
/* Encoding/decoding indentation */
|
||
|
||
- (void) encodeIndent
|
||
{
|
||
[stream writeFormat: @"%*s {\n", indentation, ""];
|
||
indentation += 2;
|
||
}
|
||
|
||
- (void) encodeUnindent
|
||
{
|
||
indentation -= 2;
|
||
[stream writeFormat: @"%*s }\n", indentation, ""];
|
||
}
|
||
|
||
- (void) decodeIndent
|
||
{
|
||
id line;
|
||
const char *lp;
|
||
|
||
line = [stream readLine];
|
||
lp = [line cString];
|
||
while (*lp == ' ') lp++;
|
||
if (*lp != '{')
|
||
[NSException raise: NSGenericException
|
||
format: @"bad indent format, got \"%s\"", line];
|
||
}
|
||
|
||
- (void) decodeUnindent
|
||
{
|
||
id line;
|
||
const char *lp;
|
||
|
||
line = [stream readLine];
|
||
lp = [line cString];
|
||
while (*lp == ' ') lp++;
|
||
if (*lp != '}')
|
||
[NSException raise: NSGenericException
|
||
format: @"bad unindent format, got \"%s\"", line];
|
||
}
|
||
|
||
- (void) encodeName: (NSString*) n
|
||
{
|
||
if (n)
|
||
[stream writeFormat:@"%*s<%s>\n", indentation, "", [n cString]];
|
||
else
|
||
[stream writeFormat:@"%*s<NULL>\n", indentation, ""];
|
||
}
|
||
|
||
- (void) decodeName: (NSString* *) name
|
||
{
|
||
char *n;
|
||
if (name)
|
||
{
|
||
if ([stream readFormat: @" <%a[^>]> \n", &n] != 1)
|
||
[NSException raise: NSGenericException
|
||
format: @"bad format"];
|
||
*name = [[[NSString alloc] initWithCStringNoCopy: n
|
||
length: strlen (n)
|
||
freeWhenDone: YES]
|
||
autorelease];
|
||
if (debug_textcoder)
|
||
fprintf(stderr, "got name <%s>\n", n);
|
||
}
|
||
else
|
||
{
|
||
[stream readFormat: @" <%*[^>]> \n"];
|
||
}
|
||
}
|
||
|
||
|
||
/* Returning default format version. */
|
||
|
||
+ (int) defaultFormatVersion
|
||
{
|
||
return DEFAULT_FORMAT_VERSION;
|
||
}
|
||
|
||
@end
|