| /* GNU Objective C Runtime archiving |
| Copyright (C) 1993, 1995, 1996, 1997, 2002 Free Software Foundation, Inc. |
| Contributed by Kresten Krab Thorup |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under the |
| terms of the GNU General Public License as published by the Free Software |
| Foundation; either version 2, or (at your option) any later version. |
| |
| GCC 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 General Public License for more |
| details. |
| |
| You should have received a copy of the GNU General Public License along with |
| GCC; see the file COPYING. If not, write to the Free Software |
| Foundation, 59 Temple Place - Suite 330, |
| Boston, MA 02111-1307, USA. */ |
| |
| /* As a special exception, if you link this library with files compiled with |
| GCC to produce an executable, this does not cause the resulting executable |
| to be covered by the GNU General Public License. This exception does not |
| however invalidate any other reasons why the executable file might be |
| covered by the GNU General Public License. */ |
| |
| #include "tconfig.h" |
| #include "runtime.h" |
| #include "typedstream.h" |
| #include "encoding.h" |
| #include <stdlib.h> |
| |
| extern int fflush (FILE *); |
| |
| #define ROUND(V, A) \ |
| ({ typeof (V) __v = (V); typeof (A) __a = (A); \ |
| __a * ((__v + __a - 1)/__a); }) |
| |
| #define PTR2LONG(P) (((char *) (P))-(char *) 0) |
| #define LONG2PTR(L) (((char *) 0) + (L)) |
| |
| /* Declare some functions... */ |
| |
| static int |
| objc_read_class (struct objc_typed_stream *stream, Class *class); |
| |
| int objc_sizeof_type (const char *type); |
| |
| static int |
| objc_write_use_common (struct objc_typed_stream *stream, unsigned long key); |
| |
| static int |
| objc_write_register_common (struct objc_typed_stream *stream, |
| unsigned long key); |
| |
| static int |
| objc_write_class (struct objc_typed_stream *stream, |
| struct objc_class *class); |
| |
| const char *objc_skip_type (const char *type); |
| |
| static void __objc_finish_write_root_object (struct objc_typed_stream *); |
| static void __objc_finish_read_root_object (struct objc_typed_stream *); |
| |
| static __inline__ int |
| __objc_code_unsigned_char (unsigned char *buf, unsigned char val) |
| { |
| if ((val&_B_VALUE) == val) |
| { |
| buf[0] = val|_B_SINT; |
| return 1; |
| } |
| else |
| { |
| buf[0] = _B_NINT|0x01; |
| buf[1] = val; |
| return 2; |
| } |
| } |
| |
| int |
| objc_write_unsigned_char (struct objc_typed_stream *stream, |
| unsigned char value) |
| { |
| unsigned char buf[sizeof (unsigned char) + 1]; |
| int len = __objc_code_unsigned_char (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| static __inline__ int |
| __objc_code_char (unsigned char *buf, signed char val) |
| { |
| if (val >= 0) |
| return __objc_code_unsigned_char (buf, val); |
| else |
| { |
| buf[0] = _B_NINT|_B_SIGN|0x01; |
| buf[1] = -val; |
| return 2; |
| } |
| } |
| |
| int |
| objc_write_char (struct objc_typed_stream *stream, signed char value) |
| { |
| unsigned char buf[sizeof (char) + 1]; |
| int len = __objc_code_char (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| static __inline__ int |
| __objc_code_unsigned_short (unsigned char *buf, unsigned short val) |
| { |
| if ((val&_B_VALUE) == val) |
| { |
| buf[0] = val|_B_SINT; |
| return 1; |
| } |
| else |
| { |
| int c, b; |
| |
| buf[0] = _B_NINT; |
| |
| for (c = sizeof (short); c != 0; c -= 1) |
| if (((val >> (8*(c - 1)))%0x100) != 0) |
| break; |
| |
| buf[0] |= c; |
| |
| for (b = 1; c != 0; c--, b++) |
| { |
| buf[b] = (val >> (8*(c - 1)))%0x100; |
| } |
| |
| return b; |
| } |
| } |
| |
| int |
| objc_write_unsigned_short (struct objc_typed_stream *stream, |
| unsigned short value) |
| { |
| unsigned char buf[sizeof (unsigned short) + 1]; |
| int len = __objc_code_unsigned_short (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| static __inline__ int |
| __objc_code_short (unsigned char *buf, short val) |
| { |
| int sign = (val < 0); |
| int size = __objc_code_unsigned_short (buf, sign ? -val : val); |
| if (sign) |
| buf[0] |= _B_SIGN; |
| return size; |
| } |
| |
| int |
| objc_write_short (struct objc_typed_stream *stream, short value) |
| { |
| unsigned char buf[sizeof (short) + 1]; |
| int len = __objc_code_short (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| |
| static __inline__ int |
| __objc_code_unsigned_int (unsigned char *buf, unsigned int val) |
| { |
| if ((val&_B_VALUE) == val) |
| { |
| buf[0] = val|_B_SINT; |
| return 1; |
| } |
| else |
| { |
| int c, b; |
| |
| buf[0] = _B_NINT; |
| |
| for (c = sizeof (int); c != 0; c -= 1) |
| if (((val >> (8*(c - 1)))%0x100) != 0) |
| break; |
| |
| buf[0] |= c; |
| |
| for (b = 1; c != 0; c--, b++) |
| { |
| buf[b] = (val >> (8*(c-1)))%0x100; |
| } |
| |
| return b; |
| } |
| } |
| |
| int |
| objc_write_unsigned_int (struct objc_typed_stream *stream, unsigned int value) |
| { |
| unsigned char buf[sizeof (unsigned int) + 1]; |
| int len = __objc_code_unsigned_int (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| static __inline__ int |
| __objc_code_int (unsigned char *buf, int val) |
| { |
| int sign = (val < 0); |
| int size = __objc_code_unsigned_int (buf, sign ? -val : val); |
| if (sign) |
| buf[0] |= _B_SIGN; |
| return size; |
| } |
| |
| int |
| objc_write_int (struct objc_typed_stream *stream, int value) |
| { |
| unsigned char buf[sizeof (int) + 1]; |
| int len = __objc_code_int (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| static __inline__ int |
| __objc_code_unsigned_long (unsigned char *buf, unsigned long val) |
| { |
| if ((val&_B_VALUE) == val) |
| { |
| buf[0] = val|_B_SINT; |
| return 1; |
| } |
| else |
| { |
| int c, b; |
| |
| buf[0] = _B_NINT; |
| |
| for (c = sizeof (long); c != 0; c -= 1) |
| if (((val >> (8*(c - 1)))%0x100) != 0) |
| break; |
| |
| buf[0] |= c; |
| |
| for (b = 1; c != 0; c--, b++) |
| { |
| buf[b] = (val >> (8*(c - 1)))%0x100; |
| } |
| |
| return b; |
| } |
| } |
| |
| int |
| objc_write_unsigned_long (struct objc_typed_stream *stream, |
| unsigned long value) |
| { |
| unsigned char buf[sizeof (unsigned long) + 1]; |
| int len = __objc_code_unsigned_long (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| static __inline__ int |
| __objc_code_long (unsigned char *buf, long val) |
| { |
| int sign = (val < 0); |
| int size = __objc_code_unsigned_long (buf, sign ? -val : val); |
| if (sign) |
| buf[0] |= _B_SIGN; |
| return size; |
| } |
| |
| int |
| objc_write_long (struct objc_typed_stream *stream, long value) |
| { |
| unsigned char buf[sizeof (long) + 1]; |
| int len = __objc_code_long (buf, value); |
| return (*stream->write) (stream->physical, buf, len); |
| } |
| |
| |
| int |
| objc_write_string (struct objc_typed_stream *stream, |
| const unsigned char *string, unsigned int nbytes) |
| { |
| unsigned char buf[sizeof (unsigned int) + 1]; |
| int len = __objc_code_unsigned_int (buf, nbytes); |
| |
| if ((buf[0]&_B_CODE) == _B_SINT) |
| buf[0] = (buf[0]&_B_VALUE)|_B_SSTR; |
| |
| else /* _B_NINT */ |
| buf[0] = (buf[0]&_B_VALUE)|_B_NSTR; |
| |
| if ((*stream->write) (stream->physical, buf, len) != 0) |
| return (*stream->write) (stream->physical, string, nbytes); |
| else |
| return 0; |
| } |
| |
| int |
| objc_write_string_atomic (struct objc_typed_stream *stream, |
| unsigned char *string, unsigned int nbytes) |
| { |
| unsigned long key; |
| if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, string)))) |
| return objc_write_use_common (stream, key); |
| else |
| { |
| int length; |
| hash_add (&stream->stream_table, LONG2PTR(key=PTR2LONG(string)), string); |
| if ((length = objc_write_register_common (stream, key))) |
| return objc_write_string (stream, string, nbytes); |
| return length; |
| } |
| } |
| |
| static int |
| objc_write_register_common (struct objc_typed_stream *stream, |
| unsigned long key) |
| { |
| unsigned char buf[sizeof (unsigned long)+2]; |
| int len = __objc_code_unsigned_long (buf + 1, key); |
| if (len == 1) |
| { |
| buf[0] = _B_RCOMM|0x01; |
| buf[1] &= _B_VALUE; |
| return (*stream->write) (stream->physical, buf, len + 1); |
| } |
| else |
| { |
| buf[1] = (buf[1]&_B_VALUE)|_B_RCOMM; |
| return (*stream->write) (stream->physical, buf + 1, len); |
| } |
| } |
| |
| static int |
| objc_write_use_common (struct objc_typed_stream *stream, unsigned long key) |
| { |
| unsigned char buf[sizeof (unsigned long)+2]; |
| int len = __objc_code_unsigned_long (buf + 1, key); |
| if (len == 1) |
| { |
| buf[0] = _B_UCOMM|0x01; |
| buf[1] &= _B_VALUE; |
| return (*stream->write) (stream->physical, buf, 2); |
| } |
| else |
| { |
| buf[1] = (buf[1]&_B_VALUE)|_B_UCOMM; |
| return (*stream->write) (stream->physical, buf + 1, len); |
| } |
| } |
| |
| static __inline__ int |
| __objc_write_extension (struct objc_typed_stream *stream, unsigned char code) |
| { |
| if (code <= _B_VALUE) |
| { |
| unsigned char buf = code|_B_EXT; |
| return (*stream->write) (stream->physical, &buf, 1); |
| } |
| else |
| { |
| objc_error (nil, OBJC_ERR_BAD_OPCODE, |
| "__objc_write_extension: bad opcode %c\n", code); |
| return -1; |
| } |
| } |
| |
| __inline__ int |
| __objc_write_object (struct objc_typed_stream *stream, id object) |
| { |
| unsigned char buf = '\0'; |
| SEL write_sel = sel_get_any_uid ("write:"); |
| if (object) |
| { |
| __objc_write_extension (stream, _BX_OBJECT); |
| objc_write_class (stream, object->class_pointer); |
| (*objc_msg_lookup (object, write_sel)) (object, write_sel, stream); |
| return (*stream->write) (stream->physical, &buf, 1); |
| } |
| else |
| return objc_write_use_common (stream, 0); |
| } |
| |
| int |
| objc_write_object_reference (struct objc_typed_stream *stream, id object) |
| { |
| unsigned long key; |
| if ((key = PTR2LONG(hash_value_for_key (stream->object_table, object)))) |
| return objc_write_use_common (stream, key); |
| |
| __objc_write_extension (stream, _BX_OBJREF); |
| return objc_write_unsigned_long (stream, PTR2LONG (object)); |
| } |
| |
| int |
| objc_write_root_object (struct objc_typed_stream *stream, id object) |
| { |
| int len = 0; |
| if (stream->writing_root_p) |
| objc_error (nil, OBJC_ERR_RECURSE_ROOT, |
| "objc_write_root_object called recursively"); |
| else |
| { |
| stream->writing_root_p = 1; |
| __objc_write_extension (stream, _BX_OBJROOT); |
| if ((len = objc_write_object (stream, object))) |
| __objc_finish_write_root_object (stream); |
| stream->writing_root_p = 0; |
| } |
| return len; |
| } |
| |
| int |
| objc_write_object (struct objc_typed_stream *stream, id object) |
| { |
| unsigned long key; |
| if ((key = PTR2LONG(hash_value_for_key (stream->object_table, object)))) |
| return objc_write_use_common (stream, key); |
| |
| else if (object == nil) |
| return objc_write_use_common (stream, 0); |
| |
| else |
| { |
| int length; |
| hash_add (&stream->object_table, LONG2PTR(key=PTR2LONG(object)), object); |
| if ((length = objc_write_register_common (stream, key))) |
| return __objc_write_object (stream, object); |
| return length; |
| } |
| } |
| |
| __inline__ int |
| __objc_write_class (struct objc_typed_stream *stream, struct objc_class *class) |
| { |
| __objc_write_extension (stream, _BX_CLASS); |
| objc_write_string_atomic (stream, (char *) class->name, |
| strlen ((char *) class->name)); |
| return objc_write_unsigned_long (stream, class->version); |
| } |
| |
| |
| static int |
| objc_write_class (struct objc_typed_stream *stream, |
| struct objc_class *class) |
| { |
| unsigned long key; |
| if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, class)))) |
| return objc_write_use_common (stream, key); |
| else |
| { |
| int length; |
| hash_add (&stream->stream_table, LONG2PTR(key = PTR2LONG(class)), class); |
| if ((length = objc_write_register_common (stream, key))) |
| return __objc_write_class (stream, class); |
| return length; |
| } |
| } |
| |
| |
| __inline__ int |
| __objc_write_selector (struct objc_typed_stream *stream, SEL selector) |
| { |
| const char *sel_name; |
| __objc_write_extension (stream, _BX_SEL); |
| /* to handle NULL selectors */ |
| if ((SEL)0 == selector) |
| return objc_write_string (stream, "", 0); |
| sel_name = sel_get_name (selector); |
| return objc_write_string (stream, sel_name, strlen ((char*)sel_name)); |
| } |
| |
| int |
| objc_write_selector (struct objc_typed_stream *stream, SEL selector) |
| { |
| const char *sel_name; |
| unsigned long key; |
| |
| /* to handle NULL selectors */ |
| if ((SEL)0 == selector) |
| return __objc_write_selector (stream, selector); |
| |
| sel_name = sel_get_name (selector); |
| if ((key = PTR2LONG(hash_value_for_key (stream->stream_table, sel_name)))) |
| return objc_write_use_common (stream, key); |
| else |
| { |
| int length; |
| hash_add (&stream->stream_table, |
| LONG2PTR(key = PTR2LONG(sel_name)), (char *) sel_name); |
| if ((length = objc_write_register_common (stream, key))) |
| return __objc_write_selector (stream, selector); |
| return length; |
| } |
| } |
| |
| |
| |
| /* |
| ** Read operations |
| */ |
| |
| __inline__ int |
| objc_read_char (struct objc_typed_stream *stream, char *val) |
| { |
| unsigned char buf; |
| int len; |
| len = (*stream->read) (stream->physical, &buf, 1); |
| if (len != 0) |
| { |
| if ((buf & _B_CODE) == _B_SINT) |
| (*val) = (buf & _B_VALUE); |
| |
| else if ((buf & _B_NUMBER) == 1) |
| { |
| len = (*stream->read) (stream->physical, val, 1); |
| if (buf&_B_SIGN) |
| (*val) = -1 * (*val); |
| } |
| |
| else |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected 8bit signed int, got %dbit int", |
| (int) (buf&_B_NUMBER)*8); |
| } |
| return len; |
| } |
| |
| |
| __inline__ int |
| objc_read_unsigned_char (struct objc_typed_stream *stream, unsigned char *val) |
| { |
| unsigned char buf; |
| int len; |
| if ((len = (*stream->read) (stream->physical, &buf, 1))) |
| { |
| if ((buf & _B_CODE) == _B_SINT) |
| (*val) = (buf & _B_VALUE); |
| |
| else if ((buf & _B_NUMBER) == 1) |
| len = (*stream->read) (stream->physical, val, 1); |
| |
| else |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected 8bit unsigned int, got %dbit int", |
| (int) (buf&_B_NUMBER)*8); |
| } |
| return len; |
| } |
| |
| __inline__ int |
| objc_read_short (struct objc_typed_stream *stream, short *value) |
| { |
| unsigned char buf[sizeof (short) + 1]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| if ((buf[0] & _B_CODE) == _B_SINT) |
| (*value) = (buf[0] & _B_VALUE); |
| |
| else |
| { |
| int pos = 1; |
| int nbytes = buf[0] & _B_NUMBER; |
| if (nbytes > (int) sizeof (short)) |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected short, got bigger (%dbits)", nbytes*8); |
| len = (*stream->read) (stream->physical, buf + 1, nbytes); |
| (*value) = 0; |
| while (pos <= nbytes) |
| (*value) = ((*value)*0x100) + buf[pos++]; |
| if (buf[0] & _B_SIGN) |
| (*value) = -(*value); |
| } |
| } |
| return len; |
| } |
| |
| __inline__ int |
| objc_read_unsigned_short (struct objc_typed_stream *stream, |
| unsigned short *value) |
| { |
| unsigned char buf[sizeof (unsigned short) + 1]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| if ((buf[0] & _B_CODE) == _B_SINT) |
| (*value) = (buf[0] & _B_VALUE); |
| |
| else |
| { |
| int pos = 1; |
| int nbytes = buf[0] & _B_NUMBER; |
| if (nbytes > (int) sizeof (short)) |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected short, got int or bigger"); |
| len = (*stream->read) (stream->physical, buf + 1, nbytes); |
| (*value) = 0; |
| while (pos <= nbytes) |
| (*value) = ((*value)*0x100) + buf[pos++]; |
| } |
| } |
| return len; |
| } |
| |
| |
| __inline__ int |
| objc_read_int (struct objc_typed_stream *stream, int *value) |
| { |
| unsigned char buf[sizeof (int) + 1]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| if ((buf[0] & _B_CODE) == _B_SINT) |
| (*value) = (buf[0] & _B_VALUE); |
| |
| else |
| { |
| int pos = 1; |
| int nbytes = buf[0] & _B_NUMBER; |
| if (nbytes > (int) sizeof (int)) |
| objc_error (nil, OBJC_ERR_BAD_DATA, "expected int, got bigger"); |
| len = (*stream->read) (stream->physical, buf + 1, nbytes); |
| (*value) = 0; |
| while (pos <= nbytes) |
| (*value) = ((*value)*0x100) + buf[pos++]; |
| if (buf[0] & _B_SIGN) |
| (*value) = -(*value); |
| } |
| } |
| return len; |
| } |
| |
| __inline__ int |
| objc_read_long (struct objc_typed_stream *stream, long *value) |
| { |
| unsigned char buf[sizeof (long) + 1]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| if ((buf[0] & _B_CODE) == _B_SINT) |
| (*value) = (buf[0] & _B_VALUE); |
| |
| else |
| { |
| int pos = 1; |
| int nbytes = buf[0] & _B_NUMBER; |
| if (nbytes > (int) sizeof (long)) |
| objc_error (nil, OBJC_ERR_BAD_DATA, "expected long, got bigger"); |
| len = (*stream->read) (stream->physical, buf + 1, nbytes); |
| (*value) = 0; |
| while (pos <= nbytes) |
| (*value) = ((*value)*0x100) + buf[pos++]; |
| if (buf[0] & _B_SIGN) |
| (*value) = -(*value); |
| } |
| } |
| return len; |
| } |
| |
| __inline__ int |
| __objc_read_nbyte_uint (struct objc_typed_stream *stream, |
| unsigned int nbytes, unsigned int *val) |
| { |
| int len; |
| unsigned int pos = 0; |
| unsigned char buf[sizeof (unsigned int) + 1]; |
| |
| if (nbytes > sizeof (int)) |
| objc_error (nil, OBJC_ERR_BAD_DATA, "expected int, got bigger"); |
| |
| len = (*stream->read) (stream->physical, buf, nbytes); |
| (*val) = 0; |
| while (pos < nbytes) |
| (*val) = ((*val)*0x100) + buf[pos++]; |
| return len; |
| } |
| |
| |
| __inline__ int |
| objc_read_unsigned_int (struct objc_typed_stream *stream, |
| unsigned int *value) |
| { |
| unsigned char buf[sizeof (unsigned int) + 1]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| if ((buf[0] & _B_CODE) == _B_SINT) |
| (*value) = (buf[0] & _B_VALUE); |
| |
| else |
| len = __objc_read_nbyte_uint (stream, (buf[0] & _B_VALUE), value); |
| |
| } |
| return len; |
| } |
| |
| int |
| __objc_read_nbyte_ulong (struct objc_typed_stream *stream, |
| unsigned int nbytes, unsigned long *val) |
| { |
| int len; |
| unsigned int pos = 0; |
| unsigned char buf[sizeof (unsigned long) + 1]; |
| |
| if (nbytes > sizeof (long)) |
| objc_error (nil, OBJC_ERR_BAD_DATA, "expected long, got bigger"); |
| |
| len = (*stream->read) (stream->physical, buf, nbytes); |
| (*val) = 0; |
| while (pos < nbytes) |
| (*val) = ((*val)*0x100) + buf[pos++]; |
| return len; |
| } |
| |
| |
| __inline__ int |
| objc_read_unsigned_long (struct objc_typed_stream *stream, |
| unsigned long *value) |
| { |
| unsigned char buf[sizeof (unsigned long) + 1]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| if ((buf[0] & _B_CODE) == _B_SINT) |
| (*value) = (buf[0] & _B_VALUE); |
| |
| else |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), value); |
| |
| } |
| return len; |
| } |
| |
| __inline__ int |
| objc_read_string (struct objc_typed_stream *stream, |
| char **string) |
| { |
| unsigned char buf[sizeof (unsigned int) + 1]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| unsigned long key = 0; |
| |
| if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ |
| { |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| len = (*stream->read) (stream->physical, buf, 1); |
| } |
| |
| switch (buf[0]&_B_CODE) { |
| case _B_SSTR: |
| { |
| int length = buf[0]&_B_VALUE; |
| (*string) = (char*)objc_malloc (length + 1); |
| if (key) |
| hash_add (&stream->stream_table, LONG2PTR(key), *string); |
| len = (*stream->read) (stream->physical, *string, length); |
| (*string)[length] = '\0'; |
| } |
| break; |
| |
| case _B_UCOMM: |
| { |
| char *tmp; |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| tmp = hash_value_for_key (stream->stream_table, LONG2PTR (key)); |
| *string = objc_malloc (strlen (tmp) + 1); |
| strcpy (*string, tmp); |
| } |
| break; |
| |
| case _B_NSTR: |
| { |
| unsigned int nbytes = buf[0]&_B_VALUE; |
| len = __objc_read_nbyte_uint (stream, nbytes, &nbytes); |
| if (len) { |
| (*string) = (char*)objc_malloc (nbytes + 1); |
| if (key) |
| hash_add (&stream->stream_table, LONG2PTR(key), *string); |
| len = (*stream->read) (stream->physical, *string, nbytes); |
| (*string)[nbytes] = '\0'; |
| } |
| } |
| break; |
| |
| default: |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected string, got opcode %c\n", (buf[0]&_B_CODE)); |
| } |
| } |
| |
| return len; |
| } |
| |
| |
| int |
| objc_read_object (struct objc_typed_stream *stream, id *object) |
| { |
| unsigned char buf[sizeof (unsigned int)]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| SEL read_sel = sel_get_any_uid ("read:"); |
| unsigned long key = 0; |
| |
| if ((buf[0]&_B_CODE) == _B_RCOMM) /* register common */ |
| { |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| len = (*stream->read) (stream->physical, buf, 1); |
| } |
| |
| if (buf[0] == (_B_EXT | _BX_OBJECT)) |
| { |
| Class class; |
| |
| /* get class */ |
| len = objc_read_class (stream, &class); |
| |
| /* create instance */ |
| (*object) = class_create_instance (class); |
| |
| /* register? */ |
| if (key) |
| hash_add (&stream->object_table, LONG2PTR(key), *object); |
| |
| /* send -read: */ |
| if (__objc_responds_to (*object, read_sel)) |
| (*get_imp (class, read_sel)) (*object, read_sel, stream); |
| |
| /* check null-byte */ |
| len = (*stream->read) (stream->physical, buf, 1); |
| if (buf[0] != '\0') |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected null-byte, got opcode %c", buf[0]); |
| } |
| |
| else if ((buf[0]&_B_CODE) == _B_UCOMM) |
| { |
| if (key) |
| objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| (*object) = hash_value_for_key (stream->object_table, LONG2PTR(key)); |
| } |
| |
| else if (buf[0] == (_B_EXT | _BX_OBJREF)) /* a forward reference */ |
| { |
| struct objc_list *other; |
| len = objc_read_unsigned_long (stream, &key); |
| other = (struct objc_list *) hash_value_for_key (stream->object_refs, |
| LONG2PTR(key)); |
| hash_add (&stream->object_refs, LONG2PTR(key), |
| (void *)list_cons (object, other)); |
| } |
| |
| else if (buf[0] == (_B_EXT | _BX_OBJROOT)) /* a root object */ |
| { |
| if (key) |
| objc_error (nil, OBJC_ERR_BAD_KEY, |
| "cannot register root object..."); |
| len = objc_read_object (stream, object); |
| __objc_finish_read_root_object (stream); |
| } |
| |
| else |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected object, got opcode %c", buf[0]); |
| } |
| return len; |
| } |
| |
| static int |
| objc_read_class (struct objc_typed_stream *stream, Class *class) |
| { |
| unsigned char buf[sizeof (unsigned int)]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| unsigned long key = 0; |
| |
| if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ |
| { |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| len = (*stream->read) (stream->physical, buf, 1); |
| } |
| |
| if (buf[0] == (_B_EXT | _BX_CLASS)) |
| { |
| char *class_name; |
| unsigned long version; |
| |
| /* get class */ |
| len = objc_read_string (stream, &class_name); |
| (*class) = objc_get_class (class_name); |
| objc_free (class_name); |
| |
| /* register */ |
| if (key) |
| hash_add (&stream->stream_table, LONG2PTR(key), *class); |
| |
| objc_read_unsigned_long (stream, &version); |
| hash_add (&stream->class_table, (*class)->name, (void *)version); |
| } |
| |
| else if ((buf[0]&_B_CODE) == _B_UCOMM) |
| { |
| if (key) |
| objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| *class = hash_value_for_key (stream->stream_table, LONG2PTR(key)); |
| if (! *class) |
| objc_error (nil, OBJC_ERR_BAD_CLASS, |
| "cannot find class for key %lu", key); |
| } |
| |
| else |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected class, got opcode %c", buf[0]); |
| } |
| return len; |
| } |
| |
| int |
| objc_read_selector (struct objc_typed_stream *stream, SEL* selector) |
| { |
| unsigned char buf[sizeof (unsigned int)]; |
| int len; |
| if ((len = (*stream->read) (stream->physical, buf, 1))) |
| { |
| unsigned long key = 0; |
| |
| if ((buf[0]&_B_CODE) == _B_RCOMM) /* register following */ |
| { |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| len = (*stream->read) (stream->physical, buf, 1); |
| } |
| |
| if (buf[0] == (_B_EXT|_BX_SEL)) /* selector! */ |
| { |
| char *selector_name; |
| |
| /* get selector */ |
| len = objc_read_string (stream, &selector_name); |
| /* To handle NULL selectors */ |
| if (0 == strlen (selector_name)) |
| { |
| (*selector) = (SEL)0; |
| return 0; |
| } |
| else |
| (*selector) = sel_get_any_uid (selector_name); |
| objc_free (selector_name); |
| |
| /* register */ |
| if (key) |
| hash_add (&stream->stream_table, LONG2PTR(key), (void *) *selector); |
| } |
| |
| else if ((buf[0]&_B_CODE) == _B_UCOMM) |
| { |
| if (key) |
| objc_error (nil, OBJC_ERR_BAD_KEY, "cannot register use upcode..."); |
| len = __objc_read_nbyte_ulong (stream, (buf[0] & _B_VALUE), &key); |
| (*selector) = hash_value_for_key (stream->stream_table, |
| LONG2PTR(key)); |
| } |
| |
| else |
| objc_error (nil, OBJC_ERR_BAD_DATA, |
| "expected selector, got opcode %c", buf[0]); |
| } |
| return len; |
| } |
| |
| /* |
| ** USER LEVEL FUNCTIONS |
| */ |
| |
| /* |
| ** Write one object, encoded in TYPE and pointed to by DATA to the |
| ** typed stream STREAM. |
| */ |
| |
| int |
| objc_write_type (TypedStream *stream, const char *type, const void *data) |
| { |
| switch (*type) { |
| case _C_ID: |
| return objc_write_object (stream, *(id *) data); |
| break; |
| |
| case _C_CLASS: |
| return objc_write_class (stream, *(Class *) data); |
| break; |
| |
| case _C_SEL: |
| return objc_write_selector (stream, *(SEL *) data); |
| break; |
| |
| case _C_CHR: |
| return objc_write_char (stream, *(signed char *) data); |
| break; |
| |
| case _C_UCHR: |
| return objc_write_unsigned_char (stream, *(unsigned char *) data); |
| break; |
| |
| case _C_SHT: |
| return objc_write_short (stream, *(short *) data); |
| break; |
| |
| case _C_USHT: |
| return objc_write_unsigned_short (stream, *(unsigned short *) data); |
| break; |
| |
| case _C_INT: |
| return objc_write_int (stream, *(int *) data); |
| break; |
| |
| case _C_UINT: |
| return objc_write_unsigned_int (stream, *(unsigned int *) data); |
| break; |
| |
| case _C_LNG: |
| return objc_write_long (stream, *(long *) data); |
| break; |
| |
| case _C_ULNG: |
| return objc_write_unsigned_long (stream, *(unsigned long *) data); |
| break; |
| |
| case _C_CHARPTR: |
| return objc_write_string (stream, |
| *(char **) data, strlen (*(char **) data)); |
| break; |
| |
| case _C_ATOM: |
| return objc_write_string_atomic (stream, *(char **) data, |
| strlen (*(char **) data)); |
| break; |
| |
| case _C_ARY_B: |
| { |
| int len = atoi (type + 1); |
| while (isdigit ((unsigned char) *++type)) |
| ; |
| return objc_write_array (stream, type, len, data); |
| } |
| break; |
| |
| case _C_STRUCT_B: |
| { |
| int acc_size = 0; |
| int align; |
| while (*type != _C_STRUCT_E && *type++ != '=') |
| ; /* skip "<name>=" */ |
| while (*type != _C_STRUCT_E) |
| { |
| align = objc_alignof_type (type); /* padd to alignment */ |
| acc_size += ROUND (acc_size, align); |
| objc_write_type (stream, type, ((char *) data) + acc_size); |
| acc_size += objc_sizeof_type (type); /* add component size */ |
| type = objc_skip_typespec (type); /* skip component */ |
| } |
| return 1; |
| } |
| |
| default: |
| { |
| objc_error (nil, OBJC_ERR_BAD_TYPE, |
| "objc_write_type: cannot parse typespec: %s\n", type); |
| return 0; |
| } |
| } |
| } |
| |
| /* |
| ** Read one object, encoded in TYPE and pointed to by DATA to the |
| ** typed stream STREAM. DATA specifies the address of the types to |
| ** read. Expected type is checked against the type actually present |
| ** on the stream. |
| */ |
| |
| int |
| objc_read_type(TypedStream *stream, const char *type, void *data) |
| { |
| char c; |
| switch (c = *type) { |
| case _C_ID: |
| return objc_read_object (stream, (id*)data); |
| break; |
| |
| case _C_CLASS: |
| return objc_read_class (stream, (Class*)data); |
| break; |
| |
| case _C_SEL: |
| return objc_read_selector (stream, (SEL*)data); |
| break; |
| |
| case _C_CHR: |
| return objc_read_char (stream, (char*)data); |
| break; |
| |
| case _C_UCHR: |
| return objc_read_unsigned_char (stream, (unsigned char*)data); |
| break; |
| |
| case _C_SHT: |
| return objc_read_short (stream, (short*)data); |
| break; |
| |
| case _C_USHT: |
| return objc_read_unsigned_short (stream, (unsigned short*)data); |
| break; |
| |
| case _C_INT: |
| return objc_read_int (stream, (int*)data); |
| break; |
| |
| case _C_UINT: |
| return objc_read_unsigned_int (stream, (unsigned int*)data); |
| break; |
| |
| case _C_LNG: |
| return objc_read_long (stream, (long*)data); |
| break; |
| |
| case _C_ULNG: |
| return objc_read_unsigned_long (stream, (unsigned long*)data); |
| break; |
| |
| case _C_CHARPTR: |
| case _C_ATOM: |
| return objc_read_string (stream, (char**)data); |
| break; |
| |
| case _C_ARY_B: |
| { |
| int len = atoi (type + 1); |
| while (isdigit ((unsigned char) *++type)) |
| ; |
| return objc_read_array (stream, type, len, data); |
| } |
| break; |
| |
| case _C_STRUCT_B: |
| { |
| int acc_size = 0; |
| int align; |
| while (*type != _C_STRUCT_E && *type++ != '=') |
| ; /* skip "<name>=" */ |
| while (*type != _C_STRUCT_E) |
| { |
| align = objc_alignof_type (type); /* padd to alignment */ |
| acc_size += ROUND (acc_size, align); |
| objc_read_type (stream, type, ((char*)data)+acc_size); |
| acc_size += objc_sizeof_type (type); /* add component size */ |
| type = objc_skip_typespec (type); /* skip component */ |
| } |
| return 1; |
| } |
| |
| default: |
| { |
| objc_error (nil, OBJC_ERR_BAD_TYPE, |
| "objc_read_type: cannot parse typespec: %s\n", type); |
| return 0; |
| } |
| } |
| } |
| |
| /* |
| ** Write the object specified by the template TYPE to STREAM. Last |
| ** arguments specify addresses of values to be written. It might |
| ** seem surprising to specify values by address, but this is extremely |
| ** convenient for copy-paste with objc_read_types calls. A more |
| ** down-to-the-earth cause for this passing of addresses is that values |
| ** of arbitrary size is not well supported in ANSI C for functions with |
| ** variable number of arguments. |
| */ |
| |
| int |
| objc_write_types (TypedStream *stream, const char *type, ...) |
| { |
| va_list args; |
| const char *c; |
| int res = 0; |
| |
| va_start(args, type); |
| |
| for (c = type; *c; c = objc_skip_typespec (c)) |
| { |
| switch (*c) { |
| case _C_ID: |
| res = objc_write_object (stream, *va_arg (args, id*)); |
| break; |
| |
| case _C_CLASS: |
| res = objc_write_class (stream, *va_arg (args, Class*)); |
| break; |
| |
| case _C_SEL: |
| res = objc_write_selector (stream, *va_arg (args, SEL*)); |
| break; |
| |
| case _C_CHR: |
| res = objc_write_char (stream, *va_arg (args, char*)); |
| break; |
| |
| case _C_UCHR: |
| res = objc_write_unsigned_char (stream, |
| *va_arg (args, unsigned char*)); |
| break; |
| |
| case _C_SHT: |
| res = objc_write_short (stream, *va_arg (args, short*)); |
| break; |
| |
| case _C_USHT: |
| res = objc_write_unsigned_short (stream, |
| *va_arg (args, unsigned short*)); |
| break; |
| |
| case _C_INT: |
| res = objc_write_int(stream, *va_arg (args, int*)); |
| break; |
| |
| case _C_UINT: |
| res = objc_write_unsigned_int(stream, *va_arg (args, unsigned int*)); |
| break; |
| |
| case _C_LNG: |
| res = objc_write_long(stream, *va_arg (args, long*)); |
| break; |
| |
| case _C_ULNG: |
| res = objc_write_unsigned_long(stream, *va_arg (args, unsigned long*)); |
| break; |
| |
| case _C_CHARPTR: |
| { |
| char **str = va_arg (args, char **); |
| res = objc_write_string (stream, *str, strlen (*str)); |
| } |
| break; |
| |
| case _C_ATOM: |
| { |
| char **str = va_arg (args, char **); |
| res = objc_write_string_atomic (stream, *str, strlen (*str)); |
| } |
| break; |
| |
| case _C_ARY_B: |
| { |
| int len = atoi (c + 1); |
| const char *t = c; |
| while (isdigit ((unsigned char) *++t)) |
| ; |
| res = objc_write_array (stream, t, len, va_arg (args, void *)); |
| t = objc_skip_typespec (t); |
| if (*t != _C_ARY_E) |
| objc_error (nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t); |
| } |
| break; |
| |
| default: |
| objc_error (nil, OBJC_ERR_BAD_TYPE, |
| "objc_write_types: cannot parse typespec: %s\n", type); |
| } |
| } |
| va_end(args); |
| return res; |
| } |
| |
| |
| /* |
| ** Last arguments specify addresses of values to be read. Expected |
| ** type is checked against the type actually present on the stream. |
| */ |
| |
| int |
| objc_read_types(TypedStream *stream, const char *type, ...) |
| { |
| va_list args; |
| const char *c; |
| int res = 0; |
| |
| va_start (args, type); |
| |
| for (c = type; *c; c = objc_skip_typespec(c)) |
| { |
| switch (*c) { |
| case _C_ID: |
| res = objc_read_object(stream, va_arg (args, id*)); |
| break; |
| |
| case _C_CLASS: |
| res = objc_read_class(stream, va_arg (args, Class*)); |
| break; |
| |
| case _C_SEL: |
| res = objc_read_selector(stream, va_arg (args, SEL*)); |
| break; |
| |
| case _C_CHR: |
| res = objc_read_char(stream, va_arg (args, char*)); |
| break; |
| |
| case _C_UCHR: |
| res = objc_read_unsigned_char(stream, va_arg (args, unsigned char*)); |
| break; |
| |
| case _C_SHT: |
| res = objc_read_short(stream, va_arg (args, short*)); |
| break; |
| |
| case _C_USHT: |
| res = objc_read_unsigned_short(stream, va_arg (args, unsigned short*)); |
| break; |
| |
| case _C_INT: |
| res = objc_read_int(stream, va_arg (args, int*)); |
| break; |
| |
| case _C_UINT: |
| res = objc_read_unsigned_int(stream, va_arg (args, unsigned int*)); |
| break; |
| |
| case _C_LNG: |
| res = objc_read_long(stream, va_arg (args, long*)); |
| break; |
| |
| case _C_ULNG: |
| res = objc_read_unsigned_long(stream, va_arg (args, unsigned long*)); |
| break; |
| |
| case _C_CHARPTR: |
| case _C_ATOM: |
| { |
| char **str = va_arg (args, char **); |
| res = objc_read_string (stream, str); |
| } |
| break; |
| |
| case _C_ARY_B: |
| { |
| int len = atoi (c + 1); |
| const char *t = c; |
| while (isdigit ((unsigned char) *++t)) |
| ; |
| res = objc_read_array (stream, t, len, va_arg (args, void *)); |
| t = objc_skip_typespec (t); |
| if (*t != _C_ARY_E) |
| objc_error (nil, OBJC_ERR_BAD_TYPE, "expected `]', got: %s", t); |
| } |
| break; |
| |
| default: |
| objc_error (nil, OBJC_ERR_BAD_TYPE, |
| "objc_read_types: cannot parse typespec: %s\n", type); |
| } |
| } |
| va_end (args); |
| return res; |
| } |
| |
| /* |
| ** Write an array of COUNT elements of TYPE from the memory address DATA. |
| ** This is equivalent of objc_write_type (stream, "[N<type>]", data) |
| */ |
| |
| int |
| objc_write_array (TypedStream *stream, const char *type, |
| int count, const void *data) |
| { |
| int off = objc_sizeof_type(type); |
| const char *where = data; |
| |
| while (count-- > 0) |
| { |
| objc_write_type(stream, type, where); |
| where += off; |
| } |
| return 1; |
| } |
| |
| /* |
| ** Read an array of COUNT elements of TYPE into the memory address |
| ** DATA. The memory pointed to by data is supposed to be allocated |
| ** by the callee. This is equivalent of |
| ** objc_read_type (stream, "[N<type>]", data) |
| */ |
| |
| int |
| objc_read_array (TypedStream *stream, const char *type, |
| int count, void *data) |
| { |
| int off = objc_sizeof_type(type); |
| char *where = (char*)data; |
| |
| while (count-- > 0) |
| { |
| objc_read_type(stream, type, where); |
| where += off; |
| } |
| return 1; |
| } |
| |
| static int |
| __objc_fread (FILE *file, char *data, int len) |
| { |
| return fread(data, len, 1, file); |
| } |
| |
| static int |
| __objc_fwrite (FILE *file, char *data, int len) |
| { |
| return fwrite(data, len, 1, file); |
| } |
| |
| static int |
| __objc_feof (FILE *file) |
| { |
| return feof(file); |
| } |
| |
| static int |
| __objc_no_write (FILE *file __attribute__ ((__unused__)), |
| const char *data __attribute__ ((__unused__)), |
| int len __attribute__ ((__unused__))) |
| { |
| objc_error (nil, OBJC_ERR_NO_WRITE, "TypedStream not open for writing"); |
| return 0; |
| } |
| |
| static int |
| __objc_no_read (FILE *file __attribute__ ((__unused__)), |
| const char *data __attribute__ ((__unused__)), |
| int len __attribute__ ((__unused__))) |
| { |
| objc_error (nil, OBJC_ERR_NO_READ, "TypedStream not open for reading"); |
| return 0; |
| } |
| |
| static int |
| __objc_read_typed_stream_signature (TypedStream *stream) |
| { |
| char buffer[80]; |
| int pos = 0; |
| do |
| (*stream->read) (stream->physical, buffer+pos, 1); |
| while (buffer[pos++] != '\0') |
| ; |
| sscanf (buffer, "GNU TypedStream %d", &stream->version); |
| if (stream->version != OBJC_TYPED_STREAM_VERSION) |
| objc_error (nil, OBJC_ERR_STREAM_VERSION, |
| "cannot handle TypedStream version %d", stream->version); |
| return 1; |
| } |
| |
| static int |
| __objc_write_typed_stream_signature (TypedStream *stream) |
| { |
| char buffer[80]; |
| sprintf(buffer, "GNU TypedStream %d", OBJC_TYPED_STREAM_VERSION); |
| stream->version = OBJC_TYPED_STREAM_VERSION; |
| (*stream->write) (stream->physical, buffer, strlen (buffer) + 1); |
| return 1; |
| } |
| |
| static void __objc_finish_write_root_object(struct objc_typed_stream *stream) |
| { |
| hash_delete (stream->object_table); |
| stream->object_table = hash_new(64, |
| (hash_func_type)hash_ptr, |
| (compare_func_type)compare_ptrs); |
| } |
| |
| static void __objc_finish_read_root_object(struct objc_typed_stream *stream) |
| { |
| node_ptr node; |
| SEL awake_sel = sel_get_any_uid ("awake"); |
| cache_ptr free_list = hash_new (64, |
| (hash_func_type) hash_ptr, |
| (compare_func_type) compare_ptrs); |
| |
| /* resolve object forward references */ |
| for (node = hash_next (stream->object_refs, NULL); node; |
| node = hash_next (stream->object_refs, node)) |
| { |
| struct objc_list *reflist = node->value; |
| const void *key = node->key; |
| id object = hash_value_for_key (stream->object_table, key); |
| while (reflist) |
| { |
| *((id*) reflist->head) = object; |
| if (hash_value_for_key (free_list,reflist) == NULL) |
| hash_add (&free_list,reflist,reflist); |
| |
| reflist = reflist->tail; |
| } |
| } |
| |
| /* apply __objc_free to all objects stored in free_list */ |
| for (node = hash_next (free_list, NULL); node; |
| node = hash_next (free_list, node)) |
| objc_free ((void *) node->key); |
| |
| hash_delete (free_list); |
| |
| /* empty object reference table */ |
| hash_delete (stream->object_refs); |
| stream->object_refs = hash_new(8, (hash_func_type)hash_ptr, |
| (compare_func_type)compare_ptrs); |
| |
| /* call -awake for all objects read */ |
| if (awake_sel) |
| { |
| for (node = hash_next (stream->object_table, NULL); node; |
| node = hash_next (stream->object_table, node)) |
| { |
| id object = node->value; |
| if (__objc_responds_to (object, awake_sel)) |
| (*objc_msg_lookup (object, awake_sel)) (object, awake_sel); |
| } |
| } |
| |
| /* empty object table */ |
| hash_delete (stream->object_table); |
| stream->object_table = hash_new(64, |
| (hash_func_type)hash_ptr, |
| (compare_func_type)compare_ptrs); |
| } |
| |
| /* |
| ** Open the stream PHYSICAL in MODE |
| */ |
| |
| TypedStream * |
| objc_open_typed_stream (FILE *physical, int mode) |
| { |
| TypedStream *s = (TypedStream *) objc_malloc (sizeof (TypedStream)); |
| |
| s->mode = mode; |
| s->physical = physical; |
| s->stream_table = hash_new (64, |
| (hash_func_type) hash_ptr, |
| (compare_func_type) compare_ptrs); |
| s->object_table = hash_new (64, |
| (hash_func_type) hash_ptr, |
| (compare_func_type) compare_ptrs); |
| s->eof = (objc_typed_eof_func) __objc_feof; |
| s->flush = (objc_typed_flush_func) fflush; |
| s->writing_root_p = 0; |
| if (mode == OBJC_READONLY) |
| { |
| s->class_table = hash_new (8, (hash_func_type) hash_string, |
| (compare_func_type) compare_strings); |
| s->object_refs = hash_new (8, (hash_func_type) hash_ptr, |
| (compare_func_type) compare_ptrs); |
| s->read = (objc_typed_read_func) __objc_fread; |
| s->write = (objc_typed_write_func) __objc_no_write; |
| __objc_read_typed_stream_signature (s); |
| } |
| else if (mode == OBJC_WRITEONLY) |
| { |
| s->class_table = 0; |
| s->object_refs = 0; |
| s->read = (objc_typed_read_func) __objc_no_read; |
| s->write = (objc_typed_write_func) __objc_fwrite; |
| __objc_write_typed_stream_signature (s); |
| } |
| else |
| { |
| objc_close_typed_stream (s); |
| return NULL; |
| } |
| s->type = OBJC_FILE_STREAM; |
| return s; |
| } |
| |
| /* |
| ** Open the file named by FILE_NAME in MODE |
| */ |
| |
| TypedStream* |
| objc_open_typed_stream_for_file (const char *file_name, int mode) |
| { |
| FILE *file = NULL; |
| TypedStream *s; |
| |
| if (mode == OBJC_READONLY) |
| file = fopen (file_name, "r"); |
| else |
| file = fopen (file_name, "w"); |
| |
| if (file) |
| { |
| s = objc_open_typed_stream (file, mode); |
| if (s) |
| s->type |= OBJC_MANAGED_STREAM; |
| return s; |
| } |
| else |
| return NULL; |
| } |
| |
| /* |
| ** Close STREAM freeing the structure it self. If it was opened with |
| ** objc_open_typed_stream_for_file, the file will also be closed. |
| */ |
| |
| void |
| objc_close_typed_stream (TypedStream *stream) |
| { |
| if (stream->mode == OBJC_READONLY) |
| { |
| __objc_finish_read_root_object (stream); /* Just in case... */ |
| hash_delete (stream->class_table); |
| hash_delete (stream->object_refs); |
| } |
| |
| hash_delete (stream->stream_table); |
| hash_delete (stream->object_table); |
| |
| if (stream->type == (OBJC_MANAGED_STREAM | OBJC_FILE_STREAM)) |
| fclose ((FILE *)stream->physical); |
| |
| objc_free(stream); |
| } |
| |
| BOOL |
| objc_end_of_typed_stream (TypedStream *stream) |
| { |
| return (*stream->eof) (stream->physical); |
| } |
| |
| void |
| objc_flush_typed_stream (TypedStream *stream) |
| { |
| (*stream->flush) (stream->physical); |
| } |
| |
| long |
| objc_get_stream_class_version (TypedStream *stream, Class class) |
| { |
| if (stream->class_table) |
| return PTR2LONG(hash_value_for_key (stream->class_table, class->name)); |
| else |
| return class_get_version (class); |
| } |
| |