/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include "pmodflags.h" #include "utils.h" #include "distp.h" #include "parse.h" #include #include #include #include #include #include /* funcs for dynamic loading. these belong in a header file somewhere */ extern Error DXLoadAndRunObjFile (char *fname, char *envvar); extern Error DXUnloadObjFile (char *fname, char *envvar); extern PFI DXLoadObjFile (char *fname, char *envvar); /* belongs in libdx somewhere. see loader.c for source and usage. */ extern Error _dxf_fileSearch(char *inname, char **outname, char *extension, char *environment); /* for starting a task on one or all processors. private to the exec */ Error _dxf_ExRunOnAll (PFE func, Pointer arg, int size); Error _dxf_ExRunOn (int JID, PFE func, Pointer arg, int size); /* * token and private flag constants: */ #define T_NONE 0x00000000 #define T_MODULE 0x00000001 #define T_CATEGORY 0x00000002 #define T_DESCRIPTION 0x00000004 #define T_INPUT 0x00000008 #define T_REPEAT 0x00000010 #define T_OUTPUT 0x00000020 #define T_OPTIONS 0x00000040 #define T_FLAGS 0x00000080 #define T_OUTBOARD 0x00000100 #define T_LOADABLE 0x00000200 #define T_ERROR 0x00008000 #define PF_RELOAD 0x00000000 /* redefinition of an existing module */ #define PF_LOADABLE 0x00000001 /* run-time dynamicly loaded module */ #define PF_OUTBOARD 0x00000002 /* outboard module definition */ #define F_NONE 0 #define REPCOUNT 20 /* should match constant in mdf2c file */ /* this now works for both outboards and loadable modules. */ struct modargs { char * thisarg; /* name of this input/output */ char * deflt; /* default value (not used yet) */ struct modargs * nextarg; /* linked list pointer */ }; struct moddef { char * m_name; /* module name */ PFI m_func; /* entry point - function address */ int m_flags; /* module flags */ int m_nin; /* number of inputs */ struct modargs * m_innames; /* list of inputs - modargs structs */ int m_nout; /* number of outputs */ struct modargs * m_outnames; /* list of outputs - modargs structs */ char * m_exec; /* for outboards, filename to run */ char * m_host; /* for outboards, host to run on */ int m_pflags; /* private flags for parsemdf use */ char * m_loadfile; /* filename for loadable modules */ }; extern Error DXOutboard (Object *in, Object *out); extern EXDictionary _dxd_exGraphCache; extern int _dxd_exGoneMP; static Error do_mdf_load(struct moddef *mp); /* this has to be in global memory for MP systems * since adding run-time modules has to happen on * all processors */ static struct moddef *newmod() { struct moddef *mp; mp = DXAllocateZero (sizeof(struct moddef)); if (!mp) return NULL; return mp; } static Error delmod(struct moddef *mp) { struct modargs *map, *next; int i; if (!mp) return OK; if (mp->m_name) DXFree ((Pointer)mp->m_name); if (mp->m_exec) DXFree ((Pointer)mp->m_exec); if (mp->m_host) DXFree ((Pointer)mp->m_host); if (mp->m_loadfile) DXFree ((Pointer)mp->m_loadfile); /* follow the chains here and delete the char strings */ for (i=0, map = mp->m_innames; im_nin; i++, map=next) { if (!map) break; next = map->nextarg; DXFree ((Pointer)map->thisarg); DXFree ((Pointer)map->deflt); DXFree ((Pointer)map); } for (i=0, map = mp->m_outnames; im_nout; i++, map=next) { if (!map) break; next = map->nextarg; DXFree ((Pointer)map->thisarg); DXFree ((Pointer)map->deflt); DXFree ((Pointer)map); } DXFree ((Pointer)mp); return OK; } #define INPUTARG 1 #define OUTPUTARG 2 static Error addarg(struct moddef *mp, int type, char *name, char *def) { struct modargs *map, *newmap; int i; if (!mp) return OK; newmap = (struct modargs *)DXAllocateZero (sizeof(struct modargs)); if (!newmap) return ERROR; newmap->thisarg = name; newmap->deflt = def; if (type == INPUTARG) { mp->m_nin++; if (!mp->m_innames) { mp->m_innames = newmap; return OK; } map = mp->m_innames; } else { mp->m_nout++; if (!mp->m_outnames) { mp->m_outnames = newmap; return OK; } map = mp->m_outnames; } while (map->nextarg) map = map->nextarg; map->nextarg = newmap; return OK; } static Error argdup(struct moddef *mp, int type, int repcount) { int i; int startrep; struct modargs *map; char *name, *value; if (type == INPUTARG) { startrep = mp->m_nin - repcount; map = mp->m_innames; } else { startrep = mp->m_nout - repcount; map = mp->m_outnames; } if (startrep < 0) { DXSetError(ERROR_INVALID_DATA, "invalid repeat count"); return ERROR; } for (i=0; inextarg) ; for (i=0; ithisarg) { name = DXAllocateZero (strlen(map->thisarg) + 4); if (!name) return ERROR; sprintf(name, "%s%d", map->thisarg, i); } value = NULL; if (map->deflt) { value = DXAllocateZero (strlen(map->deflt) + 1); if (!value) return ERROR; } if (!addarg(mp, type, name, value)) return ERROR; } return OK; } /* * DXAddModuleV (name, func, flags, nin, inlist, nout, outlist, exec, host) * * struct moddef { * char * m_name; * PFI m_func; * int m_flags; * int m_nin; * struct modargs * m_innames; * int m_nout; * struct modargs * m_outnames; * char * m_exec; * char * m_host; * int m_pflags; * char * m_loadfile; * } */ static Error callmdf_remote(char *fname) { if (! DXLoadAndRunObjFile(fname, "MODULES")) return ERROR; else return OK; } static Error callmdf(struct moddef *mp, int doremote) { int rc; /* check the parms against the already defined module */ if (mp->m_pflags == PF_RELOAD) { /* add code here */ if (!(mp->m_pflags & PF_LOADABLE) && !(mp->m_pflags & PF_OUTBOARD)) { /* set error code here? we are going to ignore this defn */ DXMessage("ignoring redefinition of module %s", mp->m_name); return OK; } /* fall through - it's ok to reload an outboard or runtime loadable */ } if (mp->m_pflags & PF_LOADABLE) { /* on an MP machine, we have to load the code into the * running exec for each process. */ if (DXProcessors(0) > 1 && _dxd_exGoneMP) { #ifdef DXD_NO_MP_RUNTIME if (_dxd_exGoneMP) { /* can't do runtime loadable after forking on sgi or solaris */ #define LONGHELP "Runtime-loadable modules cannot be added after startup when running with more than 1 processor. Either specify -mdf on the startup command line, or run -processors 1" DXSetError(ERROR_INVALID_DATA, LONGHELP); return ERROR; } #endif if (_dxf_ExRunOnAll (callmdf_remote, mp->m_loadfile, strlen(mp->m_loadfile)+1) == ERROR) { DXAddMessage("module %s", mp->m_name); return ERROR; } } else { /* do the load for a single process machine, or MP machine * before the fork. */ if (!DXLoadAndRunObjFile(mp->m_loadfile, "DXMODULES")) { DXAddMessage("module %s", mp->m_name); return ERROR; } } return OK; } if (mp->m_flags & MODULE_OUTBOARD) mp->m_func = DXOutboard; if (mp->m_func == NULL) { DXSetError(ERROR_INVALID_DATA, "module %s must have an OUTBOARD or LOADABLE entry to be added at run time", mp->m_name); return ERROR; } /* mark this module as being loaded at run time instead of being * known at compile time. i don't think any one cares, but just * in case it turns out to be useful... */ mp->m_flags |= RUNTIMELOAD; /* the work happens here. */ if (DXProcessors(0) == 1 || _dxd_exGoneMP == 0) rc = do_mdf_load(mp); else rc = _dxf_ExRunOn (1, do_mdf_load, (Pointer)mp, 0); /* if running distributed, tell the others about the new module. */ if (doremote) _dxf_ExDistributeMsg(DM_LOADMDF, (Pointer)mp, 0, TOSLAVES); return rc; } static Error do_mdf_load(struct moddef *mp) { int i; char **inlist = NULL, **outlist = NULL; struct modargs *map; Error rc; if (mp->m_nin) { inlist = (char **)DXAllocateZero(mp->m_nin * sizeof (char *)); if (!inlist) return ERROR; } if (mp->m_nout) { outlist = (char **)DXAllocateZero(mp->m_nout * sizeof (char *)); if (!outlist) { DXFree((Pointer)inlist); return ERROR; } } for (i=0, map=mp->m_innames; im_nin; i++, map=map->nextarg) inlist[i] = map->thisarg; for (i=0, map=mp->m_outnames; im_nout; i++, map=map->nextarg) outlist[i] = map->thisarg; /* this loads the executive jump table. */ rc = DXAddModuleV(mp->m_name, mp->m_func, mp->m_flags, mp->m_nin, inlist, mp->m_nout, outlist, mp->m_exec, mp->m_host); /* we may have some graph with these functions built in already. * this forces us to rebuild all graphs with the new mdf defintion. */ if ((node *)_dxf_ExMacroSearch (mp->m_name) != NULL) _dxf_ExDictionaryPurge (_dxd_exGraphCache); DXFree((Pointer)inlist); DXFree((Pointer)outlist); return rc; } /* communicating with remote execs for distributed. */ void _dxf_ExSendMdfPkg(Pointer *data, int tofd) { int i, len; struct moddef *mp; struct modargs *map; mp = (struct moddef *)data; len = (mp->m_name) ? strlen(mp->m_name) : 0; _dxf_ExWriteSock(tofd, &len, sizeof(int)); if (len > 0) _dxf_ExWriteSock(tofd, mp->m_name, len); _dxf_ExWriteSock(tofd, &mp->m_flags, sizeof(int)); _dxf_ExWriteSock(tofd, &mp->m_nin, sizeof(int)); map = mp->m_innames; for (i=0, map=mp->m_innames; im_nin; i++, map=map->nextarg) { len = (map->thisarg) ? strlen(map->thisarg) : 0; _dxf_ExWriteSock(tofd, &len, sizeof(int)); if (len > 0) _dxf_ExWriteSock(tofd, map->thisarg, len); /* i'm NOT writing the default value here because they are not * supported yet. this will have to be added sometime. */ } _dxf_ExWriteSock(tofd, &mp->m_nout, sizeof(int)); map = mp->m_outnames; for (i=0, map=mp->m_outnames; im_nout; i++, map=map->nextarg) { len = (map->thisarg) ? strlen(map->thisarg) : 0; _dxf_ExWriteSock(tofd, &len, sizeof(int)); if (len > 0) _dxf_ExWriteSock(tofd, map->thisarg, len); /* i'm NOT writing the default value here because they are not * supported yet. this will have to be added sometime. */ } len = (mp->m_exec) ? strlen(mp->m_exec) : 0; _dxf_ExWriteSock(tofd, &len, sizeof(int)); if (len > 0) _dxf_ExWriteSock(tofd, mp->m_exec, len); len = (mp->m_host) ? strlen(mp->m_host) : 0; _dxf_ExWriteSock(tofd, &len, sizeof(int)); if (len > 0) _dxf_ExWriteSock(tofd, mp->m_host, len); _dxf_ExWriteSock(tofd, &mp->m_pflags, sizeof(int)); len = (mp->m_loadfile) ? strlen(mp->m_loadfile) : 0; _dxf_ExWriteSock(tofd, &len, sizeof(int)); if (len > 0) _dxf_ExWriteSock(tofd, mp->m_loadfile, len); return; } Error _dxf_ExRecvMdfPkg(int fromfd, int swap) { int i, len, num; struct moddef *mp; struct modargs *map; char *argname; if ((mp = newmod()) == NULL) return ERROR; _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap); if (len > 0) { mp->m_name = DXAllocateZero(len+1); _dxf_ExReceiveBuffer(fromfd, mp->m_name, len, TYPE_UBYTE, swap); } _dxf_ExReceiveBuffer(fromfd, &mp->m_flags, 1, TYPE_INT, swap); _dxf_ExReceiveBuffer(fromfd, &num, 1, TYPE_INT, swap); for (i=0; i 0) { argname = DXAllocateZero(len+1); _dxf_ExReceiveBuffer(fromfd, argname, len, TYPE_UBYTE, swap); addarg(mp, INPUTARG, argname, NULL); } } _dxf_ExReceiveBuffer(fromfd, &num, 1, TYPE_INT, swap); for (i=0; i 0) { argname = DXAllocateZero(len+1); _dxf_ExReceiveBuffer(fromfd, argname, len, TYPE_UBYTE, swap); addarg(mp, OUTPUTARG, argname, NULL); } } _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap); if (len > 0) { mp->m_exec = DXAllocateZero(len+1); _dxf_ExReceiveBuffer(fromfd, mp->m_exec, len, TYPE_UBYTE, swap); } _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap); if (len > 0) { mp->m_host = DXAllocateZero(len+1); _dxf_ExReceiveBuffer(fromfd, mp->m_host, len, TYPE_UBYTE, swap); } _dxf_ExReceiveBuffer(fromfd, &mp->m_pflags, 1, TYPE_INT, swap); _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap); if (len > 0) { mp->m_loadfile = DXAllocateZero(len+1); _dxf_ExReceiveBuffer(fromfd, mp->m_loadfile, len, TYPE_UBYTE, swap); } if (!callmdf(mp, 0)) goto error; delmod(mp); return OK; error: if (mp) delmod(mp); return ERROR; } /* * utility type routines. */ /* return pointer to start of next line. if flag is set, assume you are * in the middle of a line and always search forward. if flag is zero, * assume you could already be at the start of the line and only space * forward if the line is empty or a comment. */ static char *nextline(char *str, int forward, int *lineno) { char *cp = str; if (!cp) return NULL; again: (*lineno)++; if (!forward) { if (*cp != '#' && *cp != '\n' && *cp != '\r') return cp; forward = 1; } while (*cp != '\0' && *cp != '\n' && *cp != '\r') cp++; if (*cp == '\0') return NULL; if (*cp == '\n' || *cp == '\r') { while (*cp == '\n' || *cp == '\r') cp ++; forward = 0; goto again; } if (*cp == '#') goto again; return cp; } /* return a pointer to the next char which matches. this is different * than strindex or strstr because it won't space past a newline. * return NULL if there isn't a match before the next newline or * end of file (actually, end of string). */ static char *find_next(char *str, char find) { char *cp = str; if (cp == NULL) return NULL; while (*cp != '\0' && *cp != '\n' && *cp != find) cp++; return (*cp == find) ? cp : NULL; } static char *end_of_line(char *str) { return find_next(str, '\n'); } /* * return start of next whitespace separated token. return NULL if * end-of-line or end-of-string encountered first. if semi is set, * allow semicolons to be considered as whitespace. */ static char *find_next_token(char *str, int semi) { char *cp = str; if (cp == NULL) return NULL; /* find whitespace (or semi) */ if (semi) while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && *cp != '\r' && *cp != ';') cp++; else while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && *cp != '\r') cp++; if (*cp == '\r') cp++; if (*cp == '\0' || *cp == '\n') return NULL; /* now find non-whitespace */ if (semi && *cp == ';') cp++; while (*cp != '\0' && *cp != '\n' && *cp != '\r' && (*cp == ' ' || *cp == '\t')) cp++; if (*cp == '\r') cp++; if (*cp == '\0' || *cp == '\n') return NULL; return cp; } /* return the end of the current string of chars, delimited by either * end-of-line, end-of-string, whitespace or semicolon. */ static char *find_token_end(char *str) { char *cp = str; if (cp == NULL) return NULL; while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && *cp != '\r' && *cp != ';') cp++; return cp; } /* names have to start with an alpha, and then * contain only alphas or digits */ static int IsGoodIdentifier(char *name) { char *cp = name; if (!isalpha(*cp++)) return FALSE; while (*cp) { if (!isalnum(*cp)) return FALSE; cp++; } return TRUE; } /* allocate space for and make a copy of the next string up to the * next ';', ' ' or end of line */ static char *AllocToken(char *str) { char *newbuf; char *strend; strend = find_token_end(str); newbuf = DXAllocate(strend - str + 1); if (!newbuf) return NULL; strncpy(newbuf, str, strend-str); newbuf[strend-str] = '\0'; return newbuf; } /* allocate space for and make a copy of the next string up to the * next '"' */ static char *AllocQuote(char *str) { char *newbuf; char *strend; char *last; last = end_of_line(str); strend = find_next(str, '"'); if (!strend || (last && strend > last)) { strend = last; if (!strend) { DXSetError(ERROR_INVALID_DATA, "missing name or mismatched quotes"); return NULL; } } newbuf = DXAllocate(strend - str + 1); if (!newbuf) return NULL; strncpy(newbuf, str, strend-str); newbuf[strend-str] = '\0'; return newbuf; } /* allocate space for and make a copy of the next string up to the * next ';' or end of line, including looking for attributes and squeezing * any intervening spaces from them. (should this be necessary? * why doesn't the exec code handle spaces?) */ static char *AllocVariable(char *str) { char *newbuf; char *strend; char *cp, *dest; strend = find_token_end(str); if (!strend) return NULL; cp = strend; while (*cp == ' ') cp++; if(*cp == '[') { strend = strchr(cp, ']'); if(!strend) return NULL; strend++; } newbuf = DXAllocate(strend - str + 1); if (!newbuf) return NULL; for (cp = str, dest = newbuf; cp < strend; cp++) if (!isspace(*cp)) *dest++ = *cp; *dest = '\0'; return newbuf; } static int IDKeyword(char *str) { if (!str || str[0] == '\0') return T_NONE; if (!strncmp(str, "MODULE", sizeof("MODULE")-1)) return T_MODULE; if (!strncmp(str, "CATEGORY", sizeof("CATEGORY")-1)) return T_CATEGORY; if (!strncmp(str, "DESCRIPTION", sizeof("DESCRIPTION")-1)) return T_DESCRIPTION; if (!strncmp(str, "INPUT", sizeof("INPUT")-1)) return T_INPUT; if (!strncmp(str, "REPEAT", sizeof("REPEAT")-1)) return T_REPEAT; if (!strncmp(str, "OUTPUT", sizeof("OUTPUT")-1)) return T_OUTPUT; if (!strncmp(str, "OPTIONS", sizeof("OPTIONS")-1)) return T_OPTIONS; if (!strncmp(str, "FLAGS", sizeof("FLAGS")-1)) return T_FLAGS; if (!strncmp(str, "OUTBOARD", sizeof("OUTBOARD")-1)) return T_OUTBOARD; if (!strncmp(str, "LOADABLE", sizeof("LOADABLE")-1)) return T_LOADABLE; return T_ERROR; } static int IDFlag(char *str) { if (!str || str[0] == '\0') return 0; #if 0 if (!strncmp(str, "SERIAL", sizeof("SERIAL")-1)) return MODULE_SERIAL; if (!strncmp(str, "SEQUENCED", sizeof("SEQUENCED")-1)) return MODULE_SEQUENCED; #endif if (!strncmp(str, "PIN", sizeof("PIN")-1)) return MODULE_PIN; #if 0 if (!strncmp(str, "ASSIGN", sizeof("ASSIGN")-1)) return MODULE_ASSIGN; #endif if (!strncmp(str, "SIDE_EFFECT", sizeof("SIDE_EFFECT")-1)) return MODULE_SIDE_EFFECT; if (!strncmp(str, "LOOP", sizeof("LOOP")-1)) return MODULE_LOOP; #if 0 if (!strncmp(str, "JOIN", sizeof("JOIN")-1)) return MODULE_JOIN; #endif if (!strncmp(str, "ERR_CONT", sizeof("ERR_CONT")-1)) return MODULE_ERR_CONT; if (!strncmp(str, "REROUTABLE", sizeof("REROUTABLE")-1)) return MODULE_REROUTABLE; if (!strncmp(str, "REACH", sizeof("REACH")-1)) return MODULE_REACH; if (!strncmp(str, "OUTBOARD", sizeof("OUTBOARD")-1)) return MODULE_OUTBOARD; if (!strncmp(str, "PERSISTENT", sizeof("PERSISTENT")-1)) return MODULE_PERSISTENT; if (!strncmp(str, "ASYNC", sizeof("ASYNC")-1)) return MODULE_ASYNC; if (!strncmp(str, "ASYNCHRONOUS", sizeof("ASYNCHRONOUS")-1)) return MODULE_ASYNC; if (!strncmp(str, "ASYNCLOCAL", sizeof("ASYNCLOCAL")-1)) return MODULE_ASYNCLOCAL; return 0; } /* * parse the string into lines, and call DXAddModuleV for each new * module definition. */ static Error ExParseMDF(char *str) { int lineno = 0; int modflag; int repcount; int loadable = 0; /* see the comment below in the loadable section */ int id, rc; int argtype = -1; char *tempc; char *nextc, *cp; struct moddef *mp = NULL; /* start at the first non-comment line in the string */ nextc = nextline(str, 0, &lineno); while ((id = IDKeyword(nextc)) != T_NONE) { switch (id) { case T_MODULE: /* output previous module definition */ if (mp) { if (!callmdf(mp, 1)) goto error; delmod(mp); } /* start new definition */ mp = newmod(); if (!mp) goto error; nextc = find_next_token(nextc, 0); if (!nextc) { DXSetError(ERROR_INVALID_DATA, "missing module name, line %d", lineno); goto error; } mp->m_name = AllocToken(nextc); if (!IsGoodIdentifier(mp->m_name)) { DXSetError(ERROR_INVALID_DATA, "module names must start with a letter and contain " "only letters and numbers, line %d", lineno); goto error; } #if 0 /* reload == 0, so that's the default */ /* does it already exist? */ if ((node *)_dxf_ExMacroSearch (mp->m_name) != NULL) mp->m_pflags |= PF_RELOAD; #endif break; /* get name only; ignore type, default & description. */ case T_INPUT: case T_OUTPUT: nextc = find_next_token(nextc, 1); if (!nextc) { DXSetError(ERROR_INVALID_DATA, "missing %s parameter name, line %d", (id==T_INPUT) ? "input" : "output", lineno); goto error; } tempc = AllocVariable(nextc); if (!tempc) goto error; if (!addarg(mp, (id==T_INPUT) ? INPUTARG : OUTPUTARG, tempc, NULL)) goto error; /* save this for later, if handling repeats */ argtype = id; break; case T_ERROR: /* could this be more helpful? */ DXSetError(ERROR_INVALID_DATA, "unrecognized keyword on line %d", lineno); return ERROR; case T_OUTBOARD: /* get the exec and host name */ tempc = find_next(nextc, '"'); if (!tempc) { nextc = find_next_token(nextc, 1); if (!nextc) { DXSetError(ERROR_INVALID_DATA, "missing outboard execution name, line %d", lineno); goto error; } mp->m_exec = AllocToken(nextc); } else mp->m_exec = AllocQuote(++tempc); if (!mp->m_exec) { DXSetError(ERROR_INVALID_DATA, "missing outboard execution name, line %d", lineno); goto error; } tempc = find_next(nextc, ';'); if (tempc) { nextc = tempc; tempc = find_next_token(nextc, 1); if (tempc) { mp->m_host = AllocToken(tempc); nextc = tempc; } } mp->m_flags |= MODULE_OUTBOARD; mp->m_pflags |= PF_OUTBOARD; break; case T_LOADABLE: /* get the executable filename */ tempc = find_next(nextc, '"'); if (!tempc) { nextc = find_next_token(nextc, 1); if (!nextc) { DXSetError(ERROR_INVALID_DATA, "missing loadable module filename, line %d", lineno); goto error; } mp->m_loadfile = AllocToken(nextc); } else mp->m_loadfile = AllocQuote(++tempc); if (!mp->m_loadfile) { DXSetError(ERROR_INVALID_DATA, "missing loadable module filename, line %d", lineno); goto error; } mp->m_pflags |= PF_LOADABLE; break; case T_FLAGS: tempc = find_next_token(nextc, 0); while ((modflag = IDFlag(tempc)) != F_NONE) { mp->m_flags |= modflag; nextc = tempc; tempc = find_next_token(tempc, 0); } break; case T_REPEAT: nextc = find_next_token(nextc, 0); if (!nextc) { DXSetError(ERROR_INVALID_DATA, "missing repeat count, line %d", lineno); goto error; } repcount = atoi(nextc); if (argtype == T_INPUT) { if (repcount <= 0 || repcount > mp->m_nin) { DXSetError(ERROR_INVALID_DATA, "invalid input repeat count, line %d", lineno); goto error; } if (!argdup(mp, INPUTARG, repcount)) { DXAddMessage("line %d", lineno); goto error; } } else if (argtype == T_OUTPUT) { if (repcount <= 0 || repcount > mp->m_nout) { DXSetError(ERROR_INVALID_DATA, "invalid output repeat count, line %d", lineno); goto error; } if (!argdup(mp, OUTPUTARG, repcount)) { DXAddMessage("line %d", lineno); goto error; } } else { DXSetError(ERROR_INVALID_DATA, "misplaced REPEAT line, line %d", lineno); } break; case T_CATEGORY: case T_DESCRIPTION: case T_OPTIONS: /* ignore rest of line */ break; } nextc = nextline(nextc, 1, &lineno); } done: /* at end-of-string, finish current definition if there is one */ if (mp) { if (!callmdf(mp, 1)) goto error; delmod(mp); } return OK; error: if (mp) delmod(mp); return ERROR; } /* if file, read into memory and then parse the same way a string obj * would be parsed. */ Error DXLoadMDFFile(char *filename) { int fd; int len, rlen; int rc = ERROR; char *foundname = NULL; char *cp = NULL; if (_dxf_fileSearch(filename, &foundname, "mdf", "DXMODULES") == ERROR) return ERROR; fd = open(foundname, O_RDONLY); if (fd < 0) { DXSetError(ERROR_INVALID_DATA, "cannot open %s as MDF file", foundname); goto error; } len = lseek(fd, 0, 2); /* find length of file */ if (len <= 0) { DXSetError(ERROR_INVALID_DATA, "error reading from %s", foundname); goto error; } lseek(fd, 0, 0); cp = (char *)DXAllocate(len+1); if (!cp) goto error; rlen = read(fd, cp, len); if (rlen != len) { DXSetError(ERROR_INVALID_DATA, "error reading from %s", foundname); goto error; } cp[len] = '\0'; close (fd); rc = ExParseMDF(cp); error: DXFree((Pointer)cp); DXFree((Pointer)foundname); return rc; } Error DXLoadMDFString(char *cp) { return ExParseMDF(cp); }