/************************************************************************* * allocate_db_connection( ) . allocates a hDBC struct with the given ID * push_db_connection( ) ..... adds an allocated hDBC struct to the DB * connections * pop_db_connection( ) ...... removes an allocated hDBC struct from * the DB connections * get_db_connection_by_id( ) returns the requested handle and moves it on * top of hODBCEnv->hDBCons * dispose_db_connection( ) .. disposes the passed connection *************************************************************************/ static hDBC * allocate_db_connection( int ID ) { hDBC * handle; if ( !ID ) return( NULL ); handle = pxalloc( sizeof( hDBC ) ); MEM_CHECK( handle ); handle->ID = ID; handle->name = NULL; handle->prev = NULL; handle->next = NULL; handle->hDBCon = NULL; handle->hStmt = NULL; handle->colcnt = 0; handle->rowcnt = 0; handle->columns = NULL; return( handle ); }
/************************************************************************* * extracts the dignosticinfo of an handle. *************************************************************************/ static char * extract_diagnostics_info( SQLSMALLINT type, SQLHANDLE handle ) { char ** tmp; char * ret; int i, j = 0; SQLINTEGER recnum; SQLGetDiagField( type, handle, 0, SQL_DIAG_NUMBER, &recnum, SQL_IS_INTEGER, 0 ); if ( !recnum ) return( NULL ); tmp = pxalloc( recnum * sizeof( tmp ) ); SQLCHAR state[ 7 ]; SQLINTEGER native; SQLCHAR text[1000], buffer[1000]; SQLSMALLINT len; for ( i = 0; i < recnum; i++ ) { SQLGetDiagRec( type, handle, i+1, state, &native, text, sizeof( text ), &len ); sprintf( buffer, "[%5s]%s\n", state, text ); tmp[ i ] = string_copy( buffer ); j += strlen( tmp[ i ] ); } ret = pxalloc( (j + 1) * sizeof( char ) ); for ( i = 0; i < recnum; i++ ) { if( i ) { strcat( ret, tmp[ i ] ); } else { strcpy( ret, tmp[ i ] ); } pfree( tmp[ i ] ); } pfree( tmp ); return( ret ); }
static mapping_t * fetch_into_mapping( hDBC * handle ) { int i; mapping_t * map; svalue_t * key, * value; STORE_DOUBLE_USED; map = allocate_mapping( handle->colcnt, 1 ); MEM_CHECK( map ); for( i = 0; i < handle->colcnt; ++i ) { if ( !handle->columns[ i ] ) continue; //printf( " fetch_into_mapping[%2d] ", i ); key = pxalloc( sizeof( svalue_t ) ); MEM_CHECK( key ); put_malloced_string( key, string_copy( handle->columns[ i ]->name ) ); value = get_map_lvalue( map, key ); MEM_CHECK( value ); switch( handle->columns[ i ]->type ){ case T_FLOAT: //printf( "float=%f\n", handle->columns[ i ]->data.double_v ); STORE_DOUBLE( value, *handle->columns[ i ]->data.double_v ); value->type = T_FLOAT; break; case T_NUMBER: //printf( "number=%d\n", *handle->columns[ i ]->data.number_v ); put_number( value, *handle->columns[ i ]->data.number_v ); break; case T_STRING: default : //printf( "string=%s\n", handle->columns[ i ]->data.string_v ); put_malloced_string( value, string_copy( handle->columns[ i ]->data.string_v ) ); break; } } return( map ); }
/************************************************************************* * allocate_column_meta_data( ) . allocates a COL_META_DATA struct * dispose_column_meta_data( ) .. disposes a COL_META_DATA struct *************************************************************************/ static COL_META_DATA * allocate_column_meta_data( void ) { COL_META_DATA * column; column = pxalloc( sizeof( COL_META_DATA ) ); MEM_CHECK( column ); column->nr = 0; column->name = NULL; column->type = 0; column->data.number_v = NULL; column->data.double_v = NULL; column->data.string_v = NULL; return( column ); }
static char * init_odbc_environment( void ) { #if ODBC_DEBUG & DEBUG_FUNC printf( "call init_odbc_environment( )\n" ); #endif SQLRETURN ret; if ( hODBCEnv ) // already initialized return( NULL ); hODBCEnv = pxalloc( sizeof( hODBCENV ) ); MEM_CHECK( hODBCEnv ); ret = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(hODBCEnv->hODBCEnv) ); if ( !SQL_SUCCEEDED( ret ) ) { destruct_odbc_environment(); return( "Allocation of ODBC Environment failed.\n" ); } ret = SQLSetEnvAttr( hODBCEnv->hODBCEnv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0 ); if ( !SQL_SUCCEEDED( ret ) ) { destruct_odbc_environment(); return( "Could not set ODBC Environment Attributes.\n" ); } hODBCEnv->next_hDBCon_ID = 1; hODBCEnv->hDBCons = NULL; #if ODBC_DEBUG & DEBUG_FUNC printf( "return init_odbc_environment( )\n" ); #endif return( NULL ); }
/*-------------------------------------------------------------------------*/ static sqlite_dbs_t * new_db() /* Create a new database entry, link it into the global list, and return it. * On out of memory, return NULL. */ { sqlite_dbs_t *tmp; tmp = pxalloc (sizeof (*tmp)); if (!tmp) return NULL; tmp->db = NULL; tmp->obj = NULL; tmp->next = NULL; tmp->prev = head; if (head) head->next=tmp; head=tmp; return tmp; } /* new_db() */
/************************************************************************* * f_sql_exec( int handle, string statement ) * * executes an SQL statement *************************************************************************/ svalue_t * f_sql_exec( svalue_t * argv, int argc ) { #if ODBC_DEBUG & DEBUG_FUNC printf( "call f_sql_exec( )\n" ); #endif int id, i; SQLCHAR * statement; hDBC * handle; //SQLSMALLINT cols; SQLRETURN ret; TYPE_TEST2( argv, T_STRING ); statement = string_copy( argv->u.string ); free_string_svalue( argv ); argv--; TYPE_TEST1( argv, T_NUMBER ); id = argv->u.number; free_svalue( argv ); if ( !(handle = get_db_connection_by_id( id )) ) { pfree( statement ); errorf( "Illegal handle for database.\n" ); return( NULL ); } if ( handle->hStmt ) { //printf( "freeing statement\n" ); ret = SQLFreeStmt( handle->hStmt, SQL_UNBIND ); if ( !SQL_SUCCEEDED( ret ) ) { //printf( "SQLFreeStmt( handle->hStmt, SQL_UNBIND ) = %d\n", ret ); pfree( statement ); errorf( extract_diagnostics_info( SQL_HANDLE_STMT, handle->hStmt ) ); return( NULL ); } ret = SQLFreeHandle( SQL_HANDLE_STMT, handle->hStmt ); if ( !SQL_SUCCEEDED( ret ) ) { //printf( "SQLFreeHandle( SQL_HANDLE_STMT, handle->hStmt ) = %d\n", ret ); pfree( statement ); errorf( extract_diagnostics_info( SQL_HANDLE_STMT, handle->hStmt ) ); return( NULL ); } } if ( handle->columns ) { for ( i = 0; i < handle->colcnt; ++i ) { dispose_column_meta_data( handle->columns[ i ] ); } pfree( handle->columns ); handle->columns = NULL; } //printf( "allocating statement \n" ); ret = SQLAllocHandle( SQL_HANDLE_STMT, handle->hDBCon, &handle->hStmt ); if ( !SQL_SUCCEEDED( ret ) ) { pfree( statement ); errorf( extract_diagnostics_info( SQL_HANDLE_DBC, handle->hDBCon ) ); return( NULL ); } handle->colcnt = 0; handle->rowcnt = 0; // printf( "executing\n" ); ret = SQLExecDirect( handle->hStmt, statement, SQL_NTS ); pfree( statement ); if ( !SQL_SUCCEEDED( ret ) ) { //printf( "XXX: %s\n", extract_diagnostics_info( SQL_HANDLE_STMT, handle->hStmt ) ); put_number( argv, 0 ); return( argv ); } /* getting number of columns. */ //ret = SQLNumResultCols( handle->hStmt, &cols ); ret = SQLNumResultCols( handle->hStmt, &handle->colcnt ); if ( !SQL_SUCCEEDED( ret ) ) { put_number( argv, 0 ); return( argv ); } ret = SQLRowCount( handle->hStmt, &handle->rowcnt ); if ( !SQL_SUCCEEDED( ret ) ) { put_number( argv, 0 ); return( argv ); } //handle->colcnt = cols; if ( handle->colcnt ) { handle->columns = pxalloc( handle->colcnt * sizeof( COL_META_DATA* ) ); MEM_CHECK( handle->columns ); } /* fetching meta data */ COL_META_DATA * tmp; SQLCHAR dColname[ 100 ]; SQLSMALLINT dColnameLen; SQLSMALLINT dType; SQLSMALLINT dDDigits; SQLSMALLINT dNullable; SQLUINTEGER dColSize; for ( i = 1; i <= handle->colcnt; ++i ) { ret = SQLDescribeCol( handle->hStmt, i, dColname, sizeof( dColname ), &dColnameLen, &dType, &dColSize, &dDDigits, &dNullable ); if ( !SQL_SUCCEEDED( ret ) ) { put_number( argv, 0 ); return( argv ); } tmp = allocate_column_meta_data(); MEM_CHECK( tmp ); tmp->nr = i; tmp->name = string_copy( dColname ); tmp->type = map_column_type( dType, dDDigits ); // printf( "[%s] dColSize=%d dDDigits=%d\n", dColname, dColSize, dDDigits ); SQLLEN len; switch( tmp->type ) { case T_NUMBER: tmp->data.number_v = pxalloc( sizeof( SQL_C_LONG ) ); *tmp->data.number_v = 0; SQLBindCol( handle->hStmt, i, SQL_C_LONG, tmp->data.number_v, 100, &len ); break; case T_FLOAT: tmp->data.double_v = pxalloc( sizeof( SQL_C_DOUBLE ) ); *tmp->data.double_v = 0; SQLBindCol( handle->hStmt, i, SQL_C_DOUBLE, tmp->data.double_v, 100, &len ); break; default: tmp->data.string_v = pxalloc( (dColSize + 1) * sizeof( SQLCHAR ) ); SQLBindCol( handle->hStmt, i, SQL_C_CHAR, tmp->data.string_v, (dColSize + 1), &len ); } handle->columns[ i-1 ] = tmp; } put_number( argv, id ); #if ODBC_DEBUG & DEBUG_FUNC printf( "ret f_sql_exec( )\n" ); #endif return( argv ); }
/************************************************************************* * f_sql_odbc_datasources( void ) * * returns a mapping of all defined datasources. * ([ name : description ]) *************************************************************************/ svalue_t * f_sql_odbc_datasources( svalue_t * argv, int argc ) { #if ODBC_DEBUG & DEBUG_FUNC printf( "call f_sql_odbc_datasources( )\n" ); #endif SQLRETURN ret; char * err; char dsName[ 256 ], dsDescription[ 256 ]; SQLSMALLINT dsNameLen, dsDescriptionLen; mapping_t * map; svalue_t * key; svalue_t * value; argv++; err = init_odbc_environment(); if ( err ) { errorf( err ); return( NULL ); } map = allocate_mapping( 0, 1 ); MEM_CHECK( map ); do { ret = SQLDataSources( hODBCEnv->hODBCEnv, SQL_FETCH_NEXT, dsName, 256, &dsNameLen, dsDescription, 256, &dsDescriptionLen ); if ( SQL_SUCCEEDED( ret ) ) { key = pxalloc( sizeof( svalue_t ) ); put_malloced_string( key, string_copy( dsName ) ); value = get_map_lvalue( map, key ); MEM_CHECK( value ); put_malloced_string( value, string_copy( dsDescription ) ); } } while( SQL_SUCCEEDED( ret ) ); if ( ret != SQL_NO_DATA ) { char * err; err = extract_diagnostics_info( SQL_HANDLE_ENV, hODBCEnv->hODBCEnv ); free_mapping( map ); errorf( (err?err:"Failed to fetch datasource information.\n") ); return( NULL ); } put_mapping( argv, map ); #if ODBC_DEBUG & DEBUG_FUNC printf( "ret f_sql_odbc_datasources( )\n" ); #endif return( argv ); }
/*-------------------------------------------------------------------------*/ svalue_t * v_sl_exec (svalue_t * sp, int num_arg) /* EFUN sl_exec() * * mixed* sl_exec(string statement, ...) * * Executes the SQL statement <statement> for the current * SQLite database. The SQL statement may contain wildcards like * '?' and '?nnn', where 'nnn' is an integer. These wildcards * can be given as further parameters to sl_exec. With '?nnn' * the number of a specific parameter can be given, the first * parameter has number 1. * * If the statement returns data, sl_exec returns an array * with each row (which is itself an array of columns) as * an element. */ { svalue_t *argp; sqlite_dbs_t *db; sqlite3_stmt *stmt; const char* tail; int err, rows, cols, num; struct sl_exec_cleanup_s * rec_data; vector_t * result; argp = sp - num_arg + 1; /* First argument: the SQL query */ db = find_db (current_object); if (!db) errorf("The current object doesn't have a database open.\n"); err = sqlite3_prepare(db->db, get_txt(argp->u.str), mstrsize(argp->u.str), &stmt, &tail); if(err) { const char* msg = sqlite3_errmsg(db->db); if(stmt) sqlite3_finalize(stmt); errorf("sl_exec: %s\n", msg); /* NOTREACHED */ } /* Now bind all parameters. */ for(argp++, num=1; argp <= sp; argp++, num++) { switch(argp->type) { default: sqlite3_finalize(stmt); errorf("Bad argument %d to sl_exec(): type %s\n", num+1, typename(argp->type)); break; /* NOTREACHED */ case T_FLOAT: sqlite3_bind_double(stmt, num, READ_DOUBLE(argp)); break; case T_NUMBER: if (sizeof(argp->u.number) > 4) sqlite3_bind_int64(stmt, num, argp->u.number); else sqlite3_bind_int(stmt, num, argp->u.number); break; case T_STRING: sqlite3_bind_text(stmt, num, get_txt(argp->u.str), mstrsize(argp->u.str), SQLITE_STATIC); break; } } rows = 0; cols = sqlite3_column_count(stmt); rec_data = xalloc(sizeof(*rec_data)); if(!rec_data) { sqlite3_finalize(stmt); errorf("(sl_exec) Out of memory: (%lu bytes) for cleanup structure\n", (unsigned long) sizeof(*rec_data)); } rec_data->rows = NULL; rec_data->stmt = stmt; sp = push_error_handler(sl_exec_cleanup, &(rec_data->head)); while((err = sqlite3_step(stmt)) == SQLITE_ROW) { int col; sqlite_rows_t *this_row; rows++; this_row = pxalloc(sizeof(*this_row)); if(!this_row) errorf("(sl_exec) Out of memory: (%lu bytes)\n", (unsigned long) sizeof(*this_row)); this_row->last = rec_data->rows; rec_data->rows = this_row; this_row->row = NULL; /* Because allocate_array may throw an error. */ this_row->row = allocate_array(cols); if(!this_row->row) errorf("(sl_exec) Out of memory: row vector\n"); for(col = 0; col < cols; col++) { svalue_t * entry; STORE_DOUBLE_USED; entry = this_row->row->item + col; switch(sqlite3_column_type(stmt, col)) { default: errorf( "sl_exec: Unknown type %d.\n" , sqlite3_column_type(stmt, col)); break; case SQLITE_BLOB: errorf("sl_exec: Blob columns are not supported.\n"); break; case SQLITE_INTEGER: if (sizeof(entry->u.number) >= 8) put_number(entry, sqlite3_column_int64(stmt, col)); else put_number(entry, sqlite3_column_int(stmt, col)); break; case SQLITE_FLOAT: entry->type = T_FLOAT; STORE_DOUBLE(entry, sqlite3_column_double(stmt, col)); break; case SQLITE_TEXT: put_c_n_string( entry , (char *)sqlite3_column_text(stmt, col) , sqlite3_column_bytes(stmt, col)); break; case SQLITE_NULL: /* All elements from this_row->row are initialized to 0. */ break; } } } sqlite3_finalize(stmt); rec_data->stmt = NULL; switch(err) { default: errorf("sl_exec: Unknown return code from sqlite3_step: %d.\n", err); break; case SQLITE_BUSY: errorf("sl_exec: Database is locked.\n"); break; case SQLITE_ERROR: errorf("sl_exec: %s\n", sqlite3_errmsg(db->db)); break; case SQLITE_MISUSE: errorf("sl_exec: sqlite3_step was called inappropriately.\n"); break; case SQLITE_DONE: break; } if(rows) { sqlite_rows_t *this_row; result = allocate_array(rows); if(!result) errorf("(sl_exec) Out of memory: result vector\n"); this_row = rec_data->rows; while(rows--) { put_array(result->item + rows, this_row->row); this_row->row = NULL; this_row = this_row->last; } } else result = NULL; // Pop arguments and our error handler. // Our error handler gets called and cleans the row stuff. sp = pop_n_elems(num_arg + 1, sp) + 1; if(rows) put_array(sp,result); else put_number(sp, 0); return sp; } /* v_sl_exec() */
int main() { void * r; long x; void * r0; long x0; int i; int do_xtra; do_xtra = 1; // Allocate the xmem pool data area xmem_data = xalloc(XMEM_POOL_SIZE * XMEM_ELS); xmem_data_xtra = xalloc(XMEM_POOL_SIZE * XMEM_ELS_XTRA); // Init the pools pool_init(&root_pool, root_data, ROOT_ELS, ROOT_POOL_SIZE); pool_xinit(&xmem_pool, xmem_data, XMEM_ELS, XMEM_POOL_SIZE); // Turn linking on, so we can easily iterate through allocated elements pool_link(&root_pool, 1); pool_link(&xmem_pool, 1); _again: printf("Available in root pool: %u\n", pavail(&root_pool)); printf("Available in xmem pool: %u\n", pavail(&xmem_pool)); printf("Elements in root pool: %u\n", pnel(&root_pool)); printf("Elements in xmem pool: %u\n", pnel(&xmem_pool)); for (i = 0; i < 10; ++i) { r = palloc(&root_pool); if (r) printf("Got root element at %04X\n", r); if (!i) r0 = r; x = pxalloc(&xmem_pool); if (x) printf("Got xmem element at %08lX\n", x); if (!i) x0 = x; } printf("Available in root pool: %u\n", pavail(&root_pool)); printf("Available in xmem pool: %u\n", pavail(&xmem_pool)); // Now free the first elements we remembered printf("Freeing up %04X and %08lX...\n", r0, x0); pfree(&root_pool, r0); pxfree(&xmem_pool, x0); printf("Available in root pool: %u\n", pavail(&root_pool)); printf("Available in xmem pool: %u\n", pavail(&xmem_pool)); printf("Maximum used in root pool: %u\n", phwm(&root_pool)); printf("Maximum used in xmem pool: %u\n", phwm(&xmem_pool)); printf("Deleting everything...\n"); for (r = pfirst(&root_pool); r; r = r0) { r0 = pnext(&root_pool, r); pfree(&root_pool, r); } for (x = pxfirst(&xmem_pool); x; x = x0) { x0 = pxnext(&xmem_pool, x); pxfree(&xmem_pool, x); } if (do_xtra) { do_xtra = 0; printf("Doing it all again, with appended data areas...\n"); pool_append(&root_pool, root_data_xtra, ROOT_ELS_XTRA); pool_xappend(&xmem_pool, xmem_data_xtra, XMEM_ELS_XTRA); goto _again; } return 0; }
/*-------------------------------------------------------------------------*/ static bool add_struct_name (struct_name_t * pSName) /* Add the struct name <pSName> to the hash table. * The <pSName>->hash must already been computed. * Returns false on error. */ { size_t ix; #ifdef DEBUG if (find_by_struct_name(pSName)) fatal("struct type %s (%s) already in table.\n" , get_txt(pSName->name) , get_txt(pSName->prog_name) ); #endif if (!table) { table = pxalloc(sizeof(*table)); if (table == NULL) return false; table_size = 1; table[0] = pSName; pSName->next = NULL; num_types = 1; return true; } ix = pSName->hash & (table_size-1); pSName->next = table[ix]; table[ix] = pSName; num_types++; /* If chain lengths grow too much (more than 2 entries per bucket * on average), increase the table size */ if (num_types > 2 * table_size && table_size * 2 < MAX_HASH32) { size_t new_size = 2 * table_size; struct_name_t ** table2; table2 = pxalloc(new_size * sizeof(*table2)); if (table2) { memset(table2, 0, new_size * sizeof(*table2)); /* Rehash all existing entries */ for (ix = 0; ix < table_size; ix++) { struct_name_t * this; while (NULL != (this = table[ix])) { size_t ix2; table[ix] = this->next; ix2 = this->hash & (new_size-1); this->next = table2[ix2]; table2[ix2] = this; } } /* for() */ pfree(table); table = table2; table_size = new_size; } /* if (table2) */ else return false; } /* if (check for rehash condition) */ return true; } /* add_struct_name() */
void fill_header_from_mapping (svalue_t *key, svalue_t *val, void *extra) { psyc_modifier_t *m = extra; char oper = 0; char *name, *value; size_t namelen, valuelen, i; uint8_t type; svalue_t vsp, *lval; psycList list; psycString *elems = NULL; if (key->type != T_STRING) { errorf("fill_header_from_mapping: key type %d not supported\n", key->type); return; // not reached } name = get_txt(key->u.str); namelen = mstrsize(key->u.str); type = psyc_getVarType2(name, namelen); if (m->num_values > 1) oper = val[1].u.number; if (!oper) oper = C_GLYPH_OPERATOR_SET; switch (val->type) { case T_STRING: value = get_txt(val->u.str); valuelen = mstrsize(val->u.str); break; case T_NUMBER: case T_OBJECT: vsp.type = val->type; switch (val->type) { case T_NUMBER: if (type == PSYC_TYPE_DATE) vsp.u.number = val->u.number - PSYC_EPOCH; else vsp.u.number = val->u.number; break; case T_OBJECT: vsp.u.ob = val->u.ob; break; } f_to_string(&vsp); // generates an mstring value = get_txt(vsp.u.str); valuelen = mstrsize(vsp.u.str); break; case T_POINTER: if (VEC_SIZE(val->u.vec)) { elems = pxalloc(sizeof(psycString) * VEC_SIZE(val->u.vec)); if (!elems) { errorf("Out of memory in fill_header_from_mapping for elems\n"); return; // not reached } for (i = 0; i < VEC_SIZE(val->u.vec); i++) { lval = &(val->u.vec->item[i]); switch (lval->type) { case T_STRING: elems[i] = (psycString){mstrsize(lval->u.str), get_txt(lval->u.str)}; break; case T_NUMBER: case T_OBJECT: vsp.type = lval->type; switch (lval->type) { case T_NUMBER: vsp.u.number = lval->u.number; break; case T_OBJECT: vsp.u.ob = lval->u.ob; break; } f_to_string(&vsp); elems[i] = (psycString){mstrsize(vsp.u.str), get_txt(vsp.u.str)}; break; default: errorf("fill_header_from_mapping: list value type %d not supported\n", lval->type); return; // not reached } } } list = psyc_newList(elems, VEC_SIZE(val->u.vec), PSYC_LIST_CHECK_LENGTH); valuelen = list.length; value = pxalloc(valuelen); if (!value) { errorf("Out of memory in fill_header_from_mapping for list value\n"); return; // not reached } psyc_renderList(&list, value, valuelen); break; default: errorf("fill_header_from_mapping: value type %d not supported\n", val->type); return; // not reached } m->header->modifiers[m->header->lines++] = psyc_newModifier2(oper, name, namelen, value, valuelen, m->flag); }
svalue_t * f_psyc_parse (svalue_t *sp) { char *buffer = NULL; svalue_t *sv; vector_t *v, *list; mapping_t *map; char oper = 0; psycString name = {0,0}, value = {0,0}, elems[MAX_LIST_SIZE], elem; psycParseListState listState; int ret, retl, type = -1, error = 0; size_t size, i; ssize_t n; time_t timmy; if (!psyc_dispatch_callback) psyc_dispatch_callback = new_tabled("psyc_dispatch"); if (!psyc_error_callback) psyc_error_callback = new_tabled("psyc_error"); assert_shadow_sent(current_object); psyc_state_t *state = O_GET_PSYC_STATE(current_object); if (!state) { state = pxalloc(sizeof(psyc_state_t)); if (!state) { errorf("Out of memory for psyc state struct.\n"); return sp; // not reached } O_GET_PSYC_STATE(current_object) = state; memset(state, 0, sizeof(psyc_state_t)); state->parser = pxalloc(sizeof(psycParseState)); if (!state->parser) { errorf("Out of memory for psyc parse state struct.\n"); return sp; // not reached } psyc_initParseState(state->parser); } v = state->packet; if (sp->type == T_POINTER) { errorf("\npsyc_parse got %ld int* bytes... not supported yet\n", VEC_SIZE(sp->u.vec)); return sp; // not reached } else if (sp->type == T_STRING) { #ifdef DEBUG printf("\npsyc_parse got a %ld bytes long string...\n", mstrsize(sp->u.str)); #endif if (state->remaining) { // there are remaining bytes from the previous call to psyc_parse, // copy them together with the newly arrived data buffer = pxalloc(state->remaining_len + mstrsize(sp->u.str)); if (!buffer) { errorf("Out of memory for psyc_parse buffer.\n"); return sp; // not reached } memcpy(buffer, state->remaining, state->remaining_len); memcpy(buffer + state->remaining_len, get_txt(sp->u.str), mstrsize(sp->u.str)); psyc_setParseBuffer2(state->parser, buffer, state->remaining_len + mstrsize(sp->u.str)); pfree(state->remaining); state->remaining = NULL; state->remaining_len = 0; } else { psyc_setParseBuffer2(state->parser, get_txt(sp->u.str), mstrsize(sp->u.str)); } } else { errorf("\npsyc_parse got type %d, not supported\n", sp->type); return sp; // not reached } do { ret = psyc_parse(state->parser, &oper, &name, &value); #ifdef DEBUG printf("#%2d %c%.*s = %.*s\n", ret, oper ? oper : ' ', (int)name.length, name.ptr, (int)value.length, value.ptr); #endif if (!state->packet) { state->packet = allocate_array(4); if (!state->packet) { errorf("Out of memory for psyc_parse array.\n"); return sp; // not reached } v = state->packet; map = allocate_mapping(0, 2); // empty mapping if (!map) { errorf("Out of memory for psyc_parse routing header.\n"); return sp; // not reached } put_mapping(&v->item[PACKET_ROUTING], map); map = allocate_mapping(0, 2); // empty mapping if (!map) { errorf("Out of memory for psyc_parse entity header.\n"); return sp; // not reached } put_mapping(&v->item[PACKET_ENTITY], map); } switch (ret) { case PSYC_PARSE_ENTITY_START: case PSYC_PARSE_BODY_START: // save oper, name & value in state at the start of // incomplete entity or body state->oper = oper; state->name = mstring_alloc_string(name.length); memcpy(get_txt(state->name), name.ptr, name.length); if (!state->name) { errorf("Out of memory for name.\n"); return sp; // not reached } // allocate memory for the total length of the value state->value_len = 0; state->value = mstring_alloc_string(psyc_getParseValueLength(state->parser)); if (!state->value) { errorf("Out of memory for value.\n"); return sp; // not reached } // fall thru case PSYC_PARSE_ENTITY_CONT: case PSYC_PARSE_BODY_CONT: case PSYC_PARSE_ENTITY_END: case PSYC_PARSE_BODY_END: // append value to tmp buffer in state memcpy(get_txt(state->value) + state->value_len, value.ptr, value.length); state->value_len += value.length; } if (ret == PSYC_PARSE_ENTITY_END || ret == PSYC_PARSE_BODY_END) { // incomplete entity or body parsing done, // set oper/name/value to the ones saved in state oper = state->oper; name.ptr = get_txt(state->name); name.length = mstrsize(state->name); value.ptr = get_txt(state->value); value.length = mstrsize(state->value); } switch (ret) { case PSYC_PARSE_ROUTING: sv = pxalloc(sizeof(svalue_t)); // new_n_tabled fetches a reference of a probably existing // shared string put_string(sv, new_n_tabled(name.ptr, name.length)); sv = get_map_lvalue(v->item[PACKET_ROUTING].u.map, sv); put_number(&sv[1], oper); // strings are capable of containing 0 so we can do this // for binary data too. let's use a tabled string even // for values of routing variables as they repeat a lot put_string(sv, new_n_tabled(value.ptr, value.length)); break; case PSYC_PARSE_ENTITY_START: case PSYC_PARSE_ENTITY_CONT: break; case PSYC_PARSE_ENTITY_END: case PSYC_PARSE_ENTITY: sv = pxalloc(sizeof(svalue_t)); if (ret == PSYC_PARSE_ENTITY) put_string(sv, new_n_tabled(name.ptr, name.length)); else // PSYC_PARSE_ENTITY_END put_string(sv, make_tabled(state->name)); sv = get_map_lvalue(v->item[PACKET_ENTITY].u.map, sv); put_number(&sv[1], oper); type = psyc_getVarType(&name); switch (type) { case PSYC_TYPE_DATE: // number + PSYC_EPOCH if (psyc_parseDate(&value, &timmy)) put_number(sv, timmy); else error = PSYC_PARSE_ERROR_DATE; break; case PSYC_TYPE_TIME: // number if (psyc_parseTime(&value, &timmy)) put_number(sv, timmy); else error = PSYC_PARSE_ERROR_TIME; break; case PSYC_TYPE_AMOUNT: // number if (psyc_parseNumber(&value, &n)) put_number(sv, n); else error = PSYC_PARSE_ERROR_AMOUNT; break; case PSYC_TYPE_DEGREE: // first digit if (value.length && value.ptr[0] >= '0' && value.ptr[0] <= '9') put_number(sv, value.ptr[0] - '0'); else error = PSYC_PARSE_ERROR_DEGREE; break; case PSYC_TYPE_FLAG: // 0 or 1 if (value.length && value.ptr[0] >= '0' && value.ptr[0] <= '1') put_number(sv, value.ptr[0] - '0'); else error = PSYC_PARSE_ERROR_FLAG; break; case PSYC_TYPE_LIST: // array size = 0; if (value.length) { psyc_initParseListState(&listState); psyc_setParseListBuffer(&listState, value); elem = (psycString){0, 0}; do { retl = psyc_parseList(&listState, &elem); switch (retl) { case PSYC_PARSE_LIST_END: retl = 0; case PSYC_PARSE_LIST_ELEM: if (size >= MAX_LIST_SIZE) { error = PSYC_PARSE_ERROR_LIST_TOO_LARGE; break; } elems[size++] = elem; break; default: error = PSYC_PARSE_ERROR_LIST; } } while (retl > 0 && !error); } if (error) break; list = allocate_array(size); for (i = 0; i < size; i++) put_string(&list->item[i], new_n_tabled(elems[i].ptr, elems[i].length)); put_array(sv, list); break; default: // string if (ret == PSYC_PARSE_ENTITY) // is it good to put entity variable values into the // shared string table? probably yes.. but it's a guess //t_string(sv, new_n_mstring(value.ptr, value.length)); put_string(sv, new_n_tabled(value.ptr, value.length)); else // PSYC_PARSE_ENTITY_END put_string(sv, state->value); } break; case PSYC_PARSE_BODY_START: case PSYC_PARSE_BODY_CONT: break; case PSYC_PARSE_BODY_END: put_string(&v->item[PACKET_METHOD], make_tabled(state->name)); put_string(&v->item[PACKET_BODY], state->value); break; case PSYC_PARSE_BODY: // new_n_tabled gets the shared string for the method put_string(&v->item[PACKET_METHOD], new_n_tabled(name.ptr, name.length)); // allocate an untabled string for the packet body put_string(&v->item[PACKET_BODY], new_n_mstring(value.ptr, value.length)); break; case PSYC_PARSE_COMPLETE: put_array(inter_sp, v); sapply(psyc_dispatch_callback, current_object, 1); state->packet = NULL; break; case PSYC_PARSE_INSUFFICIENT: // insufficient data, save remaining bytes state->remaining_len = psyc_getParseRemainingLength(state->parser); if (state->remaining_len) { state->remaining = pxalloc(state->remaining_len); memcpy(state->remaining, psyc_getParseRemainingBuffer(state->parser), state->remaining_len); } else state->remaining = NULL; ret = 0; break; default: error = ret; } switch (ret) { case PSYC_PARSE_BODY_END: case PSYC_PARSE_ENTITY_END: // reset tmp buffers in state when incomplete // entity or body parsing is finished state->oper = 0; state->name = NULL; state->value = NULL; } } while (ret && !error); if (buffer) pfree(buffer); free_svalue(sp); put_number(sp, error); return sp; } /* f_psyc_parse */