| /* | |
| * Routines dealing with format descriptor strings (FDS). | |
| * | |
| * Copyright (c) 1996-2002 by Guardsoft Pty Ltd. | |
| * | |
| * All rights reserved. This program and the accompanying materials | |
| * are made available under the terms of the Eclipse Public License v1.0 | |
| * which accompanies this distribution, and is available at | |
| * http://www.eclipse.org/legal/epl-v10.html | |
| * | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| #include <config.h> | |
| #endif /* HAVE_CONFIG_H */ | |
| #include <stdio.h> | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include <stdlib.h> | |
| #include "aif.h" | |
| #include "aiferr.h" | |
| #include "aifint.h" | |
| static char _fds_type_str[NUM_AIF_TYPES][12] = | |
| { | |
| { FDS_INVALID, '%', 'd', FDS_INVALID, '\0' }, | |
| { FDS_INTEGER, '%', 'c', '%', 'd', '\0' }, | |
| { FDS_FLOATING, '%', 'd', '\0' }, | |
| { FDS_POINTER, '%', 's', '%', 's', '\0' }, | |
| { FDS_ARRAY_START, '%', 's', FDS_ARRAY_END, '%', 's', '\0' }, | |
| { FDS_AGGREGATE_START, '%', 's', FDS_TYPENAME_END, ';', ';', ';', FDS_AGGREGATE_END, '\0' }, | |
| { FDS_UNION_START, '%', 's', FDS_TYPENAME_END, FDS_UNION_END, '\0' }, | |
| { FDS_FUNCTION, FDS_FUNCTION_ARG_END, '%', 's', '\0' }, | |
| { FDS_VOID, '%', 'd', '\0' }, | |
| { FDS_REGION, '%', 'd', '%', 's', '\0' }, | |
| { '%', FDS_NAME, '%', 'd', FDS_NAME_END, '%', 's', '\0' }, | |
| { FDS_REFERENCE, '%', 'd', FDS_REFERENCE_END, '\0' }, | |
| { FDS_STRING, '\0' }, | |
| { FDS_CHARACTER, '\0' }, | |
| { FDS_RANGE, '%', 'd', FDS_RANGE_SEP, '%', 'd', '%', 's', '\0' }, | |
| { FDS_ENUM_START, '%', 's', FDS_TYPENAME_END, FDS_ENUM_END, FDS_INTEGER, '%', 'c', '4', '\0' }, | |
| { FDS_BOOLEAN, '%', 'd', '\0' }, | |
| { FDS_ADDRESS, '%', 'd', '\0' }, | |
| { FDS_CHAR_POINTER, '%', 's', '\0' } | |
| }; | |
| static char _fds_array_end[] = {FDS_ARRAY_END, '\0'}; | |
| static char _fds_function_arg_end[] = {FDS_FUNCTION_ARG_END, '\0'}; | |
| static char _fds_name_end[] = {FDS_NAME_END, '\0'}; | |
| static char _fds_enum_end[] = {FDS_ENUM_END, '\0'}; | |
| static char _fds_enum_const_name_end[] = {FDS_ENUM_SEP, '\0' }; | |
| static char _fds_enum_const_end[] = { FDS_ENUM_CONST_SEP, FDS_ENUM_END, '\0' }; | |
| static char _fds_aggregate_access_sep[] = {FDS_AGGREGATE_ACCESS_SEP, '\0'}; | |
| static char _fds_aggregate_field_name_end[] = {FDS_AGGREGATE_FIELD_NAME_END, '\0' }; | |
| static char _fds_aggregate_field_end[] = { FDS_AGGREGATE_ACCESS_SEP, FDS_AGGREGATE_FIELD_SEP, FDS_AGGREGATE_END, '\0' }; | |
| static char _fds_union_field_name_end[] = {FDS_UNION_FIELD_NAME_END, '\0' }; | |
| static char _fds_union_field_end[] = { FDS_UNION_FIELD_SEP, FDS_UNION_END, '\0' }; | |
| #define MAX_CALLS 4 | |
| static char * _fds_buf[MAX_CALLS]; | |
| static int _fds_pos = 0; | |
| char * | |
| _fds_skipnum(char *fds) | |
| { | |
| if ( fds == NULL ) | |
| return NULL; | |
| while ( *fds != '\0' && isdigit((int)*fds) ) | |
| fds++; | |
| return fds; | |
| } | |
| /* | |
| * Skip the name field. Leaves fds pointing | |
| * at the first character after the end of name. | |
| * Does nothing if there is no name field. | |
| */ | |
| void | |
| _fds_skip_name(char **fds) | |
| { | |
| if (**fds == FDS_NAME) { | |
| *fds = _fds_skipto(*fds, _fds_name_end); | |
| (*fds)++; | |
| } | |
| } | |
| /* | |
| * Skip to the location of a character from 'set' in the supplied | |
| * type string. | |
| * | |
| * Returns a pointer to the character if located, or | |
| * to the NULL character if not. | |
| * | |
| * Only searches the "top level" of the type. If the type contains | |
| * nested structured types, these will be skipped. | |
| */ | |
| char * | |
| _fds_skipto(char *fds, char *set) | |
| { | |
| char * s; | |
| if ( fds == NULL ) | |
| return NULL; | |
| for ( ; *fds != '\0' ; fds++ ) | |
| { | |
| if | |
| ( | |
| *fds == FDS_AGGREGATE_START | |
| || | |
| *fds == FDS_UNION_START | |
| || | |
| *fds == FDS_ARRAY_START | |
| || | |
| *fds == FDS_ENUM_START | |
| ) | |
| { | |
| fds = _fds_skiptomatch(fds); | |
| fds++; | |
| } | |
| for ( s = set ; *s != '\0' ; s++ ) | |
| if ( *s == *fds ) | |
| return fds; | |
| } | |
| return fds; | |
| } | |
| /* | |
| * Convert an ascii number to a number. NOTE: silently truncates | |
| * the number to fit in sizeof int. | |
| */ | |
| int | |
| _fds_getnum(char *str) | |
| { | |
| // int n; | |
| // char * p; | |
| // char * t; | |
| // | |
| // p = _fds_skipnum(str); | |
| // | |
| // if ( *p == '\0' ) | |
| // return strtoul(str, NULL, 10); | |
| // | |
| // n = p - str; | |
| // t = (char *)_aif_alloc(n + 1); | |
| // memcpy(t, str, n); | |
| // t[n] = '\0'; | |
| // | |
| // n = strtoul(t, NULL, 10); | |
| // | |
| // _aif_free(t); | |
| // | |
| // return n; | |
| return strtoul(str, NULL, 10); | |
| } | |
| int | |
| FDSType(char *str) | |
| { | |
| int type; | |
| if | |
| ( | |
| str == NULL | |
| || | |
| *str == '\0' | |
| ) | |
| return AIF_INVALID; | |
| switch ( *str ) | |
| { | |
| case FDS_BOOLEAN: /* boolean */ | |
| type = AIF_BOOLEAN; | |
| break; | |
| case FDS_CHARACTER: /* character */ | |
| type = AIF_CHARACTER; | |
| break; | |
| case FDS_INTEGER: /* integer */ | |
| type = AIF_INTEGER; | |
| break; | |
| case FDS_FLOATING: /* floating */ | |
| type = AIF_FLOATING; | |
| break; | |
| case FDS_POINTER: /* pointer */ | |
| type = AIF_POINTER; | |
| break; | |
| case FDS_ARRAY_START: /* array */ | |
| type = AIF_ARRAY; | |
| break; | |
| case FDS_AGGREGATE_START: /* struct or class */ | |
| type = AIF_AGGREGATE; | |
| break; | |
| case FDS_UNION_START: /* union */ | |
| type = AIF_UNION; | |
| break; | |
| case FDS_ENUM_START: /* enumeration */ | |
| type = AIF_ENUM; | |
| break; | |
| case FDS_RANGE: /* range */ | |
| type = AIF_INTEGER; | |
| break; | |
| case FDS_FUNCTION: /* function */ | |
| type = AIF_FUNCTION; | |
| break; | |
| case FDS_STRING: /* string */ | |
| type = AIF_STRING; | |
| break; | |
| case FDS_CHAR_POINTER: /* char pointer */ | |
| type = AIF_CHAR_POINTER; | |
| break; | |
| case FDS_ADDRESS: /* address */ | |
| type = AIF_ADDRESS; | |
| break; | |
| case FDS_VOID: /* void */ | |
| type = AIF_VOID; | |
| break; | |
| case FDS_REGION: /* ZPL region */ | |
| type = AIF_REGION; | |
| break; | |
| case FDS_NAME: /* named component */ | |
| type = AIF_NAME; | |
| break; | |
| case FDS_REFERENCE: /* reference to named component*/ | |
| type = AIF_REFERENCE; | |
| break; | |
| default: | |
| type = AIF_INVALID; | |
| } | |
| return type; | |
| } | |
| /* extracts the base type of a pointer or array type, specified by the */ | |
| /* type descriptor. */ | |
| char * | |
| FDSBaseType(char *type) | |
| { | |
| do | |
| { | |
| if ( (type = _fds_base_type(type)) == NULL ) | |
| return NULL; | |
| } | |
| while ( FDSType(type) == AIF_ARRAY || FDSType(type) == AIF_POINTER ); | |
| return type; | |
| } | |
| char * | |
| _fds_base_type(char *type) | |
| { | |
| char * p; | |
| if ( type == NULL || *type == '\0' ) | |
| return NULL; | |
| switch ( *type++ ) | |
| { | |
| case FDS_FUNCTION: /* function */ | |
| p = _fds_skipto(type, _fds_function_arg_end); | |
| p++; | |
| break; | |
| case FDS_POINTER: /* pointer */ | |
| _fds_advance(&type); /* skip address */ | |
| p = type; | |
| break; | |
| case FDS_ARRAY_START: /* array */ | |
| p = _fds_skipto(type, _fds_array_end); | |
| p++; | |
| break; | |
| case FDS_ENUM_START: /* enum */ | |
| p = _fds_skipto(type, _fds_enum_end); | |
| p++; | |
| break; | |
| case FDS_RANGE: /* range */ | |
| p = _fds_skipnum(type); /* skip MinValue */ | |
| p += 2; /* skip '..' */ | |
| p = _fds_skipnum(p); /* skip MaxValue */ | |
| break; | |
| case FDS_REGION: /* region */ | |
| p = type + 1; /* skip FDS_REGION Rank */ | |
| break; | |
| default: | |
| return NULL; | |
| } | |
| return p; | |
| } | |
| /* In AIF, we can find out about the size of the AIF object by looking at the | |
| * AIF_LEN(), but this method cannot be used to find out about the size | |
| * of each individual element in a aggregate. To overcome this problem, we can use | |
| * FDSTypeSize() with the FDS for each element. | |
| * | |
| * However, FDSTypeSize() cannot be used to know the real size if the aggregate | |
| * contains references or strings (since we also need to access the data to | |
| * know the size of these data types). | |
| * | |
| * We use FDSDataSize() to overcome the limitation of FDSTypeSize(). It can | |
| * access FDS and the data of an AIF object to calculate the (real) size of | |
| * the data. | |
| */ | |
| int | |
| FDSDataSize(char *fds, char *data) | |
| { | |
| char * tmp1 = fds; | |
| char * tmp2 = data; | |
| ResetAIFError(); | |
| _fds_skip_data(&tmp1, &tmp2); | |
| if ( AIFError() == AIFERR_NOERR ) | |
| return (tmp2-data); | |
| else | |
| return -1; | |
| } | |
| /* | |
| * Gets the size of a type in bytes, specified by the type descriptor. | |
| * If the size cannot fit in a int, then it is silently truncated. | |
| * returns size if successful, -1 otherwise. | |
| * | |
| * Note: we no longer know the size of aggregates or unions so these are | |
| * always -1. | |
| */ | |
| int | |
| FDSTypeSize(char *type) | |
| { | |
| int s; | |
| int rank; | |
| int size; | |
| int subsize; | |
| if ( type == NULL || *type == '\0' ) | |
| return -1; | |
| switch ( *type ) | |
| { | |
| case FDS_ADDRESS: | |
| case FDS_BOOLEAN: /* boolean */ | |
| return _fds_getnum(type + 1); | |
| case FDS_CHARACTER: /* character */ | |
| return 1; | |
| case FDS_INTEGER: /* integer */ | |
| return _fds_getnum(type + 2); | |
| case FDS_REFERENCE: /* reference use */ | |
| return 0; | |
| case FDS_NAME: /* named component */ | |
| return FDSTypeSize(strchr(type, FDS_NAME_END) + 1); | |
| case FDS_FUNCTION: /* function */ | |
| return FDSTypeSize(strchr(type, FDS_FUNCTION_ARG_END) + 1); | |
| case FDS_POINTER: /* pointer */ | |
| return FDSTypeSize(type+1); | |
| case FDS_FLOATING: /* floating */ | |
| case FDS_VOID: /* void */ | |
| return _fds_getnum(type + 1); | |
| case FDS_AGGREGATE_START: /* aggregate */ | |
| type++; /* past open brace */ | |
| size = 0; | |
| subsize = 0; | |
| _fds_skip_typename(&type); | |
| while ( *type != FDS_AGGREGATE_END ) | |
| { | |
| if ( *type == FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| type++; | |
| continue; | |
| } | |
| type = strchr(type, FDS_AGGREGATE_FIELD_NAME_END) + 1; | |
| /* to start of field */ | |
| if ( (subsize = FDSTypeSize(type)) < 0 ) | |
| return -1; | |
| size += subsize; | |
| _fds_advance(&type); | |
| if ( *type == FDS_AGGREGATE_FIELD_SEP ) | |
| type++; | |
| } | |
| return size; | |
| case FDS_UNION_START: | |
| type++; /* past open brace */ | |
| size = 0; | |
| subsize = 0; | |
| _fds_skip_typename(&type); | |
| while ( *type != FDS_UNION_END ) | |
| { | |
| type = strchr(type, FDS_UNION_FIELD_NAME_END) + 1; | |
| /* to start of field */ | |
| if ( (subsize = FDSTypeSize(type)) < 0 ) | |
| return -1; | |
| if ( subsize > size ) | |
| size = subsize; | |
| _fds_advance(&type); | |
| if ( *type == FDS_UNION_FIELD_SEP ) | |
| type++; | |
| } | |
| return size; | |
| case FDS_ARRAY_START: /* array */ | |
| if ( (s = FDSTypeSize(_fds_base_type(type))) < 0 ) | |
| return -1; | |
| return s * _fds_array_size(type); | |
| case FDS_ENUM_START: /* enumeration */ | |
| return 4; /* all enumerations are based on 4-byte integers */ | |
| case FDS_RANGE: /* range */ | |
| return FDSTypeSize(_fds_base_type(type)); | |
| case FDS_REGION: /* region */ | |
| rank = _fds_getnum(type + 1); | |
| s = FDSTypeSize(_fds_base_type(type)); | |
| return rank * s * 2; | |
| case FDS_CHAR_POINTER: | |
| case FDS_STRING: | |
| default: | |
| return -1; | |
| } | |
| /* NOT REACHED */ | |
| } | |
| int | |
| FDSIsSigned(char *fds) | |
| { | |
| if ( *fds++ == FDS_INTEGER ) | |
| return (int)(*fds == FDS_INTEGER_SIGNED); | |
| return 0; | |
| } | |
| /* | |
| * Create an FDS type description. The result does not | |
| * need to be freed. | |
| * | |
| * Calls to TypeToFDS() may be nested as follows: | |
| * | |
| * TypeToFDS(AIF_ARRAY, | |
| * TypeToFDS(AIF_RANGE, 0, 10, | |
| * TypeToFDS(AIF_INTEGER, 4) | |
| * ), | |
| * TypeToFDS(AIF_INTEGER, 10) | |
| * ); | |
| * | |
| * TypeToFDS currently only allows two nested calls. If | |
| * an FDS type is created requiring more than two, this will | |
| * need to be increased. | |
| */ | |
| char * | |
| TypeToFDS(int type, ...) | |
| { | |
| va_list args; | |
| int v1; | |
| int v2; | |
| char * v3; | |
| char * v4; | |
| char * buf; | |
| if ( type < 0 || type >= NUM_AIF_TYPES ) | |
| type = AIF_INVALID; | |
| va_start(args, type); | |
| switch ( type ) | |
| { | |
| case AIF_INTEGER: | |
| v1 = va_arg(args, int); | |
| v2 = va_arg(args, int); | |
| asprintf(&buf, _fds_type_str[type], v1 ? FDS_INTEGER_SIGNED : FDS_INTEGER_UNSIGNED, v2); | |
| break; | |
| case AIF_FLOATING: | |
| case AIF_VOID: | |
| case AIF_REFERENCE: | |
| v1 = va_arg(args, int); | |
| asprintf(&buf, _fds_type_str[type], v1); | |
| break; | |
| case AIF_POINTER: | |
| v3 = va_arg(args, char *); | |
| v4 = va_arg(args, char *); | |
| asprintf(&buf, _fds_type_str[type], v3, v4); | |
| break; | |
| case AIF_FUNCTION: | |
| v3 = va_arg(args, char *); | |
| asprintf(&buf, _fds_type_str[type], v3); | |
| break; | |
| case AIF_ENUM: | |
| v3 = va_arg(args, char *); | |
| if (v3 == NULL) { | |
| v3 = ""; | |
| } | |
| v1 = va_arg(args, int); | |
| asprintf(&buf, _fds_type_str[type], v3, v1 ? FDS_INTEGER_SIGNED : FDS_INTEGER_UNSIGNED); | |
| break; | |
| case AIF_CHAR_POINTER: | |
| v3 = va_arg(args, char *); | |
| asprintf(&buf, _fds_type_str[type], v3); | |
| break; | |
| case AIF_ADDRESS: | |
| case AIF_BOOLEAN: | |
| v1 = va_arg(args, int); | |
| asprintf(&buf, _fds_type_str[type], v1); | |
| break; | |
| case AIF_STRING: | |
| case AIF_CHARACTER: | |
| asprintf(&buf, _fds_type_str[type]); | |
| break; | |
| case AIF_AGGREGATE: | |
| case AIF_UNION: | |
| v3 = va_arg(args, char *); | |
| if (v3 == NULL) { | |
| v3 = ""; | |
| } | |
| asprintf(&buf, _fds_type_str[type], v3); | |
| break; | |
| case AIF_RANGE: | |
| v1 = va_arg(args, int); | |
| v2 = va_arg(args, int); | |
| v3 = va_arg(args, char *); | |
| asprintf(&buf, _fds_type_str[type], v1, v2, v3); | |
| break; | |
| case AIF_ARRAY: | |
| v3 = va_arg(args, char *); | |
| v4 = va_arg(args, char *); | |
| asprintf(&buf, _fds_type_str[type], v3, v4); | |
| break; | |
| case AIF_NAME: | |
| case AIF_REGION: | |
| v1 = va_arg(args, int); | |
| v3 = va_arg(args, char *); | |
| if ( v3 == NULL ) { | |
| v3 = ""; | |
| } | |
| asprintf(&buf, _fds_type_str[type], v1, v3); | |
| break; | |
| case AIF_INVALID: | |
| default: | |
| asprintf(&buf, _fds_type_str[AIF_INVALID], 0); | |
| } | |
| va_end(args); | |
| if (_fds_pos >= MAX_CALLS) { | |
| _fds_pos = 0; | |
| } | |
| if (_fds_buf[_fds_pos] != NULL) { | |
| free(_fds_buf[_fds_pos]); | |
| } | |
| _fds_buf[_fds_pos++] = buf; | |
| return buf; | |
| } | |
| int | |
| FDSTypeCompare(char *f1, char *f2) | |
| { | |
| int n; | |
| int res; | |
| char * n1; | |
| char * n2; | |
| char * t1; | |
| char * t2; | |
| AIFIndex * ix1; | |
| AIFIndex * ix2; | |
| if ( *f1 == FDS_NAME ) | |
| { | |
| f1 = _fds_skipto(f1, _fds_name_end); | |
| f1++; | |
| } | |
| if ( *f2 == FDS_NAME ) | |
| { | |
| f2 = _fds_skipto(f2, _fds_name_end); | |
| f2++; | |
| } | |
| if ( FDSType(f1) != FDSType(f2) ) | |
| return 0; | |
| switch ( FDSType(f1) ) | |
| { | |
| case AIF_ENUM: | |
| res = strcmp(f1, f2) == 0; | |
| break; | |
| case AIF_INTEGER: | |
| case AIF_FLOATING: | |
| case AIF_VOID: | |
| case AIF_REGION: | |
| case AIF_ADDRESS: | |
| case AIF_BOOLEAN: | |
| res = FDSTypeSize(f1) == FDSTypeSize(f2); | |
| break; | |
| case AIF_POINTER: | |
| case AIF_FUNCTION: | |
| res = FDSTypeCompare(FDSBaseType(f1), FDSBaseType(f2)); | |
| break; | |
| case AIF_REFERENCE: | |
| res = FDSType(f2) == FDSType(f1); | |
| break; | |
| case AIF_ARRAY: | |
| ix1 = FDSArrayIndexInit(f1); | |
| ix2 = FDSArrayIndexInit(f1); | |
| if ( ix1->i_rank != ix2->i_rank ) | |
| { | |
| AIFArrayIndexFree(ix1); | |
| AIFArrayIndexFree(ix2); | |
| return 0; | |
| } | |
| for ( n = 0 ; n < ix1->i_rank ; n++ ) | |
| { | |
| if ( ix1->i_size[n] != ix2->i_size[n] ) | |
| { | |
| AIFArrayIndexFree(ix1); | |
| AIFArrayIndexFree(ix2); | |
| return 0; | |
| } | |
| } | |
| res = FDSTypeSize(ix1->i_btype) == FDSTypeSize(ix2->i_btype); | |
| AIFArrayIndexFree(ix1); | |
| AIFArrayIndexFree(ix2); | |
| break; | |
| case AIF_UNION: | |
| case AIF_AGGREGATE: | |
| if ( FDSNumFields(f1) != FDSNumFields(f2) ) | |
| { | |
| res = 0; | |
| break; | |
| } | |
| for ( n = 0 ; n < FDSNumFields(f1) ; n++ ) | |
| { | |
| if | |
| ( | |
| FDSAggregateFieldByNumber(f1, n, &n1, &t1) < 0 | |
| || | |
| FDSAggregateFieldByNumber(f2, n, &n2, &t2) < 0 | |
| ) | |
| return 0; | |
| _aif_free(n1); | |
| _aif_free(n2); | |
| if ( !FDSTypeCompare(t1, t2) ) | |
| { | |
| _aif_free(t1); | |
| _aif_free(t2); | |
| return 0; | |
| } | |
| _aif_free(t1); | |
| _aif_free(t2); | |
| } | |
| res = 1; | |
| break; | |
| case AIF_CHAR_POINTER: | |
| case AIF_STRING: | |
| case AIF_CHARACTER: | |
| res = 1; | |
| break; | |
| default: | |
| res = 0; | |
| } | |
| return res; | |
| } | |
| /************************************************************ | |
| ********************** ARRAY ROUTINES ********************** | |
| ************************************************************/ | |
| /* extracts the index type of an array type, specified by the */ | |
| /* type descriptor. */ | |
| /* index type descriptor is returned if a valid (not a NULL) pointer to */ | |
| /* char is passed as parameter. */ | |
| /* returns 0 if successful, -1 otherwise. */ | |
| char * | |
| FDSArrayIndexType(char *type) | |
| { | |
| char * p; | |
| char * pi; | |
| char * index; | |
| if | |
| ( | |
| type == NULL | |
| || | |
| *type == '\0' | |
| || | |
| *type != FDS_ARRAY_START | |
| || | |
| *++type != FDS_RANGE | |
| ) | |
| return NULL; | |
| p = _fds_skipnum(++type); /* skip MinValue */ | |
| p += 2; /* skip '..' */ | |
| p = _fds_skipnum(p); /* skip MaxValue */ | |
| pi = p; | |
| p = _fds_skipto(p, _fds_array_end); | |
| *p = 0; | |
| index = strdup(pi); | |
| *p = FDS_ARRAY_END; | |
| return index; | |
| } | |
| int | |
| _fds_array_min_index(char *type) | |
| { | |
| if ( *++type != FDS_RANGE ) | |
| return -1; | |
| return _fds_getnum(++type); | |
| } | |
| int | |
| FDSArrayMinIndex(char *fds, int n) | |
| { | |
| while ( FDSType(fds) == AIF_ARRAY && n > 0 ) | |
| { | |
| fds = _fds_base_type(fds); | |
| n--; | |
| } | |
| return (n == 0) ? _fds_array_min_index(fds) : -1; | |
| } | |
| int | |
| _fds_array_size(char *type) | |
| { | |
| char * p; | |
| if ( *++type != FDS_RANGE ) | |
| return -1; | |
| p = _fds_skipnum(++type); /* skip MinValue */ | |
| p++; /* skip ',' */ | |
| return _fds_getnum(p); | |
| } | |
| /* | |
| * Number of elements in dimension n | |
| */ | |
| int | |
| FDSArrayRankSize(char *fds, int n) | |
| { | |
| while ( FDSType(fds) == AIF_ARRAY && n > 0 ) | |
| { | |
| fds = _fds_base_type(fds); | |
| n--; | |
| } | |
| return (n == 0) ? _fds_array_size(fds) : -1; | |
| } | |
| /* | |
| * Calculate the number of dimensions of an array. | |
| */ | |
| int | |
| FDSArrayRank(char *fds) | |
| { | |
| int d = 0; | |
| while ( FDSType(fds) == AIF_ARRAY ) | |
| { | |
| fds = _fds_base_type(fds); | |
| d++; | |
| } | |
| return d; | |
| } | |
| /* | |
| * Calculate the total number of elements in an array. | |
| * This will count elements in nested arrays. | |
| */ | |
| int | |
| FDSArraySize(char *fds) | |
| { | |
| int num; | |
| int size = 1; | |
| while ( FDSType(fds) == AIF_ARRAY ) | |
| { | |
| num = _fds_array_size(fds); | |
| fds = _fds_base_type(fds); | |
| size *= num; | |
| } | |
| return size; | |
| } | |
| char * | |
| FDSRangeInit(int min, int size) | |
| { | |
| return strdup(TypeToFDS(AIF_RANGE, min, size, "is4")); | |
| } | |
| char * | |
| FDSArrayInit(int min, int size, char *btype) | |
| { | |
| char * res; | |
| char * range = FDSRangeInit(min, size); | |
| res = strdup(TypeToFDS(AIF_ARRAY, range, btype)); | |
| _aif_free(range); | |
| return res; | |
| } | |
| /* | |
| * Parse array type descriptor and extract the minimum index value and number | |
| * of elements for each dimension. | |
| */ | |
| void | |
| FDSArrayBounds(char *fds, int rank, int **min, int **size) | |
| { | |
| int i; | |
| int * mn; | |
| int * sz; | |
| *min = (int *)_aif_alloc(rank*sizeof(int)); | |
| *size = (int *)_aif_alloc(rank*sizeof(int)); | |
| mn = *min; | |
| sz = *size; | |
| for ( i = 0 ; i < rank ; i++) { | |
| *mn++ = _fds_array_min_index(fds); | |
| *sz++ = _fds_array_size(fds); | |
| fds = _fds_base_type(fds); | |
| } | |
| } | |
| /* | |
| * Extract info about an array. Returns the index type of the | |
| * first dimension (assumes all dimensions are the same type), | |
| * the element type of the array, and the number of dimensions | |
| * of the array. It is the responsibility of the caller to free | |
| * memory allocated to el. | |
| * | |
| */ | |
| void | |
| FDSArrayInfo(char *fds, int *rank, char **el, char **ix) | |
| { | |
| int d; | |
| if ( ix != NULL ) | |
| *ix = FDSArrayIndexType(fds); | |
| d = 0; | |
| while ( FDSType(fds) == AIF_ARRAY ) | |
| { | |
| fds = _fds_base_type(fds); | |
| d++; | |
| } | |
| *el = strdup(fds); | |
| *rank = d; | |
| } | |
| AIFIndex * | |
| FDSArrayIndexInit(char *fmt) | |
| { | |
| int d; | |
| int nel = 1; | |
| int rank; | |
| char * btype; | |
| AIFIndex * ix; | |
| FDSArrayInfo(fmt, &rank, &btype, NULL); | |
| if ( rank == 0 ) | |
| return (AIFIndex *)NULL; | |
| ix = (AIFIndex *)_aif_alloc(sizeof(AIFIndex)); | |
| ix->i_finished = 0; | |
| ix->i_rank = rank; | |
| ix->i_btype = btype; | |
| ix->i_bsize = FDSTypeSize(ix->i_btype); | |
| ix->i_index = (int *)_aif_alloc(ix->i_rank * sizeof(int)); | |
| FDSArrayBounds(fmt, ix->i_rank, &(ix->i_min), &(ix->i_size)); | |
| for ( d = ix->i_rank - 1 ; d >= 0 ; d-- ) | |
| { | |
| nel *= ix->i_size[d]; | |
| ix->i_index[d] = ix->i_min[d]; | |
| } | |
| ix->i_nel = nel; | |
| return ix; | |
| } | |
| /************************************************************ | |
| ********************** AGGREGATE ROUTINES ********************** | |
| ************************************************************/ | |
| /* | |
| * The fds of a aggregate has this format: | |
| * | |
| * {name|entry,...;entry,...;entry,...;entry,...} | |
| * | |
| * name is the name of the aggregate, or empty for an unnamed type | |
| * each entry has the format "name=type" | |
| * a aggregate comprises 4 sections separated by ';', corresponding | |
| * to public, private, protected and package members | |
| */ | |
| #define AGGREGATE_START(fds, res) \ | |
| if (*(fds) == FDS_NAME) { \ | |
| (fds) = _fds_skipto((fds), _fds_name_end); \ | |
| (fds)++; \ | |
| } \ | |
| if ( *(fds++) != FDS_AGGREGATE_START ) \ | |
| return (res); \ | |
| while ( *(fds) != FDS_TYPENAME_END ) \ | |
| (fds)++; \ | |
| (fds)++; \ | |
| if ( *(fds) == '\0' ) \ | |
| return (res); \ | |
| while ( *(fds) == FDS_AGGREGATE_ACCESS_SEP ) \ | |
| (fds)++; \ | |
| if ( *(fds) == FDS_AGGREGATE_END ) \ | |
| return (res); | |
| /* | |
| * Skip to matching end of type character. | |
| * | |
| * If the fds is not a structured type, skip to end of string. | |
| * | |
| * Returns pointer to end of type character or end of string | |
| * if no match can be found. | |
| */ | |
| char * | |
| _fds_skiptomatch(char *fds) | |
| { | |
| char ender = 0; | |
| switch ( *fds ) | |
| { | |
| case FDS_AGGREGATE_START: | |
| ender = FDS_AGGREGATE_END; | |
| break; | |
| case FDS_ARRAY_START: | |
| ender = FDS_ARRAY_END; | |
| break; | |
| case FDS_ENUM_START: | |
| ender = FDS_ENUM_END; | |
| break; | |
| case FDS_UNION_START: | |
| ender = FDS_UNION_END; | |
| break; | |
| } | |
| fds++; | |
| for ( ;; ) | |
| { | |
| if ( *fds == ender ) | |
| break; | |
| if | |
| ( | |
| *fds == FDS_AGGREGATE_START | |
| || | |
| *fds == FDS_UNION_START | |
| || | |
| *fds == FDS_ARRAY_START | |
| || | |
| *fds == FDS_ENUM_START | |
| ) | |
| fds = _fds_skiptomatch(fds); | |
| fds++; | |
| } | |
| return fds; | |
| } | |
| /* | |
| * The function ignores access specifiers | |
| */ | |
| char * | |
| _fds_skiptofield(char *fds, int n) | |
| { | |
| AGGREGATE_START(fds, NULL); | |
| for ( ; *fds != '\0' && *fds != FDS_AGGREGATE_END && n > 0 ; fds++) | |
| { | |
| if ( *fds == FDS_AGGREGATE_FIELD_SEP ) { | |
| n--; | |
| continue; | |
| } | |
| if ( *fds == FDS_AGGREGATE_ACCESS_SEP ) { | |
| if | |
| ( | |
| *(fds+1) != FDS_AGGREGATE_ACCESS_SEP | |
| && | |
| *(fds+1) != FDS_AGGREGATE_END | |
| ) { | |
| n--; | |
| } | |
| continue; | |
| } | |
| if | |
| ( | |
| *fds == FDS_AGGREGATE_START | |
| || | |
| *fds == FDS_UNION_START | |
| || | |
| *fds == FDS_ARRAY_START | |
| || | |
| *fds == FDS_ENUM_START | |
| ) { | |
| fds = _fds_skiptomatch(fds); | |
| } | |
| } | |
| if ( *fds == '\0' || *fds == FDS_AGGREGATE_END ) | |
| return NULL; | |
| return fds; | |
| } | |
| /* | |
| * The function ignores access specifiers, ie: total number of fields | |
| */ | |
| int | |
| FDSNumFields(char *fds) | |
| { | |
| int n = 0; | |
| if (*fds == FDS_NAME) | |
| { | |
| fds = _fds_skipto(fds, _fds_name_end); | |
| fds++; | |
| } | |
| if ( *(fds++) != FDS_AGGREGATE_START ) | |
| return -1; | |
| while ( *(fds) != FDS_TYPENAME_END ) | |
| (fds)++; | |
| (fds)++; | |
| if ( *fds == '\0' ) | |
| return -1; | |
| while ( *fds == FDS_AGGREGATE_ACCESS_SEP ) | |
| (fds)++; | |
| if ( *fds == FDS_AGGREGATE_END ) | |
| return 0; | |
| while ( *fds != '\0' && *fds != FDS_AGGREGATE_END ) | |
| { | |
| if ( *fds == FDS_AGGREGATE_FIELD_SEP ) | |
| n++; | |
| else if ( *fds == FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| if | |
| ( | |
| *(fds+1) != FDS_AGGREGATE_ACCESS_SEP | |
| && | |
| *(fds+1) != FDS_AGGREGATE_END | |
| ) | |
| n++; | |
| } | |
| else if | |
| ( | |
| *fds == FDS_AGGREGATE_START | |
| || | |
| *fds == FDS_UNION_START | |
| || | |
| *fds == FDS_ARRAY_START | |
| || | |
| *fds == FDS_ENUM_START | |
| ) | |
| fds = _fds_skiptomatch(fds); | |
| fds++; | |
| } | |
| return n + 1; | |
| } | |
| /* | |
| * Returns a newly alloced string for name and type | |
| * The function ignores access specifiers | |
| */ | |
| int | |
| FDSAggregateFieldByNumber(char *fds, int n, char **name, char **type) | |
| { | |
| if ( (fds = _fds_skiptofield(fds, n)) == NULL ) | |
| return -1; | |
| if ( (*name = _field_attribute(fds, NULL, _fds_aggregate_field_name_end)) == NULL ) | |
| return -1; | |
| if ( (*type = _field_attribute(fds, _fds_aggregate_field_name_end, _fds_aggregate_field_end)) == NULL ) | |
| return -1; | |
| return 0; | |
| } | |
| /* | |
| * Returns a newly alloced string for type | |
| * The function ignores access specifiers | |
| */ | |
| int | |
| FDSAggregateFieldByName(char *fds, char *name, char **type) | |
| { | |
| char * nm; | |
| AGGREGATE_START(fds, -1); | |
| while ( *fds != '\0' && *fds != FDS_AGGREGATE_END ) { | |
| nm = fds; | |
| fds = _fds_skipto(fds, _fds_aggregate_field_name_end); | |
| if ( *fds == '\0' ) { | |
| return -1; | |
| } | |
| *fds = '\0'; /* temporarily */ | |
| if ( strcmp(nm, name) == 0 ) { | |
| *fds = FDS_AGGREGATE_FIELD_NAME_END; | |
| if ( (*type = _field_attribute(nm, | |
| _fds_aggregate_field_name_end, | |
| _fds_aggregate_field_end)) == NULL ) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| *fds = FDS_AGGREGATE_FIELD_NAME_END; | |
| fds = _fds_skipto(fds, _fds_aggregate_field_end); | |
| fds++; | |
| } | |
| return -1; | |
| } | |
| /* | |
| * Arrange the members of AIF aggregate defined by fds and data into a new_fds | |
| * and a new_data so that the FDS looks like fdsref | |
| * | |
| * If number_of_members of fdsref > number_of_members of fds | |
| * returns -1 | |
| * Else | |
| * creates the new_fds and new_data accordingly and returns 0 | |
| * | |
| * If fds got more members, the rest will be appended at the end, so | |
| * fdsref can be thought of as a prefix of fds. | |
| * | |
| * The function only works with the public section. | |
| * It does not change fdsref, fds and data. | |
| * It allocates the memory for new_fds and new_data | |
| */ | |
| int | |
| _fds_aggregate_arrange(char *fdsref, char *fds, char *data, char **new_fds, char **new_data) | |
| { | |
| char * ptr_fds = fds; | |
| char * ptr_data = data; | |
| int len_new_fds; | |
| int len_new_data; | |
| int number_fds = 0; /* number_of_members of fds */ | |
| int number_fdsref = 0; /* number_of_members of fdsref */ | |
| char * tmp; | |
| int n; | |
| char ** fds_members; | |
| char ** data_members; | |
| int * fds_members_len; | |
| int * data_members_len; | |
| int * fds_members_flag; /* to flag that the member has been copied */ | |
| /* calculate the number_of_members of fds and fdsref | |
| ** we do not use FDSNumFields() since we are only interested in the | |
| ** public section */ | |
| tmp = fds; | |
| AGGREGATE_START(tmp, -1); | |
| while ( *tmp != FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| tmp = strchr(tmp, FDS_AGGREGATE_FIELD_NAME_END) + 1; | |
| if | |
| ( | |
| *tmp == FDS_AGGREGATE_START || *tmp == FDS_UNION_START || | |
| *tmp == FDS_ARRAY_START || *tmp == FDS_ENUM_START | |
| ) | |
| tmp = _fds_skiptomatch(tmp) + 1; | |
| tmp = _fds_skipto(tmp, _fds_aggregate_field_end); | |
| number_fds++; | |
| } | |
| tmp = fdsref; | |
| AGGREGATE_START(tmp, -1); | |
| while ( *tmp != FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| tmp = strchr(tmp, FDS_AGGREGATE_FIELD_NAME_END) + 1; | |
| if | |
| ( | |
| *tmp == FDS_AGGREGATE_START || *tmp == FDS_UNION_START || | |
| *tmp == FDS_ARRAY_START || *tmp == FDS_ENUM_START | |
| ) | |
| tmp = _fds_skiptomatch(tmp) + 1; | |
| tmp = _fds_skipto(tmp, _fds_aggregate_field_end); | |
| number_fdsref++; | |
| } | |
| /* check for the number_of_member variables */ | |
| if ( number_fdsref > number_fds ) | |
| return -1; | |
| /* allocate new_fds and new_data */ | |
| _fds_skip_data(&ptr_fds, &ptr_data); | |
| len_new_fds = ptr_fds - fds; | |
| len_new_data = ptr_data - data; | |
| if (*new_fds == NULL) | |
| *new_fds = _aif_alloc(len_new_fds); | |
| if (*new_data == NULL) | |
| *new_data = _aif_alloc(len_new_data); | |
| /* we divide fds and data into several fields/members | |
| ** we store the pointers for easy retrieval */ | |
| fds_members = (char **) _aif_alloc(sizeof(char *) * number_fds); | |
| fds_members_len = (int *) _aif_alloc(sizeof(int) * number_fds); | |
| data_members = (char **) _aif_alloc(sizeof(char *) * number_fds); | |
| data_members_len = (int *) _aif_alloc(sizeof(int) * number_fds); | |
| fds_members_flag = (int *) _aif_alloc(sizeof(int) * number_fds); | |
| ptr_fds = fds; | |
| ptr_data = data; | |
| AGGREGATE_START(ptr_fds, -1); | |
| for (n=0; n<number_fds; n++) | |
| { | |
| fds_members[n] = ptr_fds; | |
| data_members[n] = ptr_data; | |
| ptr_fds = strchr(ptr_fds, FDS_AGGREGATE_FIELD_NAME_END) + 1; | |
| _fds_skip_data(&ptr_fds, &ptr_data); | |
| fds_members_len[n] = ptr_fds - fds_members[n]; | |
| data_members_len[n] = ptr_data - data_members[n]; | |
| fds_members_flag[n] = 0; | |
| ptr_fds++; | |
| } | |
| /* we start building up the new_fds and new_data */ | |
| ptr_fds = fds; | |
| AGGREGATE_START(ptr_fds, -1); | |
| memcpy(*new_fds, fds, ptr_fds - fds); /* copy the id to the new_fds */ | |
| ptr_fds = *new_fds + (ptr_fds - fds); | |
| ptr_data = *new_data; | |
| tmp = fdsref; | |
| AGGREGATE_START(tmp, -1); | |
| while ( *tmp != FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| char endchar, *end; | |
| if ( *tmp == FDS_AGGREGATE_FIELD_SEP ) tmp++; | |
| end = tmp; | |
| end = _fds_skipto(end, _fds_aggregate_field_end); | |
| endchar = *end; /* we store *end since *end can be ',' or ';' */ | |
| *end = '\0'; /* temporarily */ | |
| for (n=0; n<number_fds; n++) | |
| { | |
| /* we check the fds_members_flag so the function can | |
| ** run faster for large data structures */ | |
| if ( fds_members_flag[n] == 0 ) | |
| { | |
| /* tmp and fds_members[*] contain | |
| ** 'field_name=field_type', but we only want | |
| ** to compare field_name */ | |
| char * temp_a = strchr(tmp, FDS_AGGREGATE_FIELD_NAME_END); | |
| char * temp_b = strchr(fds_members[n], FDS_AGGREGATE_FIELD_NAME_END); | |
| int strcmp_res; | |
| *temp_a = *temp_b = '\0'; /* temporarily */ | |
| strcmp_res = strcmp(tmp, fds_members[n]); | |
| *temp_a = *temp_b = FDS_AGGREGATE_FIELD_NAME_END; | |
| if (strcmp_res == 0) | |
| break; | |
| } | |
| } | |
| if (n == number_fds) /* not found, an error */ | |
| { | |
| *end = endchar; | |
| _aif_free(fds_members); | |
| _aif_free(fds_members_len); | |
| _aif_free(fds_members_flag); | |
| _aif_free(data_members); | |
| _aif_free(data_members_len); | |
| _aif_free(*new_fds); | |
| _aif_free(*new_data); | |
| return -1; | |
| } | |
| memcpy(ptr_data, data_members[n], data_members_len[n]); | |
| memcpy(ptr_fds, fds_members[n], fds_members_len[n]); | |
| fds_members_flag[n] = 1; | |
| ptr_data += data_members_len[n]; | |
| ptr_fds += fds_members_len[n]; | |
| *end = endchar; | |
| *(ptr_fds++) = FDS_AGGREGATE_FIELD_SEP; | |
| tmp = end; | |
| } | |
| /* copy members which have not been copied */ | |
| if ( number_fds > number_fdsref ) | |
| { | |
| for (n=0; n<number_fds; n++) | |
| { | |
| if ( fds_members_flag[n] == 0 ) | |
| { | |
| memcpy(ptr_data, data_members[n], data_members_len[n]); | |
| memcpy(ptr_fds, fds_members[n], fds_members_len[n]); | |
| ptr_data += data_members_len[n]; | |
| ptr_fds += fds_members_len[n]; | |
| *(ptr_fds++) = FDS_AGGREGATE_FIELD_SEP; | |
| } | |
| } | |
| } | |
| /* copy the rest of the fds to the new_fds */ | |
| ptr_fds--; | |
| memcpy(ptr_fds, fds_members[number_fds-1] + fds_members_len[number_fds-1], len_new_fds - (ptr_fds - *new_fds)); | |
| _aif_free(fds_members); | |
| _aif_free(fds_members_len); | |
| _aif_free(fds_members_flag); | |
| _aif_free(data_members); | |
| _aif_free(data_members_len); | |
| return 0; | |
| } | |
| int | |
| FDSAggregateFieldSize(char *fds, char *name) | |
| { | |
| char *type; | |
| int size; | |
| if ( FDSAggregateFieldByName(fds, name, &type) ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return -1; | |
| } | |
| size = FDSTypeSize(type); | |
| _aif_free(type); | |
| return size; | |
| } | |
| /* | |
| * The function ignores access specifiers | |
| * return value indicates the position of the field | |
| * the counter starts at 0, ie: first element is at position 0 | |
| * if the field cannot be found, it returns -1 | |
| */ | |
| int | |
| FDSAggregateFieldIndex(char *fds, char *name) | |
| { | |
| int counter = 0; | |
| int len; | |
| char * nm; | |
| AGGREGATE_START(fds, -1); | |
| while ( *fds != '\0' && *fds != FDS_AGGREGATE_END ) | |
| { | |
| nm = fds; | |
| fds = _fds_skipto(fds, _fds_aggregate_field_name_end); | |
| if ( *fds == '\0' ) | |
| return -1; | |
| len = fds - nm; | |
| if ( strncmp(nm, name, len) == 0 ) | |
| return counter; | |
| counter++; | |
| fds = _fds_skipto(fds, _fds_aggregate_field_end); | |
| fds++; | |
| } | |
| return -1; | |
| } | |
| char * | |
| FDSAggregateInit(char *id) | |
| { | |
| return strdup(TypeToFDS(AIF_AGGREGATE, id)); | |
| } | |
| /* | |
| * returns the data length from field 0 until field n (inclusive) | |
| */ | |
| int | |
| _data_len_index(char *fds, int n) | |
| { | |
| int size = 0; | |
| int subsize = 0; | |
| AGGREGATE_START(fds, -1); | |
| while ( *fds != '\0' && *fds != FDS_AGGREGATE_END && n >= 0 ) | |
| { | |
| if ( *fds == FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| fds++; | |
| continue; | |
| } | |
| fds = _fds_skipto(fds, _fds_aggregate_field_name_end); | |
| if ( *fds++ == '\0' ) | |
| return 0; | |
| /* to start of field */ | |
| if ( (subsize = FDSTypeSize(fds)) < 0 ) | |
| return -1; | |
| size += subsize; | |
| n--; | |
| _fds_advance(&fds); | |
| if ( *fds == FDS_AGGREGATE_FIELD_SEP ) | |
| fds++; | |
| } | |
| return size; | |
| } | |
| int | |
| _data_len_public(char *fds) | |
| { | |
| int size = 0; | |
| int subsize = 0; | |
| if (*(fds) == FDS_NAME) { | |
| (fds) = _fds_skipto((fds), _fds_name_end); | |
| (fds)++; | |
| } | |
| if ( *(fds++) != FDS_AGGREGATE_START ) | |
| return -1; | |
| while ( *(fds) != FDS_TYPENAME_END ) | |
| (fds)++; | |
| (fds)++; | |
| if ( *(fds) == '\0' ) | |
| return -1; | |
| if ( *(fds) == FDS_AGGREGATE_ACCESS_SEP ) | |
| return 0; | |
| while ( *fds != FDS_AGGREGATE_END ) | |
| { | |
| if ( *fds == FDS_AGGREGATE_ACCESS_SEP ) | |
| break; | |
| fds = _fds_skipto(fds, _fds_aggregate_field_name_end); | |
| if ( *fds++ == '\0' ) | |
| return 0; | |
| /* to start of field */ | |
| if ( (subsize = FDSTypeSize(fds)) < 0 ) | |
| return -1; | |
| size += subsize; | |
| _fds_advance(&fds); | |
| if ( *fds == FDS_AGGREGATE_FIELD_SEP ) | |
| fds++; | |
| } | |
| return size; | |
| } | |
| /* | |
| * returns a pointer to a newly allocated structure | |
| */ | |
| char * | |
| _field_attribute(char *s, char *starter, char *ender) | |
| { | |
| int len; | |
| char * np; | |
| char * _fds_field_attr; /* we alloc as needed */ | |
| if ( starter != NULL ) | |
| { | |
| s = _fds_skipto(s, starter); | |
| if ( *s++ == '\0' ) | |
| return NULL; | |
| } | |
| np = s; | |
| s = _fds_skipto(s, ender); | |
| if ( *s == '\0' ) | |
| return NULL; | |
| len = s - np; | |
| _fds_field_attr = _aif_alloc(len+1); | |
| memcpy(_fds_field_attr, np, len); | |
| _fds_field_attr[len] = '\0'; | |
| return _fds_field_attr; | |
| } | |
| #define ENUM_START(fds, res) \ | |
| if (*(fds) == FDS_NAME) { \ | |
| (fds) = _fds_skipto((fds), _fds_name_end); \ | |
| (fds)++; \ | |
| } \ | |
| if ( *(fds++) != FDS_ENUM_START ) \ | |
| return (res); \ | |
| while ( *(fds) != FDS_TYPENAME_END ) \ | |
| (fds)++; \ | |
| (fds)++; \ | |
| if ( *(fds) == '\0' || *(fds) == FDS_ENUM_END ) \ | |
| return (res); | |
| /* based on the fds and the enum value, | |
| * the function gives the enum name (string), | |
| * it allocs the string for the caller */ | |
| int | |
| FDSEnumConstByValue(char *fds, char **name, int val) | |
| { | |
| int len; | |
| char * nm; | |
| ENUM_START(fds, -1); | |
| while ( *fds != '\0' && *fds != FDS_ENUM_END ) | |
| { | |
| nm = fds; | |
| fds = _fds_skipto(fds, _fds_enum_const_name_end); | |
| if ( *fds == '\0' ) | |
| return -1; | |
| len = fds - nm; | |
| fds++; | |
| if ( val == _fds_getnum(fds) ) | |
| { | |
| *name = _aif_alloc(len + 1); | |
| **name = '\0'; | |
| strncat(*name, nm, len); | |
| return 0; | |
| } | |
| fds = _fds_skipto(fds, _fds_enum_const_end); | |
| fds++; | |
| } | |
| return -1; | |
| } | |
| /* based on the fds and the enum name (string), | |
| * the function gives the enum value */ | |
| int | |
| FDSEnumConstByName(char *fds, char *name, int *val) | |
| { | |
| int len; | |
| char * nm; | |
| ENUM_START(fds, -1); | |
| while ( *fds != '\0' && *fds != FDS_ENUM_END ) | |
| { | |
| nm = fds; | |
| fds = _fds_skipto(fds, _fds_enum_const_name_end); | |
| if ( *fds == '\0' ) | |
| return -1; | |
| len = fds - nm; | |
| if ( strncmp(nm, name, len) == 0 ) | |
| { | |
| fds++; | |
| *val = _fds_getnum(fds); | |
| return 0; | |
| } | |
| fds = _fds_skipto(fds, _fds_enum_const_end); | |
| fds++; | |
| } | |
| return -1; | |
| } | |
| char * | |
| FDSEnumInit(char *id) | |
| { | |
| return strdup(TypeToFDS(AIF_ENUM, id, 1)); | |
| } | |
| char * | |
| FDSAddConstToEnum(char *fds, char *name, int val) | |
| { | |
| int v; | |
| char * nfmt; | |
| char * end; | |
| char field[BUFSIZ]; | |
| if ( FDSEnumConstByName(fds, name, &v) == 0 ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return NULL; | |
| } | |
| /* XXX TODO | |
| if ( FDSEnumFieldByValue(fds, name, &nfmt) == 0 ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| _aif_free(nfmt); | |
| return NULL; | |
| } | |
| */ | |
| snprintf(field, BUFSIZ, "%s%c%d", name, FDS_ENUM_SEP, val); | |
| nfmt = (char *)_aif_alloc(strlen(fds) + strlen(field) + 2); | |
| *nfmt = '\0'; | |
| strcpy(nfmt, fds); | |
| if | |
| ( | |
| (end = strrchr(nfmt, FDS_ENUM_END)) == NULL | |
| || | |
| end - nfmt < 1 | |
| ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return NULL; | |
| } | |
| if ( *(end-1) != FDS_TYPENAME_END ) | |
| *end++ = FDS_ENUM_CONST_SEP; | |
| sprintf(end, "%s%s", field, strrchr(fds, FDS_ENUM_END)); | |
| ResetAIFError(); | |
| return nfmt; | |
| } | |
| int | |
| FDSEnumAdd(char **fds, char *name, int val) | |
| { | |
| char * temp; | |
| temp = FDSAddConstToEnum(*fds, name, val); | |
| if ( temp == NULL ) | |
| return -1; | |
| _aif_free(*fds); | |
| *fds = temp; | |
| return 0; | |
| } | |
| #define UNION_START(fds, res) \ | |
| if (*(fds) == FDS_NAME) { \ | |
| (fds) = _fds_skipto((fds), _fds_name_end); \ | |
| (fds)++; \ | |
| } \ | |
| if ( *(fds++) != FDS_UNION_START ) \ | |
| return (res); \ | |
| while ( *(fds) != FDS_TYPENAME_END ) \ | |
| (fds)++; \ | |
| (fds)++; \ | |
| if ( *(fds) == '\0' || *(fds) == FDS_UNION_END ) \ | |
| return (res); | |
| int | |
| FDSUnionFieldByName(char *fds, char *name, char **type) | |
| { | |
| int len; | |
| char * nm; | |
| UNION_START(fds, -1); | |
| while ( *fds != '\0' && *fds != FDS_UNION_END ) | |
| { | |
| nm = fds; | |
| fds = _fds_skipto(fds, _fds_union_field_name_end); | |
| if ( *fds == '\0' ) | |
| return -1; | |
| len = fds - nm; | |
| if ( strncmp(nm, name, len) == 0 ) | |
| { | |
| fds++; | |
| nm = fds; | |
| fds = _fds_skipto(fds, _fds_union_field_end); | |
| if ( *fds == '\0' ) | |
| return -1; | |
| len = fds - nm; | |
| *type = _aif_alloc(len+1); | |
| strncpy(*type, nm, len); | |
| (*type)[len] = '\0'; | |
| return 0; | |
| } | |
| fds = _fds_skipto(fds, _fds_union_field_end); | |
| fds++; | |
| } | |
| return -1; | |
| } | |
| char * | |
| FDSUnionInit(char *id) | |
| { | |
| return strdup(TypeToFDS(AIF_UNION, id)); | |
| } | |
| char * | |
| FDSAddFieldToUnion(char *fds, char *name, char *type) | |
| { | |
| char * nfmt; | |
| char * end; | |
| char field[BUFSIZ]; | |
| char * dummy; | |
| if ( FDSUnionFieldByName(fds, name, &dummy) == 0 ) | |
| { | |
| _aif_free(dummy); | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return NULL; | |
| } | |
| snprintf(field, BUFSIZ, "%s%c%s", name, FDS_UNION_FIELD_NAME_END, type); | |
| nfmt = (char *)_aif_alloc(strlen(fds) + strlen(field) + 2); | |
| *nfmt = '\0'; | |
| strcpy(nfmt, fds); | |
| if | |
| ( | |
| (end = strrchr(nfmt, FDS_UNION_END)) == NULL | |
| || | |
| end - nfmt < 1 | |
| ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return NULL; | |
| } | |
| if ( *(end-1) != FDS_TYPENAME_END ) | |
| *end++ = FDS_UNION_FIELD_SEP; | |
| sprintf(end, "%s%c", field, FDS_UNION_END); | |
| ResetAIFError(); | |
| return nfmt; | |
| } | |
| int | |
| FDSUnionAdd(char **fds, char *name, char *type) | |
| { | |
| char * temp; | |
| temp = FDSAddFieldToUnion(*fds, name, type); | |
| if ( temp == NULL ) | |
| return -1; | |
| _aif_free(*fds); | |
| *fds = temp; | |
| return 0; | |
| } | |
| /* | |
| * Add the field to the end of the section specified by the access | |
| * qualifier. | |
| */ | |
| char * | |
| FDSAddFieldToAggregate(char *fds, AIFAccess acc, char *name, char *type) | |
| { | |
| char * temp; | |
| char * nfmt; | |
| char * end; | |
| char * rest; | |
| if (FDSAggregateFieldByName(fds, name, &nfmt) == 0) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| _aif_free(nfmt); | |
| return NULL; | |
| } | |
| nfmt = (char *)_aif_alloc(strlen(fds) + strlen(name) + strlen(type) +3); | |
| *nfmt = '\0'; | |
| strcpy(nfmt, fds); | |
| temp = nfmt; | |
| if (*fds == FDS_NAME) { | |
| fds = _fds_skipto(fds, _fds_name_end); | |
| fds++; | |
| } | |
| if (*temp == FDS_NAME) { | |
| temp = _fds_skipto(temp, _fds_name_end); | |
| temp++; | |
| } | |
| if | |
| ( | |
| (end = _fds_skipto(temp+1, _fds_aggregate_access_sep)) == NULL | |
| || | |
| end - temp < 1 | |
| ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| _aif_free(nfmt); | |
| return NULL; | |
| } | |
| rest = _fds_skipto(fds+1, _fds_aggregate_access_sep); | |
| switch ( acc ) | |
| { | |
| case AIF_ACCESS_PACKAGE: | |
| end = _fds_skipto(++end, _fds_aggregate_access_sep); | |
| if ( end == NULL ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| _aif_free(nfmt); | |
| return NULL; | |
| } | |
| rest = _fds_skipto(++rest, _fds_aggregate_access_sep); | |
| /* fall through */ | |
| case AIF_ACCESS_PRIVATE: | |
| end = _fds_skipto(++end, _fds_aggregate_access_sep); | |
| if ( end == NULL ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| _aif_free(nfmt); | |
| return NULL; | |
| } | |
| rest = _fds_skipto(++rest, _fds_aggregate_access_sep); | |
| /* fall through */ | |
| case AIF_ACCESS_PROTECTED: | |
| end = _fds_skipto(++end, _fds_aggregate_access_sep); | |
| if ( end == NULL ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| _aif_free(nfmt); | |
| return NULL; | |
| } | |
| rest = _fds_skipto(++rest, _fds_aggregate_access_sep); | |
| /* fall through */ | |
| case AIF_ACCESS_PUBLIC: | |
| case AIF_ACCESS_UNKNOWN: | |
| break; | |
| } | |
| if ( *(end-1) != FDS_TYPENAME_END && *(end-1) != FDS_AGGREGATE_ACCESS_SEP ) | |
| *end++ = FDS_AGGREGATE_FIELD_SEP; | |
| sprintf(end, "%s%c%s%s", | |
| name, | |
| FDS_AGGREGATE_FIELD_NAME_END, | |
| type, | |
| rest); | |
| ResetAIFError(); | |
| return nfmt; | |
| } | |
| int | |
| FDSAggregateAdd(char **fds, AIFAccess acc, char *name, char *type) | |
| { | |
| char * temp; | |
| temp = FDSAddFieldToAggregate(*fds, acc, name, type); | |
| if ( temp == NULL ) | |
| return -1; | |
| _aif_free(*fds); | |
| *fds = temp; | |
| return 0; | |
| } | |
| int | |
| FDSSetIdentifier(char **fds, char *id) | |
| { | |
| int id_len; | |
| char * temp; | |
| char * new; | |
| temp = *fds; | |
| id_len = strlen(id); | |
| new = _aif_alloc( strlen(*fds) + strlen(id) + 1 ); | |
| *new = '\0'; | |
| if ( *temp == FDS_NAME ) | |
| { | |
| temp = _fds_skipto(temp, _fds_name_end); | |
| temp++; | |
| strncat(new, *fds, temp - *fds); | |
| } | |
| if ( *temp == FDS_UNION_START || | |
| *temp == FDS_AGGREGATE_START || | |
| *temp == FDS_ENUM_START ) | |
| { | |
| if ( *(temp+1) != FDS_TYPENAME_END ) | |
| { | |
| _aif_free(new); | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return -1; | |
| } | |
| strncat(new, temp, 1); | |
| strncat(new, id, id_len); | |
| strcat(new, temp+1); | |
| _aif_free(*fds); | |
| *fds = new; | |
| return 0; | |
| } | |
| else | |
| { | |
| _aif_free(new); | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return -1; | |
| } | |
| } | |
| char * | |
| FDSGetIdentifier(char *fds) | |
| { | |
| char *temp; | |
| char *id; | |
| int id_len; | |
| if ( *fds == FDS_NAME ) | |
| { | |
| fds = _fds_skipto(fds, _fds_name_end); | |
| fds++; | |
| } | |
| if ( *fds == FDS_UNION_START || | |
| *fds == FDS_AGGREGATE_START || | |
| *fds == FDS_ENUM_START ) | |
| { | |
| fds++; | |
| if ( *fds == FDS_TYPENAME_END ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return NULL; | |
| } | |
| temp = fds; | |
| fds = strchr(fds, FDS_TYPENAME_END); | |
| id_len = fds - temp; | |
| id = _aif_alloc( id_len + 1 ); | |
| *id = '\0'; | |
| strncat(id, temp, id_len); | |
| return id; | |
| } | |
| else | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return NULL; | |
| } | |
| } | |
| void | |
| _fds_advance(char **fds) | |
| { | |
| switch ( FDSType((*fds)++) ) | |
| { | |
| case AIF_ADDRESS: | |
| case AIF_BOOLEAN: | |
| *fds = _fds_skipnum(*fds); | |
| break; | |
| case AIF_ENUM: | |
| _fds_skip_typename(fds); | |
| *fds = _fds_skipto(*fds, _fds_enum_end); | |
| (*fds) += 2; | |
| /* fall through */ | |
| case AIF_INTEGER: | |
| *fds += 1; /* u or s */ | |
| *fds = _fds_skipnum(*fds); | |
| break; | |
| case AIF_FLOATING: | |
| case AIF_VOID: | |
| *fds = _fds_skipnum(*fds); | |
| break; | |
| case AIF_FUNCTION: | |
| while ( *(*fds)++ != FDS_FUNCTION_ARG_END ) | |
| ; | |
| _fds_advance(fds); | |
| break; | |
| case AIF_ARRAY: | |
| while ( *(*fds)++ != FDS_ARRAY_END ) | |
| ; /* past the index type */ | |
| _fds_advance(fds); /* past the base type */ | |
| break; | |
| case AIF_UNION: | |
| case AIF_AGGREGATE: | |
| _fds_skip_typename(fds); | |
| for ( ;; ) { | |
| if ( **fds == FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| (*fds)++; | |
| continue; | |
| } | |
| if ( **fds == FDS_AGGREGATE_END ) | |
| break; | |
| while ( *(*fds)++ != FDS_AGGREGATE_FIELD_NAME_END ) | |
| ; /* past the field name */ | |
| _fds_advance(fds); /* past the field type */ | |
| if ( **fds == FDS_AGGREGATE_FIELD_SEP ) | |
| (*fds)++; | |
| } | |
| (*fds)++; /* past closing brace */ | |
| break; | |
| case AIF_NAME: | |
| *fds = _fds_skipnum(*fds); | |
| (*fds)++; /* past closing '/' */ | |
| _fds_advance(fds); /* past the named type */ | |
| break; | |
| case AIF_POINTER: | |
| _fds_advance(fds); /* past the address */ | |
| _fds_advance(fds); /* past the pointed-to type */ | |
| break; | |
| case AIF_CHAR_POINTER: | |
| _fds_advance(fds); /* past the address */ | |
| break; | |
| case AIF_REFERENCE: | |
| *fds = _fds_skipnum(*fds); | |
| (*fds)++; /* past closing '/' */ | |
| break; | |
| case AIF_CHARACTER: | |
| case AIF_STRING: | |
| break; /* already skipped all we should */ | |
| default: | |
| fprintf(stderr, "no way to advance ... \"%s\"\n", *fds); | |
| break; | |
| } | |
| } | |
| /* | |
| * **fds is integer, float, or character. Return how many bytes it | |
| * has and advance *fds to its end | |
| */ | |
| int | |
| _fds_count_bytes(char **fds) | |
| { | |
| int bytes; | |
| switch ( **fds ) | |
| { | |
| case FDS_FLOATING: | |
| (*fds)++; /* past f */ | |
| bytes = _fds_getnum(*fds); | |
| *fds = _fds_skipnum(*fds); /* past size indicator */ | |
| break; | |
| case FDS_ENUM_START: | |
| *fds = _fds_skipto(++(*fds), _fds_enum_end); | |
| (*fds)++; | |
| /* fall through */ | |
| case FDS_INTEGER: | |
| *fds += 2; /* past iu or is */ | |
| bytes = _fds_getnum(*fds); | |
| *fds = _fds_skipnum(*fds); /* past size indicator */ | |
| break; | |
| case FDS_CHARACTER: | |
| (*fds)++; /* past c */ | |
| bytes = 1; | |
| break; | |
| case FDS_BOOLEAN: | |
| case FDS_ADDRESS: | |
| (*fds)++; /* past b or a */ | |
| bytes = _fds_getnum(*fds); | |
| *fds = _fds_skipnum(*fds); /* past size indicator */ | |
| break; | |
| case FDS_VOID: | |
| (*fds)++; /* past v */ | |
| bytes = _fds_getnum(*fds); | |
| *fds = _fds_skipnum(*fds); /* past size indicator */ | |
| break; | |
| default: | |
| bytes = -1; /* internal error */ | |
| } | |
| return bytes; | |
| } | |
| /* | |
| * Count bytes but don't advance pointer | |
| */ | |
| int | |
| _fds_count_bytes_na(char **fds) | |
| { | |
| char * fmt = *fds; | |
| return _fds_count_bytes(&fmt); | |
| } | |
| void | |
| _fds_resolve(char **fmt) | |
| { | |
| int index; | |
| while ( **fmt == FDS_NAME ) | |
| { | |
| (*fmt)++; | |
| index = (int)strtol(*fmt, NULL, 10); | |
| *fmt = (char *)_fds_skipnum(*fmt)+1; /* past name, trailing slash */ | |
| _aif_types_seen[index+MAX_TYPES_SEEN/2] = *fmt; | |
| } | |
| } | |
| char * | |
| _fds_lookup(char **fmt) | |
| { | |
| int index; | |
| (*fmt)++; | |
| index = (int)strtol(*fmt, NULL, 10); | |
| *fmt = (char *)_fds_skipnum(*fmt)+1; /* past name, trailing slash */ | |
| return _aif_types_seen[index+MAX_TYPES_SEEN/2]; | |
| } | |
| /* | |
| * Find the target data for a pointer. | |
| * | |
| * @param fds format descriptor for pointer (need to calculate address size). At the | |
| * end of the call, fds will be advanced to the beginning of the target type | |
| * @param data points to the data part of an AIF. At the end of the call, data | |
| * will be advanced to the beggining of the target data | |
| * @param index a unique index for saving/retrieving values in order | |
| * to simultaneously maintain targets for multiple AIFs. | |
| * @return a pointer to the data region where the target data actually | |
| * starts, or 0 if null or invalid pointer | |
| * | |
| * Format of the data is: | |
| * <type>[<name>][<address>[<target>]] | |
| * | |
| * Where: | |
| * | |
| * <type> is a single byte: | |
| * 0: null pointer, no data | |
| * 1: ordinary pointer, data contains <address> and <target> | |
| * 2: named pointer, data contains <name>, <address> and <target> | |
| * 3: pointer reference, data contains <name> | |
| * 4: invalid pointer, data contains <address> | |
| * <name> is an unsigned 4 byte integer | |
| * <address> is the address of the pointer | |
| * <target> is the target data | |
| */ | |
| char * | |
| _find_target(char **fds, char **data, int index) | |
| { | |
| int name; | |
| char code = **data; | |
| char * res; | |
| (*fds)++; /* past the ^ */ | |
| (*data)++; /* past the code */ | |
| switch ( code ) | |
| { | |
| case AIF_PTR_NIL: | |
| _fds_advance(fds); /* skip address type */ | |
| return (char *)0; | |
| case AIF_PTR_INVALID: /* invalid pointer value */ | |
| _fds_skip_data(fds, data); /* skip address */ | |
| return (char *)0; | |
| case AIF_PTR_NORMAL: /* normal pointer value */ | |
| _fds_skip_data(fds, data); /* skip address */ | |
| res = *data; | |
| return res; | |
| case AIF_PTR_NAME: /* named value */ | |
| _ptrname_to_int(data, &name); | |
| _fds_skip_data(fds, data); /* skip address type */ | |
| _aif_values_seen[name+index*MAX_VALUES_SEEN/2] = *data; | |
| return *data; | |
| case AIF_PTR_REFERENCE: /* reference to named value */ | |
| _ptrname_to_int(data, &name); | |
| _fds_advance(fds); /* skip address type */ | |
| return _aif_values_seen[name+index*MAX_VALUES_SEEN/2]; | |
| } | |
| return 0; /* should never fall through */ | |
| } | |
| /* | |
| * Get the pointer type. | |
| */ | |
| int | |
| _get_pointer_type(char *data) | |
| { | |
| return (int)(*data); | |
| } | |
| /* | |
| * Get the pointer name. | |
| */ | |
| int | |
| _get_pointer_name(char *data) | |
| { | |
| int name = 0; | |
| int type = _get_pointer_type(data); | |
| if (type == AIF_PTR_NAME || type == AIF_PTR_REFERENCE) { | |
| data++; /* skip code */ | |
| _ptrname_to_int(&data, &name); | |
| } | |
| return name; | |
| } | |
| /* | |
| * Skip over one full data unit, specified by the fds, | |
| * At return, fds should be at its end, and data is advanced | |
| * to point to the next byte following the end of the data. | |
| */ | |
| void | |
| _fds_skip_data(char **fds, char **data) | |
| { | |
| int i; | |
| int bytes; | |
| AIFIndex * ix; | |
| char * fmt; | |
| _fds_resolve(fds); | |
| switch ( FDSType(*fds) ) | |
| { | |
| case AIF_REFERENCE: | |
| fmt = _fds_lookup(fds); // need a temporary copy | |
| _fds_skip_data(&fmt, data); | |
| return; | |
| case AIF_POINTER: | |
| if (_find_target(fds, data, 0) == 0) | |
| _fds_advance(fds); | |
| else | |
| _fds_skip_data(fds, data); | |
| return; | |
| case AIF_VOID: | |
| case AIF_CHARACTER: | |
| case AIF_INTEGER: | |
| case AIF_BOOLEAN: | |
| case AIF_FLOATING: | |
| case AIF_ADDRESS: | |
| case AIF_ENUM: | |
| (*data) += _fds_count_bytes(fds); | |
| return; | |
| case AIF_ARRAY: | |
| ix = FDSArrayIndexInit(*fds); | |
| _fds_advance(fds); /* skip over the entire array fds */ | |
| for ( i = 0 ; i < ix->i_nel ; i++ ) | |
| { | |
| fmt = ix->i_btype; | |
| _fds_skip_data(&fmt, data); | |
| } | |
| AIFArrayIndexFree(ix); | |
| return; | |
| case AIF_AGGREGATE: | |
| (*fds)++; /* past FDS_AGGREGATE_START */ | |
| _fds_skip_typename(fds); | |
| while ( **fds != FDS_AGGREGATE_END ) | |
| { | |
| if ( **fds == FDS_AGGREGATE_ACCESS_SEP ) | |
| { | |
| (*fds)++; | |
| continue; | |
| } | |
| *fds = strchr(*fds, FDS_AGGREGATE_FIELD_NAME_END) + 1; | |
| /* to start of field */ | |
| _fds_skip_data(fds, data); | |
| if ( **fds == FDS_AGGREGATE_FIELD_SEP ) | |
| (*fds)++; | |
| } | |
| (*fds)++; /* past FDS_AGGREGATE_END */ | |
| return; | |
| case AIF_UNION: | |
| (*fds)++; /* past FDS_UNION_START */ | |
| _fds_skip_typename(fds); | |
| while ( **fds != FDS_UNION_END ) | |
| { | |
| *fds = strchr(*fds, FDS_UNION_FIELD_NAME_END) + 1; | |
| /* to start of field */ | |
| _fds_skip_data(fds, data); | |
| if ( **fds == FDS_UNION_FIELD_SEP ) | |
| (*fds)++; | |
| } | |
| (*fds)++; /* past FDS_UNION_END */ | |
| return; | |
| case AIF_CHAR_POINTER: | |
| (*fds)++; /* past p */ | |
| _fds_skip_data(fds, data); /* past address */ | |
| bytes = (*(*data)++ & 0xff) << 8; | |
| bytes += *(*data)++ & 0xff; | |
| *data += bytes; | |
| return; | |
| case AIF_STRING: | |
| (*fds)++; /* past s */ | |
| bytes = (*(*data)++ & 0xff) << 8; | |
| bytes += *(*data)++ & 0xff; | |
| *data += bytes; | |
| return; | |
| case AIF_FUNCTION: | |
| while ( *(*fds)++ != FDS_FUNCTION_ARG_END ) | |
| ; | |
| _fds_advance(fds); | |
| while ( **data != '\0' ) | |
| (*data)++; | |
| (*data)++; /* past null */ | |
| return; | |
| default: | |
| SetAIFError(AIFERR_TYPE, NULL); | |
| return; | |
| } | |
| } | |
| /* | |
| * Correctly calculate the actual size of a type. | |
| * Requires the data in order to be able to deal | |
| * with variable length types. | |
| */ | |
| int | |
| _fds_type_size(char *fds, char *data) | |
| { | |
| char * f = fds; | |
| char * d = data; | |
| _fds_skip_data(&f, &d); | |
| return d - data; | |
| } | |
| /* | |
| * Skip the typename field. On return, fds will point | |
| * to the character after '|' or NULL if the | |
| * '|' is not found. | |
| */ | |
| void | |
| _fds_skip_typename(char **fds) | |
| { | |
| if (**fds != FDS_TYPENAME_END) { | |
| *fds = strchr(*fds, FDS_TYPENAME_END); | |
| } | |
| if (*fds != NULL) { | |
| (*fds)++; | |
| } | |
| } | |
| char * | |
| _fds_add_function_arg(char *fds, char *arg) | |
| { | |
| char * nfmt; | |
| char * temp; | |
| char * rest; | |
| if ( FDSType(fds) != AIF_FUNCTION ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| return NULL; | |
| } | |
| nfmt = (char *)_aif_alloc(strlen(fds) + strlen(arg) + 2); | |
| *nfmt = '\0'; | |
| strcpy(nfmt, fds); | |
| if ( (temp = _fds_skipto(nfmt, _fds_function_arg_end)) == NULL ) | |
| { | |
| SetAIFError(AIFERR_BADARG, NULL); | |
| _aif_free(nfmt); | |
| return NULL; | |
| } | |
| rest = _fds_skipto(fds, _fds_function_arg_end); | |
| if ( temp != (nfmt + 1) ) | |
| *temp++ = FDS_FUNCTION_ARG_SEP; | |
| sprintf(temp, "%s%s", | |
| arg, | |
| rest); | |
| ResetAIFError(); | |
| return nfmt; | |
| } | |