/* * mdapi.c * * Summary: Front end API routines. * * Author: David Hardy */ #include #include #include #include #if !defined(MD_STATIC) /* * include header file for dyanmic loading */ #if defined(HPUX) #include #elif defined(DARWIN) #include #else #include #endif #endif /* ! MD_STATIC */ #include "ihash.h" #include "mdsim.h" #if 0 #include "mdclient.h" #endif /* * error info for MDAPI -- order identical to MD_ERR_ numbers in mdtypes.h */ static const MD_Err ERR_TABLE[] = { { "no error reported", 0 }, { "inconsistent version number", -1 }, { "incorrect use of MDAPI", -1 }, { "cannot create engine", -1 }, { "memory cannot be allocated", -1 }, { "invalid data ID number", 0 }, { "unrecognized data name", 0 }, { "value is out of range", 0 }, { "unsupported feature", 0 }, { "call back failed", -1 }, { "invalid type number", 0 }, { "cannot create type", -1 }, { "dynamic linking error", -1 }, { "cannot connect to remote engine", -1 }, { "remote connection timed out", -1 }, { "communication protocol error", -1 }, { "consistency check failed", -1 }, }; /* * define minimum lengths for MD_Sim resizable arrays */ #define ERRMIN ARRLEN(ERR_TABLE) #define HASHKEYMIN 10 #define NAMEMIN 10 #define DATAMIN 10 #define PLENSTRMIN 10 #define CBMIN 5 #define CBMAXCALLS 100 #define TYPEMIN 25 #define STACKMIN 20 #define QUEUEMIN 10 /* * define dynamic link macros */ #if !defined(MD_STATIC) /* auto detect the right macro to use */ #if defined(HPUX) #ifdef BIND_IMMEDIATE #define LOADER_MODE BIND_IMMEDIATE #else #define LOADER_MODE BIND_DEFERRED #endif #elif defined(DARWIN) #define LOADER_MODE NSLINKMODULE_OPTION_NONE #define UNLOAD_MODE NSUNLINKMODULE_OPTION_NONE #else /* all other *nix */ /* (Alpha needs the _GLOBAL flags defined) */ #ifdef ALPHA #ifndef RTLD_GLOBAL #define RTLD_GLOBAL 0 #endif #ifndef DL_GLOBAL #define DL_GLOBAL 0 #endif #endif /* ALPHA */ #ifdef RTLD_NOW #define LOADER_MODE ( RTLD_NOW | RTLD_GLOBAL ) #else #ifdef DL_LAZY #define LOADER_MODE ( DL_LAZY | DL_GLOBAL ) #else #define LOADER_MODE ( RTLD_LAZY | RTLD_GLOBAL ) #endif #endif #endif /* which *nix? */ /* check for a.out platform */ #if defined(AOUT) || defined(DARWIN) #define LD_CREATE_STRING "_MD_create_engine" #define LD_DESTROY_STRING "_MD_destroy_engine" #else #define LD_CREATE_STRING "MD_create_engine" #define LD_DESTROY_STRING "MD_destroy_engine" #endif #endif /* ! MD_STATIC */ /* * Prototypes for static functions. */ static MD_Errcode finish_create( MD_Sim * ); static MD_Errcode register_callback( MD_Sim *, MD_Int accflags, MD_Callback_Fptr f, MD_Callback_Data *cbdata, MD_Int len, MD_Int numsteps, MD_Int numer, MD_Int denom, MD_Callback **pcb, MD_Int *pcblen, MD_Int *pcbmax ); /* default routines, may be optionally redefined */ static MD_Errcode init_default( MD_Sim * ); static MD_Int isdone_default( MD_Sim * ); static MD_Errcode wait_default( MD_Sim * ); static MD_Errcode prcb_default( MD_Sim *, MD_Callback *cb ); static MD_Int isdoneprcb_default( MD_Sim * ); static MD_Errcode waitprcb_default( MD_Sim * ); /***************************************************************************** * * Action routines. * *****************************************************************************/ MD_Sim *MD_create(const char *engine_name, MD_Errcode (*create_engine)(MD_Sim *), void (*destroy_engine)(MD_Sim *)) { MD_Sim *sim; MD_Int i; /* check validity of arguments */ /* function pointers should either both be NULL or both be defined */ if ( (create_engine == NULL && destroy_engine != NULL) || (create_engine != NULL && destroy_engine == NULL) ) { return NULL; } /* allocate memory for MD_Sim */ if ((sim = (MD_Sim *) calloc(1, sizeof(MD_Sim))) == NULL) { return NULL; } /* allocate error info array */ if ((sim->err = (MD_Err *) calloc(ERRMIN, sizeof(MD_Err))) == NULL) { free(sim); return NULL; } /* fill error info array from table */ for (i = 0; i < ERRMIN; i++) { sim->err[i].msg = ERR_TABLE[i].msg; sim->err[i].isfatal = ERR_TABLE[i].isfatal; } sim->errmax = sim->errlen = ERRMIN; /* * Have memory for error info, can do standard error reporting now. */ sim->state = STATE_CREATE; /* set call state */ /* set default MD_Sim "virtual" methods */ sim->init = init_default; sim->isdone = isdone_default; sim->wait = wait_default; sim->prcb = prcb_default; sim->isdoneprcb = isdoneprcb_default; sim->waitprcb = waitprcb_default; /* initialize function pointers */ sim->create_engine = create_engine; sim->destroy_engine = destroy_engine; /* make copy of back end engine name */ sim->engine_name = (engine_name != NULL ? strdup(engine_name) : strdup("")); if (sim->engine_name == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to strdup"); return sim; } /* initialize the name hash table */ if (ihash_init( &(sim->nametable), 0 ) != 0) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to ihash_init"); return sim; } /* initialize the plen hash table */ if (ihash_init( &(sim->plentable), 0 ) != 0) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to ihash_init"); return sim; } /* initialize the type name hash table */ if (ihash_init( &(sim->typetable), 0 ) != 0) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to ihash_init"); return sim; } /* allocate array of hash keys */ if ((sim->hashkey = (char **) malloc(HASHKEYMIN * sizeof(char *))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc hashkey array"); return sim; } sim->hashkeymax = HASHKEYMIN; /* allocate array of names */ if ((sim->name=(const char**)malloc(NAMEMIN*sizeof(const char*))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc name array"); return sim; } sim->namemax = NAMEMIN; /* allocate engine data array */ if ((sim->data = (MD_Data *) malloc(DATAMIN * sizeof(MD_Data))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc data array"); return sim; } sim->datamax = DATAMIN; /* allocate plenstr array */ if ((sim->plenstr = (char **) malloc(PLENSTRMIN * sizeof(char *))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc plenstr array"); return sim; } sim->plenstrmax = PLENSTRMIN; /* allocate call back registration array */ if ((sim->cb = (MD_Callback *) malloc(CBMIN*sizeof(MD_Callback))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc callback array"); return sim; } sim->cbmax = CBMIN; /* allocate call back registration array */ if ((sim->fcb = (MD_Callback *) malloc(CBMIN*sizeof(MD_Callback))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc fcallback array"); return sim; } sim->fcbmax = CBMIN; /* set maximum number of consecutive callback calls to a single routine */ /* (counting calls avoids an infinite loop) */ sim->cbmaxcalls = CBMAXCALLS; /* allocate type number stack */ if ((sim->typestack = (MD_Int *) malloc(STACKMIN * sizeof(MD_Int))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc type stack"); return sim; } sim->typestackmax = STACKMIN; /* allocate flip info queue */ if ((sim->flipqueue = (MD_Int *) malloc(QUEUEMIN * sizeof(MD_Int))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc flip queue"); return sim; } sim->flipqueuemax = QUEUEMIN; /* allocate type array */ if ((sim->type = (MD_Type *) malloc(TYPEMIN * sizeof(MD_Type))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc type array"); return sim; } sim->typemax = TYPEMIN; /* allocate type name array */ if ((sim->typename=(const char**)malloc(TYPEMIN*sizeof(const char*)))==NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_create call to malloc type name array"); return sim; } sim->typenamemax = TYPEMIN; /* set need to init flag */ sim->isneedinit = 1; return sim; } MD_Errcode finish_create(MD_Sim *sim) { MD_Int i, len; #if !defined(MD_STATIC) && defined(DARWIN) NSObjectFileImage image; NSSymbol sym; #endif /* check on error status from MD_create */ if (sim->errnum != 0) return MD_FAIL; /* check to see if create engine has been defined */ if (sim->create_engine == NULL) { /* * Check to see if we have a remote engine, indicated by a colon (:). * The format for a remote engine name is: * * hostname#portnum:enginename * * where #portnum is an optionally specified port number (# followed * by the number) and enginename is either the absolute path name of * the dynamic link library or the daemon listening at that host is * statically linked to an engine. * * If there is no colon (:) in engine name, try to open name as a * dynamic link library. */ #if 0 if (strchr(sim->engine_name, ':') != NULL) { sim->create_engine = MD_client_create_engine; sim->destroy_engine = MD_client_destroy_engine; } else #endif #if !defined(MD_STATIC) #if defined(HPUX) if (!(sim->is_engine_loaded = ((sim->dlhandle = shl_load(sim->engine_name, LOADER_MODE, 0L)) == NULL || shl_findsym( &(sim->dlhandle), LD_CREATE_STRING, TYPE_PROCEDURE, (void *)&(sim->create_engine) ) == -1 || shl_findsym( &(sim->dlhandle), LD_DESTROY_STRING, TYPE_PROCEDURE, (void *)&(sim->destroy_engine) ) == -1 ) == 0 ? 1 : 0) ) { ERR(sim, MD_ERR_DLINK, "failed to dynamically load shared library"); return MD_FAIL; } #elif defined(DARWIN) if ( NSCreateObjectFileImageFromFile(sim->engine_name, &image) != NSObjectFileImageSuccess ) { ERR(sim, MD_ERR_DLINK, "call to NSCreateObjectFileImageFromFile " "failed to load engine bundle image"); return MD_FAIL; } else if ( (sim->dlhandle = NSLinkModule(image, sim->engine_name, LOADER_MODE)) == NULL ) { ERR(sim, MD_ERR_DLINK,"call to NSLinkModule failed to get module handle"); return MD_FAIL; } else if ( (sym = NSLookupSymbolInModule(sim->dlhandle, LD_CREATE_STRING)) == NULL ) { ERR(sim, MD_ERR_DLINK, "call to NSLookupSymbolInModule failed to obtain " "symbol " LD_CREATE_STRING ); return MD_FAIL; } else if ( (sim->create_engine = (MD_Errcode (*)(MD_Sim *)) NSAddressOfSymbol(sym)) == NULL ) { ERR(sim, MD_ERR_DLINK, "call to NSAddressOfSymbol failed to obtain " "address of " LD_CREATE_STRING " routine"); return MD_FAIL; } else if ( (sym = NSLookupSymbolInModule(sim->dlhandle, LD_DESTROY_STRING)) == NULL ) { ERR(sim, MD_ERR_DLINK, "call to NSLookupSymbolInModule failed to obtain " "symbol " LD_DESTROY_STRING ); return MD_FAIL; } else if ( (sim->destroy_engine = (void (*)(MD_Sim *)) NSAddressOfSymbol(sym)) == NULL ) { ERR(sim, MD_ERR_DLINK, "call to NSAddressOfSymbol failed to obtain " "address of " LD_DESTROY_STRING " routine"); return MD_FAIL; } #else if ( (sim->dlhandle = dlopen(sim->engine_name, LOADER_MODE)) == NULL || (sim->create_engine = (MD_Errcode (*)(MD_Sim *)) dlsym(sim->dlhandle, LD_CREATE_STRING)) == NULL || (sim->destroy_engine = (void (*)(MD_Sim *)) dlsym(sim->dlhandle, LD_DESTROY_STRING)) == NULL ) { ERR(sim, MD_ERR_DLINK, dlerror()); return MD_FAIL; } #endif #else ERR(sim, MD_ERR_DLINK, "dynamic loading unsupported in static builds"); return MD_FAIL; #endif /* ! MD_STATIC */ } /* create the engine */ if (sim->create_engine(sim) != 0 || sim->errnum != 0) { /* make sure error is fatal */ sim->err[sim->errnum].isfatal = MD_FAIL; return MD_FAIL; } else if (sim->run == NULL) { ERR(sim, MD_ERR_CREATE, "finish_create call to create_engine"); return MD_FAIL; } /* keep engine from calling certain setup routines */ sim->iscreated = 1; return 0; } void MD_destroy(MD_Sim *sim) { MD_Int i; if (sim == NULL) return; if (sim->destroy_engine != NULL) { sim->destroy_engine(sim); } #if !defined(MD_STATIC) #if defined(HPUX) if (sim->is_engine_loaded) { shl_unload(sim->dlhandle); } #elif defined(DARWIN) if (sim->dlhandle != NULL) { NSUnLinkModule(sim->dlhandle, UNLOAD_MODE); } #else if (sim->dlhandle != NULL) { dlclose(sim->dlhandle); } #endif #endif /* ! MD_STATIC */ ihash_destroy( &(sim->nametable) ); for (i = 0; i < sim->hashkeylen; i++) { free(sim->hashkey[i]); } free(sim->hashkey); free(sim->name); free(sim->data); if (sim->cb) { for (i = 0; i < sim->cblen; i++) { free(sim->cb[i].data); } } free(sim->cb); if (sim->fcb) { for (i = 0; i < sim->fcblen; i++) { free(sim->fcb[i].data); } } free(sim->fcb); ihash_destroy( &(sim->plentable) ); for (i = 0; i < sim->plenstrlen; i++) { free(sim->plenstr[i]); } free(sim->plenstr); ihash_destroy( &(sim->typetable) ); free(sim->typestack); free(sim->flipqueue); for (i = 0; i < sim->typelen; i++) { free(sim->type[i].subtype); free(sim->type[i].flipoffset); } free(sim->type); free(sim->typename); free(sim->err); free(sim); } MD_Errcode MD_init(MD_Sim *sim) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_init"); return MD_FAIL; } sim->state = STATE_INIT; /* set call state */ return sim->init(sim); } MD_Errcode MD_run(MD_Sim *sim, MD_Int numsteps) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_run"); return MD_FAIL; } sim->state = STATE_RUN; /* set call state */ /* check to see if system first needs to be init'ed */ if (sim->isneedinit) { ERR(sim, MD_ERR_SUPPORT, "need to MD_init before MD_run"); return MD_FAIL; } return sim->run(sim, numsteps); } MD_Errcode init_default(MD_Sim *sim) { return 0; } /***************************************************************************** * * Asynchronous control routines. * *****************************************************************************/ MD_Int MD_isdone(MD_Sim *sim) { switch (sim->state) { case STATE_NONE: case STATE_CALLBACK: case STATE_FCALLBACK: ERR(sim, MD_ERR_USE, "MD_isdone"); return 1; case STATE_CREATE: if (!sim->iscreated && finish_create(sim) != 0) return 1; break; } return sim->isdone(sim); } MD_Errcode MD_wait(MD_Sim *sim) { MD_Int i, ret; switch (sim->state) { case STATE_NONE: case STATE_CALLBACK: case STATE_FCALLBACK: ERR(sim, MD_ERR_USE, "MD_wait"); return MD_FAIL; case STATE_CREATE: if (!sim->iscreated) finish_create(sim); if (sim->errnum != 0) { /* make sure error is fatal to kill sim */ sim->err[sim->errnum].isfatal = MD_FAIL; return MD_FAIL; } sim->state = STATE_NONE; ret = sim->wait(sim); ihash_destroy( &(sim->plentable) ); for (i = 0; i < sim->plenstrlen; i++) { free(sim->plenstr[i]); } free(sim->plenstr); sim->plenstr = NULL; sim->plenstrlen = sim->plenstrmax = 0; return (ret == 0 ? 0 : MD_FAIL); case STATE_INIT: sim->state = STATE_NONE; if (sim->wait(sim) != 0) return MD_FAIL; sim->isneedinit = 0; return 0; } sim->state = STATE_NONE; return sim->wait(sim); } MD_Int isdone_default(MD_Sim *sim) { return 1; } MD_Errcode wait_default(MD_Sim *sim) { if (sim->errnum != 0) return MD_FAIL; return 0; } /***************************************************************************** * * Data management routines. * *****************************************************************************/ const char **MD_namelist(MD_Sim *sim, MD_Int *listlen) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_namelist"); return NULL; } if (listlen != NULL) *listlen = sim->namelen; return sim->name; } MD_Int MD_lookup(MD_Sim *sim, const char *name) { MD_String buf; char *str, *pch; MD_Int idnum; if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_lookup"); return MD_FAIL; } if (strlen(name) < sizeof(buf)) { /* use stack-allocated buf for storage */ strcpy(buf, name); for (pch = buf; *pch != '\0'; pch++) { if (isupper(*pch)) *pch = tolower(*pch); } idnum = ihash_lookup( &(sim->nametable), buf); } else { /* name is too long, have to temporarily allocate heap memory */ if ((str = strdup(name)) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "MD_lookup call to strdup"); return MD_FAIL; } for (pch = str; *pch != '\0'; pch++) { if (isupper(*pch)) *pch = tolower(*pch); } idnum = ihash_lookup( &(sim->nametable), str); free(str); } if (idnum >= 0) return idnum; ERR(sim, MD_ERR_NAME, "MD_lookup call to ihash_lookup"); return MD_FAIL; } MD_Data_Attr MD_getattr(MD_Sim *sim, MD_Int idnum) { const MD_Data_Attr fail = { MD_FAIL, MD_FAIL, MD_FAIL, MD_FAIL }; if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_getattr"); return fail; } if (idnum >= 0 && idnum < sim->datalen) { return sim->data[idnum].attr; } ERR(sim, MD_ERR_IDNUM, "MD_getattr"); return fail; } MD_Errcode MD_setdata(MD_Sim *sim, MD_Int idnum, const void *buf, MD_Int nelems, MD_Int first) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_setdata"); return MD_FAIL; } sim->state = STATE_SETDATA; /* set call state */ if (idnum < 0 || idnum >= sim->datalen) { ERR(sim, MD_ERR_IDNUM, "MD_setdata"); return MD_FAIL; } else if ((sim->data[idnum].attr.access & MD_DATA_WRITE) == 0) { ERR(sim, MD_ERR_SUPPORT, "MD_setdata"); return MD_FAIL; } else if (first < 0 || nelems < 0 || first + nelems > *(sim->data[idnum].plen)) { ERR(sim, MD_ERR_RANGE, "MD_setdata"); return MD_FAIL; } if ((sim->data[idnum].attr.access & MD_DATA_INIT) != 0) { sim->isneedinit = 1; } return sim->data[idnum].setdata(sim, sim->data+idnum, buf, nelems, first); } MD_Errcode MD_getdata(MD_Sim *sim, MD_Int idnum, void *buf, MD_Int nelems, MD_Int first) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_getdata"); return MD_FAIL; } sim->state = STATE_GETDATA; /* set call state */ if (idnum < 0 || idnum >= sim->datalen) { ERR(sim, MD_ERR_IDNUM, "MD_getdata"); return MD_FAIL; } else if ((sim->data[idnum].attr.access & MD_DATA_READ) == 0) { ERR(sim, MD_ERR_SUPPORT, "MD_getdata"); return MD_FAIL; } else if (first < 0 || nelems < 0 || first + nelems > *(sim->data[idnum].plen)) { ERR(sim, MD_ERR_RANGE, "MD_getdata"); return MD_FAIL; } return sim->data[idnum].getdata(sim, sim->data+idnum, buf, nelems, first); } MD_Errcode MD_setlen(MD_Sim *sim, MD_Int idnum, MD_Int len) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_setlen"); return MD_FAIL; } sim->state = STATE_SETLEN; /* set call state */ if (idnum < 0 || idnum >= sim->datalen) { ERR(sim, MD_ERR_IDNUM, "MD_setlen"); return MD_FAIL; } else if ((sim->data[idnum].attr.access & MD_DATA_RESIZE) == 0) { ERR(sim, MD_ERR_SUPPORT, "MD_setlen"); return MD_FAIL; } else if (len > sim->data[idnum].attr.maxlen) { ERR(sim, MD_ERR_RANGE, "MD_setlen"); return MD_FAIL; } if ((sim->data[idnum].attr.access & MD_DATA_INIT) != 0) { sim->isneedinit = 1; } return sim->data[idnum].setlen(sim, sim->data+idnum, len); } MD_Int MD_getlen(MD_Sim *sim, MD_Int idnum) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_getlen"); return MD_FAIL; } if (idnum >= 0 && idnum < sim->datalen) { return *(sim->data[idnum].plen); } ERR(sim, MD_ERR_IDNUM, "MD_getlen"); return MD_FAIL; } void *MD_direct(MD_Sim *sim, MD_Int idnum, MD_Int first) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_direct"); return NULL; } if (idnum < 0 || idnum >= sim->datalen) { ERR(sim, MD_ERR_IDNUM, "MD_direct"); return NULL; } else if ((sim->data[idnum].attr.access & MD_DATA_DIRECT) == 0) { ERR(sim, MD_ERR_SUPPORT, "MD_direct"); return NULL; } else if (first >= *(sim->data[idnum].plen)) { ERR(sim, MD_ERR_RANGE, "MD_direct"); return NULL; } return *((void **) (sim->data[idnum].ppdata)); } /***************************************************************************** * * Callback management routines. * *****************************************************************************/ MD_Errcode MD_set_callback(MD_Sim *sim, MD_Callback_Fptr f, MD_Callback_Data *cbdata, MD_Int len, MD_Int numsteps) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_set_callback"); return MD_FAIL; } return register_callback(sim, (MD_DATA_CBREAD | MD_DATA_CBWRITE), f, cbdata, len, numsteps, 0, 0, &(sim->cb), &(sim->cblen), &(sim->cbmax)); } MD_Errcode MD_set_fcallback(MD_Sim *sim, MD_Callback_Fptr f, MD_Callback_Data *cbdata, MD_Int len, MD_Int numsteps, MD_Int numer, MD_Int denom) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_set_fcallback"); return MD_FAIL; } return register_callback(sim, (MD_DATA_FCBREAD | MD_DATA_FCBWRITE), f, cbdata, len, numsteps, numer, denom, &(sim->fcb), &(sim->fcblen), &(sim->fcbmax)); } MD_Errcode register_callback(MD_Sim *sim, MD_Int accflags, MD_Callback_Fptr f, MD_Callback_Data *cbdata, MD_Int len, MD_Int numsteps, MD_Int numer, MD_Int denom, MD_Callback **pcb, MD_Int *pcblen, MD_Int *pcbmax) { if (numsteps < 0) { ERR(sim, MD_ERR_RANGE, "register_callback"); return MD_FAIL; } /* * numsteps > 0 means that we are registering this callback */ if (numsteps > 0) { MD_Int k, idnum, access; MD_Callback_Data tmpcbdata; MD_Callback_Data *pcbdata; /* * Check validity of pcbdata engine data references. * Front end required to set attr.idnum and attr.access * (with access modes MD_DATA_CBREAD and/or MD_DATA_CBWRITE) * for each MD_Callback_Data element. */ for (k = 0; k < len; k++) { idnum = cbdata[k].attr.idnum; access = (cbdata[k].attr.access & accflags); /* make sure only _CBREAD and _CBWRITE access attributes are set */ /* check for valid idnum */ if (idnum < 0 || idnum >= sim->datalen) { ERR(sim, MD_ERR_IDNUM, "register_callback"); return MD_FAIL; } tmpcbdata = cbdata[k]; tmpcbdata.data = NULL; tmpcbdata.attr = sim->data[idnum].attr; tmpcbdata.attr.access &= access; /* verify that engine data allows requested callback access */ if (access == 0 || access != tmpcbdata.attr.access) { ERR(sim, MD_ERR_SUPPORT, "register_callback"); return MD_FAIL; } /* verify that subarray range is legal */ else if (tmpcbdata.first < 0 || tmpcbdata.nelems + tmpcbdata.first > *(sim->data[idnum].plen)) { ERR(sim, MD_ERR_RANGE, "register_callback"); return MD_FAIL; } } /* add callback to end of pcb array, resize if neccessary */ if (*pcblen == *pcbmax) { void *tmp = realloc(*pcb, 2 * (*pcbmax) * sizeof(MD_Callback)); if (tmp == NULL) { ERR(sim, MD_ERR_MEMALLOC, "register_callback call to realloc"); return MD_FAIL; } *pcb = (MD_Callback *) tmp; *pcbmax *= 2; } (*pcb)[*pcblen].f = f; (*pcb)[*pcblen].datanum = len; (*pcb)[*pcblen].stepnum = numsteps; (*pcb)[*pcblen].numer = numer; (*pcb)[*pcblen].denom = denom; /* allocate memory for callback data */ if (((*pcb)[*pcblen].data = (MD_Callback_Data *) calloc(len, sizeof(MD_Callback))) == NULL) { ERR(sim, MD_ERR_MEMALLOC, "register_callback call to calloc"); return MD_FAIL; } /* copy over cbdata array */ for (k = 0; k < len; k++) { pcbdata = (*pcb)[*pcblen].data + k; /* set correct attributes from engine data array */ pcbdata->data = NULL; pcbdata->nelems = cbdata[k].nelems; pcbdata->first = cbdata[k].first; /* set correct attributes from engine data array */ pcbdata->attr = sim->data[cbdata[k].attr.idnum].attr; /* adjust access to indicate CBREAD and/or CBWRITE */ pcbdata->attr.access = (cbdata[k].attr.access & accflags); } (*pcblen)++; } /* * otherwise (numsteps <= 0) we are deleting all callbacks to f */ else { MD_Int i = 0, cboffset = 0; /* need to delete all references to f in callback array */ /* do it so as to maintain order of other callbacks */ while (i + cboffset < *pcblen) { if ((*pcb)[i + cboffset].f == f) { /* free callback data array */ free( (*pcb)[i + cboffset].data ); cboffset++; (*pcblen)--; } else if (cboffset > 0) { (*pcb)[i] = (*pcb)[i + cboffset]; i++; } else { i++; } } /* downsize array if needed */ if (*pcbmax > CBMIN && *pcblen <= *pcbmax / 4) { void *tmp = realloc(*pcb, *pcbmax / 2 * sizeof(MD_Callback)); if (tmp == NULL) { ERR(sim, MD_ERR_MEMALLOC, "register_callback call to realloc"); return MD_FAIL; } *pcb = (MD_Callback *) tmp; *pcbmax /= 2; } } return 0; } void MD_setinfo(MD_Sim *sim, void *info) { sim->fe_info = info; } void *MD_getinfo(MD_Sim *sim) { return sim->fe_info; } MD_Errcode MD_set_cbmaxcalls(MD_Sim *sim, MD_Int cbmaxcalls) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_set_cbmaxcalls"); return MD_FAIL; } if (cbmaxcalls <= 0) { ERR(sim, MD_ERR_RANGE, "MD_set_cbmaxcalls"); return MD_FAIL; } sim->cbmaxcalls = cbmaxcalls; return 0; } MD_Errcode prcb_default(MD_Sim *sim, MD_Callback *cb) { sim->cbretval = cb->f(sim->fe_info, cb->data, cb->datanum, cb->stepnum, cb->numer, cb->denom); return -(sim->cbretval < 0); /* return -1 on error, 0 otherwise */ } MD_Int isdoneprcb_default(MD_Sim *sim) { MD_Callback *cb = &(sim->cbsave); if (sim->cbretval > 0) { sim->cbretval = cb->f(sim->fe_info, cb->data, cb->datanum, cb->stepnum, cb->numer, cb->denom); } return (sim->cbretval <= 0); /* return 1 if done, 0 otherwise */ } MD_Errcode waitprcb_default(MD_Sim *sim) { MD_Callback *cb = &(sim->cbsave); while (sim->cbretval > 0) { sim->cbretval = cb->f(sim->fe_info, cb->data, cb->datanum, cb->stepnum, cb->numer, cb->denom); } return -(sim->cbretval < 0); /* return -1 on error, 0 otherwise */ } /***************************************************************************** * * Obtaining type information. * *****************************************************************************/ const char **MD_type_namelist(MD_Sim *sim, MD_Int *listlen) { if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_type_namelist"); return NULL; } if (listlen != NULL) *listlen = sim->typenamelen; return sim->typename; } const char *MD_type_name(MD_Sim *sim, MD_Int typenum) { MD_Int index; if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_type_name"); return NULL; } index = INDEX(typenum); if (index < 0 || index >= sim->typelen || typenum != sim->type[index].typenum) { ERR(sim, MD_ERR_TYPENUM, "MD_type_name"); return NULL; } return sim->type[index].name; } MD_Int MD_type(MD_Sim *sim, const char *typename) { MD_Int typenum; if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_typenum"); return MD_FAIL; } typenum = ihash_lookup( &(sim->typetable), typename); if (typenum >= 0) return typenum; ERR(sim, MD_ERR_NAME, "MD_typenum call to ihash_lookup"); return MD_FAIL; } const MD_Int *MD_type_info(MD_Sim *sim, MD_Int typenum, MD_Int *infolen) { MD_Int index; if (sim->state != STATE_NONE) { ERR(sim, MD_ERR_USE, "MD_type_info"); return NULL; } index = INDEX(typenum); if (index < 0 || index >= sim->typelen || typenum != sim->type[index].typenum) { ERR(sim, MD_ERR_TYPENUM, "MD_type_info"); return NULL; } if (infolen != NULL) *infolen = sim->type[index].subtypelen; return sim->type[index].subtype; } /***************************************************************************** * * Error handling routines. * *****************************************************************************/ MD_Int MD_errnum(MD_Sim *sim) { return sim->errnum; } const char *MD_errmsg(MD_Sim *sim) { return sim->errmsg_buf; } MD_Int MD_isfatal(MD_Sim *sim) { return (sim->err[sim->errnum].isfatal != 0); } MD_Errcode MD_reset(MD_Sim *sim) { if (sim->err[sim->errnum].isfatal != 0) { return sim->err[sim->errnum].isfatal; } sim->errnum = 0; sim->errmsg_buf[0] = '\0'; return 0; } const char *MD_api_name(MD_Sim *sim) { return MD_API_NAME; } const char *MD_engine_name(MD_Sim *sim) { return sim->engine_name; }