1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <fcntl.h>
16 
17 #include <dx/dx.h>
18 
19 #if defined(HAVE_UNISTD_H)
20 #include <unistd.h>
21 #endif
22 
23 #include "config.h"
24 #include "pmodflags.h"
25 #include "utils.h"
26 #include "distp.h"
27 #include "parse.h"
28 #include "parsemdf.h"
29 #include "_macro.h"
30 #include "remote.h"
31 #include "loader.h"
32 #include "graph.h"
33 #include "function.h"
34 
35 static Error do_mdf_load(struct moddef *mp);
36 
37 /* this has to be in global memory for MP systems
38  * since adding run-time modules has to happen on
39  * all processors
40  */
newmod()41 static struct moddef *newmod()
42 {
43     struct moddef *mp;
44 
45     mp = DXAllocateZero (sizeof(struct moddef));
46     if (!mp)
47 	return NULL;
48 
49     return mp;
50 }
51 
52 
delmod(struct moddef * mp)53 static Error delmod(struct moddef *mp)
54 {
55     struct modargs *map, *next;
56     int i;
57 
58     if (!mp)
59 	return OK;
60 
61     if (mp->m_name)
62 	DXFree ((Pointer)mp->m_name);
63     if (mp->m_exec)
64 	DXFree ((Pointer)mp->m_exec);
65     if (mp->m_host)
66 	DXFree ((Pointer)mp->m_host);
67     if (mp->m_loadfile)
68 	DXFree ((Pointer)mp->m_loadfile);
69 
70     /* follow the chains here and delete the char strings */
71     for (i=0, map = mp->m_innames; i<mp->m_nin; i++, map=next) {
72 	if (!map)
73 	    break;
74 
75 	next = map->nextarg;
76 	DXFree ((Pointer)map->thisarg);
77 	DXFree ((Pointer)map->deflt);
78 	DXFree ((Pointer)map);
79     }
80     for (i=0, map = mp->m_outnames; i<mp->m_nout; i++, map=next) {
81 	if (!map)
82 	    break;
83 
84 	next = map->nextarg;
85 	DXFree ((Pointer)map->thisarg);
86 	DXFree ((Pointer)map->deflt);
87 	DXFree ((Pointer)map);
88     }
89 
90     DXFree ((Pointer)mp);
91     return OK;
92 }
93 
94 #define INPUTARG 1
95 #define OUTPUTARG 2
96 
addarg(struct moddef * mp,int type,char * name,char * def)97 static Error addarg(struct moddef *mp, int type, char *name, char *def)
98 {
99     struct modargs *map, *newmap;
100 
101     if (!mp)
102 	return OK;
103 
104     newmap = (struct modargs *)DXAllocateZero (sizeof(struct modargs));
105     if (!newmap)
106 	return ERROR;
107 
108     newmap->thisarg = name;
109     newmap->deflt = def;
110 
111     if (type == INPUTARG)
112     {
113 	mp->m_nin++;
114 	if (!mp->m_innames)
115 	{
116 	    mp->m_innames = newmap;
117 	    return OK;
118 	}
119 	map = mp->m_innames;
120     }
121     else
122     {
123 	mp->m_nout++;
124 	if (!mp->m_outnames)
125 	{
126 	    mp->m_outnames = newmap;
127 	    return OK;
128 	}
129 	map = mp->m_outnames;
130     }
131 
132     while (map->nextarg)
133 	map = map->nextarg;
134 
135     map->nextarg = newmap;
136     return OK;
137 }
138 
139 
argdup(struct moddef * mp,int type,int repcount)140 static Error argdup(struct moddef *mp, int type, int repcount)
141 {
142     int i;
143     int startrep;
144     struct modargs *map;
145     char *name, *value;
146 
147     if (type == INPUTARG) {
148 	startrep = mp->m_nin - repcount;
149 	map = mp->m_innames;
150     } else {
151 	startrep = mp->m_nout - repcount;
152 	map = mp->m_outnames;
153     }
154     if (startrep < 0) {
155 	DXSetError(ERROR_DATA_INVALID, "invalid repeat count");
156 	return ERROR;
157     }
158 
159     for (i=0; i<startrep; i++, map=map->nextarg)
160 	;
161 
162     for (i=0; i<repcount*REPCOUNT; i++) {
163 	name = NULL;
164 	if (map->thisarg) {
165 	    name = DXAllocateZero (strlen(map->thisarg) + 4);
166 	    if (!name)
167 		return ERROR;
168 	    sprintf(name, "%s%d", map->thisarg, i);
169 	}
170 
171 	value = NULL;
172 	if (map->deflt) {
173 	    value = DXAllocateZero (strlen(map->deflt) + 1);
174 	    if (!value)
175 		return ERROR;
176 	}
177 
178 	if (!addarg(mp, type, name, value))
179 	    return ERROR;
180     }
181 
182     return OK;
183 }
184 
185 
186 
187 /*
188  *  DXAddModuleV (name, func, flags, nin, inlist, nout, outlist, exec, host)
189  *
190  * struct moddef {
191  *    char *		m_name;
192  *    PFI		m_func;
193  *    int		m_flags;
194  *    int		m_nin;
195  *    struct modargs *	m_innames;
196  *    int		m_nout;
197  *    struct modargs *	m_outnames;
198  *    char *		m_exec;
199  *    char *		m_host;
200  *    int		m_pflags;
201  *    char *		m_loadfile;
202  * }
203  */
204 
205 static Error
callmdf_remote(char * fname)206 callmdf_remote(char *fname)
207 {
208     if (! DXLoadAndRunObjFile(fname, "MODULES"))
209 	return ERROR;
210     else
211 	return OK;
212 }
213 
callmdf(struct moddef * mp,int doremote)214 static Error callmdf(struct moddef *mp, int doremote)
215 {
216     int rc;
217 
218 
219     /* check the parms against the already defined module */
220     if (mp->m_pflags == PF_RELOAD) {
221 	/* add code here */
222 	if (!(mp->m_pflags & PF_LOADABLE) && !(mp->m_pflags & PF_OUTBOARD)) {
223 	    /* set error code here?  we are going to ignore this defn */
224 	    DXMessage("ignoring redefinition of module %s", mp->m_name);
225 	    return OK;
226 	}
227 	/* fall through - it's ok to reload an outboard or runtime loadable */
228     }
229 
230     if (mp->m_pflags & PF_LOADABLE) {
231 	/* on an MP machine, we have to load the code into the
232          * running exec for each process.
233 	 */
234 
235         if (DXProcessors(0) > 1 && _dxd_exGoneMP) {
236 
237 #ifdef DXD_NO_MP_RUNTIME
238 	    if (_dxd_exGoneMP) {
239 		/* can't do runtime loadable after forking on sgi or solaris */
240 #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"
241 		DXSetError(ERROR_DATA_INVALID, LONGHELP);
242 		return ERROR;
243 	    }
244 #endif
245 
246 	    if (_dxf_ExRunOnAll (callmdf_remote, mp->m_loadfile,
247 				 strlen(mp->m_loadfile)+1) == ERROR) {
248 		DXAddMessage("module %s", mp->m_name);
249 		return ERROR;
250 	    }
251 	} else {
252 	    /* do the load for a single process machine, or MP machine
253 	     * before the fork.
254 	     */
255 	    if (!DXLoadAndRunObjFile(mp->m_loadfile, "DXMODULES")) {
256 		DXAddMessage("module %s", mp->m_name);
257 		return ERROR;
258 	    }
259 	}
260 	return OK;
261     }
262 
263     if (mp->m_flags & MODULE_OUTBOARD)
264 	mp->m_func = DXOutboard;
265 
266     if (mp->m_func == NULL) {
267 	DXSetError(ERROR_DATA_INVALID,
268 	      "module %s must have an OUTBOARD or LOADABLE entry to be added at run time",
269 	       mp->m_name);
270 	return ERROR;
271     }
272 
273     /* mark this module as being loaded at run time instead of being
274      * known at compile time.  i don't think any one cares, but just
275      * in case it turns out to be useful...
276      */
277     mp->m_flags |= RUNTIMELOAD;
278 
279 
280     /* the work happens here.
281      */
282     if (DXProcessors(0) == 1 || _dxd_exGoneMP == 0)
283 	rc = do_mdf_load(mp);
284     else
285 	rc = _dxf_ExRunOn (1, do_mdf_load, (Pointer)mp, 0);
286 
287 
288     /* if running distributed, tell the others about the new module.
289      */
290     if (doremote)
291 	_dxf_ExDistributeMsg(DM_LOADMDF, (Pointer)mp, 0, TOSLAVES);
292 
293     return rc;
294 }
295 
296 
do_mdf_load(struct moddef * mp)297 static Error do_mdf_load(struct moddef *mp)
298 {
299     int i;
300     char **inlist = NULL, **outlist = NULL;
301     struct modargs *map;
302     Error rc;
303 
304     if (mp->m_nin) {
305 	inlist = (char **)DXAllocateZero(mp->m_nin * sizeof (char *));
306 	if (!inlist)
307 	    return ERROR;
308     }
309     if (mp->m_nout) {
310 	outlist = (char **)DXAllocateZero(mp->m_nout * sizeof (char *));
311 	if (!outlist) {
312 	    DXFree((Pointer)inlist);
313 	    return ERROR;
314 	}
315     }
316 
317     for (i=0, map=mp->m_innames; i<mp->m_nin; i++, map=map->nextarg)
318 	inlist[i] = map->thisarg;
319 
320     for (i=0, map=mp->m_outnames; i<mp->m_nout; i++, map=map->nextarg)
321 	outlist[i] = map->thisarg;
322 
323 
324     /* this loads the executive jump table.
325      */
326     rc = DXAddModuleV(mp->m_name, mp->m_func, mp->m_flags,
327 		      mp->m_nin, inlist,
328 		      mp->m_nout, outlist,
329 		      mp->m_exec, mp->m_host);
330 
331 
332     /* we may have some graph with these functions built in already.
333      * this forces us to rebuild all graphs with the new mdf defintion.
334      */
335     if ((node *)_dxf_ExMacroSearch (mp->m_name) != NULL)
336         _dxf_ExDictionaryPurge (_dxd_exGraphCache);
337 
338 
339     DXFree((Pointer)inlist);
340     DXFree((Pointer)outlist);
341 
342     return rc;
343 }
344 
345 
346 /* communicating with remote execs for distributed.
347  */
348 
_dxf_ExSendMdfPkg(Pointer * data,int tofd)349 void _dxf_ExSendMdfPkg(Pointer *data, int tofd)
350 {
351     int i, len;
352     struct moddef *mp;
353     struct modargs *map;
354 
355     mp = (struct moddef *)data;
356 
357     len = (mp->m_name) ? strlen(mp->m_name) : 0;
358     _dxf_ExWriteSock(tofd, &len, sizeof(int));
359     if (len > 0)
360 	_dxf_ExWriteSock(tofd, mp->m_name, len);
361     _dxf_ExWriteSock(tofd, &mp->m_flags, sizeof(int));
362 
363     _dxf_ExWriteSock(tofd, &mp->m_nin, sizeof(int));
364     map = mp->m_innames;
365     for (i=0, map=mp->m_innames; i<mp->m_nin; i++, map=map->nextarg) {
366 	len = (map->thisarg) ? strlen(map->thisarg) : 0;
367 	_dxf_ExWriteSock(tofd, &len, sizeof(int));
368 	if (len > 0)
369 	    _dxf_ExWriteSock(tofd, map->thisarg, len);
370 	/* i'm NOT writing the default value here because they are not
371 	 * supported yet.  this will have to be added sometime.
372 	 */
373     }
374 
375     _dxf_ExWriteSock(tofd, &mp->m_nout, sizeof(int));
376     map = mp->m_outnames;
377     for (i=0, map=mp->m_outnames; i<mp->m_nout; i++, map=map->nextarg) {
378 	len = (map->thisarg) ? strlen(map->thisarg) : 0;
379 	_dxf_ExWriteSock(tofd, &len, sizeof(int));
380 	if (len > 0)
381 	    _dxf_ExWriteSock(tofd, map->thisarg, len);
382 	/* i'm NOT writing the default value here because they are not
383 	 * supported yet.  this will have to be added sometime.
384 	 */
385     }
386 
387     len = (mp->m_exec) ? strlen(mp->m_exec) : 0;
388     _dxf_ExWriteSock(tofd, &len, sizeof(int));
389     if (len > 0)
390 	_dxf_ExWriteSock(tofd, mp->m_exec, len);
391     len = (mp->m_host) ? strlen(mp->m_host) : 0;
392     _dxf_ExWriteSock(tofd, &len, sizeof(int));
393     if (len > 0)
394 	_dxf_ExWriteSock(tofd, mp->m_host, len);
395 
396     _dxf_ExWriteSock(tofd, &mp->m_pflags, sizeof(int));
397     len = (mp->m_loadfile) ? strlen(mp->m_loadfile) : 0;
398     _dxf_ExWriteSock(tofd, &len, sizeof(int));
399     if (len > 0)
400 	_dxf_ExWriteSock(tofd, mp->m_loadfile, len);
401 
402     return;
403 }
404 
405 
_dxf_ExRecvMdfPkg(int fromfd,int swap)406 Error _dxf_ExRecvMdfPkg(int fromfd, int swap)
407 {
408     int i, len, num;
409     struct moddef *mp;
410     char *argname;
411 
412 
413     if ((mp = newmod()) == NULL)
414 	return ERROR;
415 
416     _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap);
417     if (len > 0) {
418 	mp->m_name = DXAllocateZero(len+1);
419 	_dxf_ExReceiveBuffer(fromfd, mp->m_name, len, TYPE_UBYTE, swap);
420     }
421     _dxf_ExReceiveBuffer(fromfd, &mp->m_flags, 1, TYPE_INT, swap);
422 
423     _dxf_ExReceiveBuffer(fromfd, &num, 1, TYPE_INT, swap);
424     for (i=0; i<num; i++) {
425 	_dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap);
426 	if (len > 0) {
427 	    argname = DXAllocateZero(len+1);
428 	    _dxf_ExReceiveBuffer(fromfd, argname, len, TYPE_UBYTE, swap);
429 	    addarg(mp, INPUTARG, argname, NULL);
430 	}
431     }
432 
433     _dxf_ExReceiveBuffer(fromfd, &num, 1, TYPE_INT, swap);
434     for (i=0; i<num; i++) {
435 	_dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap);
436 	if (len > 0) {
437 	    argname = DXAllocateZero(len+1);
438 	    _dxf_ExReceiveBuffer(fromfd, argname, len, TYPE_UBYTE, swap);
439 	    addarg(mp, OUTPUTARG, argname, NULL);
440 	}
441     }
442 
443     _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap);
444     if (len > 0) {
445 	mp->m_exec = DXAllocateZero(len+1);
446 	_dxf_ExReceiveBuffer(fromfd, mp->m_exec, len, TYPE_UBYTE, swap);
447     }
448 
449     _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap);
450     if (len > 0) {
451 	mp->m_host = DXAllocateZero(len+1);
452 	_dxf_ExReceiveBuffer(fromfd, mp->m_host, len, TYPE_UBYTE, swap);
453     }
454 
455     _dxf_ExReceiveBuffer(fromfd, &mp->m_pflags, 1, TYPE_INT, swap);
456     _dxf_ExReceiveBuffer(fromfd, &len, 1, TYPE_INT, swap);
457     if (len > 0) {
458 	mp->m_loadfile = DXAllocateZero(len+1);
459 	_dxf_ExReceiveBuffer(fromfd, mp->m_loadfile, len, TYPE_UBYTE, swap);
460     }
461 
462 
463     if (!callmdf(mp, 0))
464 	goto error;
465 
466     delmod(mp);
467     return OK;
468 
469   error:
470     if (mp)
471 	delmod(mp);
472     return ERROR;
473 }
474 
475 
476 
477 
478 /*
479  * utility type routines.
480  */
481 
482 /* return pointer to start of next line.  if flag is set, assume you are
483  *  in the middle of a line and always search forward.  if flag is zero,
484  *  assume you could already be at the start of the line and only space
485  *  forward if the line is empty or a comment.
486  */
nextline(char * str,int forward,int * lineno)487 static char *nextline(char *str, int forward, int *lineno)
488 {
489     char *cp = str;
490 
491     if (!cp)
492 	return NULL;
493 
494   again:
495     (*lineno)++;
496 
497     if (!forward) {
498 	if (*cp != '#' && *cp != '\n' && *cp != '\r')
499 	    return cp;
500 
501 	forward = 1;
502     }
503 
504     while (*cp != '\0' && *cp != '\n' && *cp != '\r')
505 	cp++;
506 
507     if (*cp == '\0')
508 	return NULL;
509 
510     if (*cp == '\n' || *cp == '\r')
511 	{
512 	    while (*cp == '\n' || *cp == '\r')
513 	        cp ++;
514 
515 	    forward = 0;
516 	    goto again;
517     }
518 
519     if (*cp == '#')
520 	goto again;
521 
522     return cp;
523 }
524 
525 
526 /* return a pointer to the next char which matches.  this is different
527  *  than strindex or strstr because it won't space past a newline.
528  *  return NULL if there isn't a match before the next newline or
529  *  end of file (actually, end of string).
530  */
find_next(char * str,char find)531 static char *find_next(char *str, char find)
532 {
533     char *cp = str;
534 
535     if (cp == NULL)
536 	return NULL;
537 
538     while (*cp != '\0' && *cp != '\n' && *cp != find)
539 	cp++;
540 
541     return (*cp == find) ? cp : NULL;
542 }
543 
end_of_line(char * str)544 static char *end_of_line(char *str)
545 {
546     return find_next(str, '\n');
547 }
548 
549 /*
550  * return start of next whitespace separated token.  return NULL if
551  * end-of-line or end-of-string encountered first.  if semi is set,
552  * allow semicolons to be considered as whitespace.
553  */
find_next_token(char * str,int semi)554 static char *find_next_token(char *str, int semi)
555 {
556     char *cp = str;
557 
558     if (cp == NULL)
559 	return NULL;
560 
561     /* find whitespace (or semi)
562      */
563     if (semi)
564 	while (*cp != '\0' && *cp != '\n' &&
565 	       *cp != ' '  && *cp != '\t' &&
566 	       *cp != '\r' && *cp != ';')
567 	    cp++;
568     else
569 	while (*cp != '\0' && *cp != '\n' &&
570 	       *cp != ' ' && *cp != '\t' &&
571 	       *cp != '\r')
572 	    cp++;
573 
574 	if (*cp == '\r')
575 		cp++;
576 
577     if (*cp == '\0' || *cp == '\n')
578 	return NULL;
579 
580     /* now find non-whitespace
581      */
582     if (semi && *cp == ';')
583 	cp++;
584 
585     while (*cp != '\0' && *cp != '\n' &&
586 		*cp != '\r' && (*cp == ' ' || *cp == '\t'))
587 	cp++;
588 
589     if (*cp == '\r')
590 		cp++;
591 
592     if (*cp == '\0' || *cp == '\n')
593 	return NULL;
594 
595     return cp;
596 }
597 
598 /* return the end of the current string of chars, delimited by either
599  * end-of-line, end-of-string, whitespace or semicolon.
600  */
find_token_end(char * str)601 static char *find_token_end(char *str)
602 {
603     char *cp = str;
604 
605     if (cp == NULL)
606 	return NULL;
607 
608     while (*cp != '\0' && *cp != '\n' &&
609 	   *cp != ' '  && *cp != '\t' &&
610 	   *cp != '\r' && *cp != ';')
611 	cp++;
612 
613     return cp;
614 }
615 
616 /* names have to start with an alpha, and then
617  * contain only alphas or digits
618  */
IsGoodIdentifier(char * name)619 static int IsGoodIdentifier(char *name)
620 {
621     char *cp = name;
622 
623     if (!isalpha(*cp++))
624 	return FALSE;
625 
626     while (*cp) {
627 	if (!isalnum(*cp))
628 	    return FALSE;
629 	cp++;
630     }
631 
632     return TRUE;
633 }
634 
635 /* allocate space for and make a copy of the next string up to the
636  * next ';', ' ' or end of line
637  */
AllocToken(char * str)638 static char *AllocToken(char *str)
639 {
640     char *newbuf;
641     char *strend;
642 
643     strend = find_token_end(str);
644 
645     newbuf = DXAllocate(strend - str + 1);
646     if (!newbuf)
647 	return NULL;
648 
649     strncpy(newbuf, str, strend-str);
650     newbuf[strend-str] = '\0';
651 
652     return newbuf;
653 }
654 
655 /* allocate space for and make a copy of the next string up to the
656  * next '"'
657  */
AllocQuote(char * str)658 static char *AllocQuote(char *str)
659 {
660     char *newbuf;
661     char *strend;
662     char *last;
663 
664     last = end_of_line(str);
665     strend = find_next(str, '"');
666     if (!strend || (last && strend > last)) {
667 	strend = last;
668 	if (!strend) {
669 	    DXSetError(ERROR_DATA_INVALID,
670 		       "missing name or mismatched quotes");
671 	    return NULL;
672 	}
673     }
674 
675     newbuf = DXAllocate(strend - str + 1);
676     if (!newbuf)
677 	return NULL;
678 
679     strncpy(newbuf, str, strend-str);
680     newbuf[strend-str] = '\0';
681 
682     return newbuf;
683 }
684 
685 /* allocate space for and make a copy of the next string up to the
686  * next ';' or end of line, including looking for attributes and squeezing
687  * any intervening spaces from them.  (should this be necessary?
688  * why doesn't the exec code handle spaces?)
689  */
AllocVariable(char * str)690 static char *AllocVariable(char *str)
691 {
692     char *newbuf;
693     char *strend;
694     char *cp, *dest;
695 
696     strend = find_token_end(str);
697     if (!strend)
698 	return NULL;
699 
700     cp = strend;
701     while (*cp == ' ')
702 	cp++;
703     if(*cp  == '[') {
704         strend = strchr(cp, ']');
705         if(!strend)
706             return NULL;
707         strend++;
708     }
709 
710     newbuf = DXAllocate(strend - str + 1);
711     if (!newbuf)
712 	return NULL;
713 
714     for (cp = str, dest = newbuf; cp < strend; cp++)
715 	if (!isspace(*cp))
716 	    *dest++ = *cp;
717 
718     *dest = '\0';
719 
720     return newbuf;
721 }
722 
IDKeyword(char * str)723 static int IDKeyword(char *str)
724 {
725     if (!str || str[0] == '\0')
726 	return T_NONE;
727 
728     if (!strncmp(str, "MODULE", sizeof("MODULE")-1))
729 	return T_MODULE;
730 
731     if (!strncmp(str, "CATEGORY", sizeof("CATEGORY")-1))
732 	return T_CATEGORY;
733 
734     if (!strncmp(str, "DESCRIPTION", sizeof("DESCRIPTION")-1))
735 	return T_DESCRIPTION;
736 
737     if (!strncmp(str, "INPUT", sizeof("INPUT")-1))
738 	return T_INPUT;
739 
740     if (!strncmp(str, "REPEAT", sizeof("REPEAT")-1))
741 	return T_REPEAT;
742 
743     if (!strncmp(str, "OUTPUT", sizeof("OUTPUT")-1))
744 	return T_OUTPUT;
745 
746     if (!strncmp(str, "OPTIONS", sizeof("OPTIONS")-1))
747 	return T_OPTIONS;
748 
749     if (!strncmp(str, "FLAGS", sizeof("FLAGS")-1))
750 	return T_FLAGS;
751 
752     if (!strncmp(str, "OUTBOARD", sizeof("OUTBOARD")-1))
753 	return T_OUTBOARD;
754 
755     if (!strncmp(str, "LOADABLE", sizeof("LOADABLE")-1))
756 	return T_LOADABLE;
757 
758     return T_ERROR;
759 }
760 
IDFlag(char * str)761 static int IDFlag(char *str)
762 {
763     if (!str || str[0] == '\0')
764 	return 0;
765 
766 #if 0
767     if (!strncmp(str, "SERIAL", sizeof("SERIAL")-1))
768 	return MODULE_SERIAL;
769 
770     if (!strncmp(str, "SEQUENCED", sizeof("SEQUENCED")-1))
771 	return MODULE_SEQUENCED;
772 #endif
773 
774     if (!strncmp(str, "PIN", sizeof("PIN")-1))
775 	return MODULE_PIN;
776 
777 #if 0
778     if (!strncmp(str, "ASSIGN", sizeof("ASSIGN")-1))
779 	return MODULE_ASSIGN;
780 #endif
781 
782     if (!strncmp(str, "SIDE_EFFECT", sizeof("SIDE_EFFECT")-1))
783 	return MODULE_SIDE_EFFECT;
784 
785     if (!strncmp(str, "LOOP", sizeof("LOOP")-1))
786         return MODULE_LOOP;
787 
788 #if 0
789     if (!strncmp(str, "JOIN", sizeof("JOIN")-1))
790 	return MODULE_JOIN;
791 #endif
792 
793     if (!strncmp(str, "ERR_CONT", sizeof("ERR_CONT")-1))
794 	return MODULE_ERR_CONT;
795 
796     if (!strncmp(str, "REROUTABLE", sizeof("REROUTABLE")-1))
797 	return MODULE_REROUTABLE;
798 
799     if (!strncmp(str, "REACH", sizeof("REACH")-1))
800 	return MODULE_REACH;
801 
802     if (!strncmp(str, "OUTBOARD", sizeof("OUTBOARD")-1))
803 	return MODULE_OUTBOARD;
804 
805     if (!strncmp(str, "PERSISTENT", sizeof("PERSISTENT")-1))
806 	return MODULE_PERSISTENT;
807 
808     if (!strncmp(str, "ASYNC", sizeof("ASYNC")-1))
809 	return MODULE_ASYNC;
810 
811     if (!strncmp(str, "ASYNCHRONOUS", sizeof("ASYNCHRONOUS")-1))
812 	return MODULE_ASYNC;
813 
814     if (!strncmp(str, "ASYNCLOCAL", sizeof("ASYNCLOCAL")-1))
815 	return MODULE_ASYNCLOCAL;
816 
817     return 0;
818 }
819 
820 /*
821  * parse the string into lines, and call DXAddModuleV for each new
822  * module definition.
823  */
ExParseMDF(char * str)824 static Error ExParseMDF(char *str)
825 {
826     int lineno = 0;
827     int modflag;
828     int repcount;
829     int id;
830     int argtype = -1;
831     char *tempc;
832     char *nextc;
833     struct moddef *mp = NULL;
834 
835 
836     /* start at the first non-comment line in the string */
837     nextc = nextline(str, 0, &lineno);
838 
839     while ((id = IDKeyword(nextc)) != T_NONE) {
840 	switch (id) {
841 	  case T_MODULE:
842 	    /* output previous module definition */
843 	    if (mp) {
844 
845 		if (!callmdf(mp, 1))
846 		    goto error;
847 
848 		delmod(mp);
849 	    }
850 	    /* start new definition */
851 	    mp = newmod();
852 	    if (!mp)
853 		goto error;
854 
855 	    nextc = find_next_token(nextc, 0);
856 	    if (!nextc) {
857 		DXSetError(ERROR_DATA_INVALID,
858 			   "missing module name, line %d", lineno);
859 		goto error;
860 	    }
861 	    mp->m_name = AllocToken(nextc);
862 	    if (!IsGoodIdentifier(mp->m_name)) {
863 		DXSetError(ERROR_DATA_INVALID,
864 			   "module names must start with a letter and contain "
865 			   "only letters and numbers, line %d", lineno);
866 		goto error;
867 	    }
868 
869 #if 0  /* reload == 0, so that's the default */
870 	    /* does it already exist? */
871             if ((node *)_dxf_ExMacroSearch (mp->m_name) != NULL)
872 		mp->m_pflags |= PF_RELOAD;
873 #endif
874 
875 	    break;
876 
877 	  /* get name only; ignore type, default & description. */
878 	  case T_INPUT:
879 	  case T_OUTPUT:
880 	    nextc = find_next_token(nextc, 1);
881 	    if (!nextc) {
882 		DXSetError(ERROR_DATA_INVALID,
883 			   "missing %s parameter name, line %d",
884 			   (id==T_INPUT) ? "input" : "output", lineno);
885 		goto error;
886 	    }
887 
888 	    tempc = AllocVariable(nextc);
889 	    if (!tempc)
890 		goto error;
891 
892 	    if (!addarg(mp, (id==T_INPUT) ? INPUTARG : OUTPUTARG, tempc, NULL))
893 		goto error;
894 
895 	    /* save this for later, if handling repeats */
896 	    argtype = id;
897 	    break;
898 
899 	  case T_ERROR:
900 	    /* could this be more helpful? */
901 	    DXSetError(ERROR_DATA_INVALID,
902 		       "unrecognized keyword on line %d", lineno);
903 	    return ERROR;
904 
905 	  case T_OUTBOARD:
906 	    /* get the exec and host name */
907 	    tempc = find_next(nextc, '"');
908 	    if (!tempc) {
909 		nextc = find_next_token(nextc, 1);
910 		if (!nextc) {
911 		    DXSetError(ERROR_DATA_INVALID,
912 			       "missing outboard execution name, line %d",
913 			       lineno);
914 		    goto error;
915 		}
916 		mp->m_exec = AllocToken(nextc);
917 	    }
918 	    else
919 		mp->m_exec = AllocQuote(++tempc);
920 
921 	    if (!mp->m_exec) {
922 		DXSetError(ERROR_DATA_INVALID,
923 			   "missing outboard execution name, line %d",
924 			   lineno);
925 		goto error;
926 	    }
927 
928 	    tempc = find_next(nextc, ';');
929 	    if (tempc) {
930 		nextc = tempc;
931 		tempc = find_next_token(nextc, 1);
932 		if (tempc) {
933 		    mp->m_host = AllocToken(tempc);
934 		    nextc = tempc;
935 		}
936 	    }
937 
938 	    mp->m_flags |= MODULE_OUTBOARD;
939 	    mp->m_pflags |= PF_OUTBOARD;
940 	    break;
941 
942 	  case T_LOADABLE:
943 	    /* get the executable filename */
944 	    tempc = find_next(nextc, '"');
945 	    if (!tempc) {
946 		nextc = find_next_token(nextc, 1);
947 		if (!nextc) {
948 		    DXSetError(ERROR_DATA_INVALID,
949 			       "missing loadable module filename, line %d",
950 			       lineno);
951 		    goto error;
952 		}
953 		mp->m_loadfile = AllocToken(nextc);
954 	    }
955 	    else
956 		mp->m_loadfile = AllocQuote(++tempc);
957 
958 	    if (!mp->m_loadfile) {
959 		DXSetError(ERROR_DATA_INVALID,
960 			   "missing loadable module filename, line %d",
961 			   lineno);
962 		goto error;
963 	    }
964 
965 	    mp->m_pflags |= PF_LOADABLE;
966 	    break;
967 
968 	  case T_FLAGS:
969 	    tempc = find_next_token(nextc, 0);
970 	    while ((modflag = IDFlag(tempc)) != F_NONE) {
971 		mp->m_flags |= modflag;
972 		nextc = tempc;
973 		tempc = find_next_token(tempc, 0);
974 	    }
975 	    break;
976 
977 	  case T_REPEAT:
978 	    nextc = find_next_token(nextc, 0);
979 	    if (!nextc) {
980 		DXSetError(ERROR_DATA_INVALID,
981 			   "missing repeat count, line %d", lineno);
982 		goto error;
983 	    }
984 	    repcount = atoi(nextc);
985 	    if (argtype == T_INPUT) {
986 		if (repcount <= 0 || repcount > mp->m_nin) {
987 		    DXSetError(ERROR_DATA_INVALID,
988 			       "invalid input repeat count, line %d", lineno);
989 		    goto error;
990 		}
991 		if (!argdup(mp, INPUTARG, repcount)) {
992 		    DXAddMessage("line %d", lineno);
993 		    goto error;
994 		}
995 
996 	    } else if (argtype == T_OUTPUT) {
997 		if (repcount <= 0 || repcount > mp->m_nout) {
998 		    DXSetError(ERROR_DATA_INVALID,
999 			       "invalid output repeat count, line %d", lineno);
1000 		    goto error;
1001 		}
1002 		if (!argdup(mp, OUTPUTARG, repcount)) {
1003 		    DXAddMessage("line %d", lineno);
1004 		    goto error;
1005 		}
1006 
1007 	    } else {
1008 		DXSetError(ERROR_DATA_INVALID,
1009 			   "misplaced REPEAT line, line %d", lineno);
1010 	    }
1011 	    break;
1012 
1013 	  case T_CATEGORY:
1014 	  case T_DESCRIPTION:
1015 	  case T_OPTIONS:
1016 	    /* ignore rest of line */
1017 	    break;
1018 	}
1019 
1020 	nextc = nextline(nextc, 1, &lineno);
1021     }
1022 
1023     /* at end-of-string, finish current definition if there is one */
1024     if (mp) {
1025 	if (!callmdf(mp, 1))
1026 	    goto error;
1027 
1028 	delmod(mp);
1029     }
1030 
1031     return OK;
1032 
1033   error:
1034     if (mp)
1035 	delmod(mp);
1036 
1037     return ERROR;
1038 }
1039 
1040 /* if file, read into memory and then parse the same way a string obj
1041  * would be parsed.
1042  */
DXLoadMDFFile(char * filename)1043 Error DXLoadMDFFile(char *filename)
1044 {
1045     int fd;
1046     int len, rlen;
1047     int rc = ERROR;
1048     char *foundname = NULL;
1049     char *cp = NULL;
1050 
1051     if (_dxf_fileSearch(filename, &foundname, "mdf", "DXMODULES") == ERROR)
1052 	return ERROR;
1053 
1054     fd = open(foundname, O_RDONLY);
1055     if (fd < 0) {
1056 	DXSetError(ERROR_DATA_INVALID, "cannot open %s as MDF file", foundname);
1057 	goto error;
1058     }
1059 
1060     len = lseek(fd, 0, 2);   /* find length of file */
1061     if (len <= 0) {
1062 	DXSetError(ERROR_DATA_INVALID, "error reading from %s", foundname);
1063 	goto error;
1064     }
1065     lseek(fd, 0, 0);
1066 
1067     cp = (char *)DXAllocate(len+1);
1068     if (!cp)
1069 	goto error;
1070 
1071     rlen = read(fd, cp, len);
1072     if (rlen != len) {
1073 	DXSetError(ERROR_DATA_INVALID, "error reading from %s", foundname);
1074 	goto error;
1075     }
1076     cp[len] = '\0';
1077 
1078     close (fd);
1079 
1080     rc = ExParseMDF(cp);
1081 
1082   error:
1083     DXFree((Pointer)cp);
1084     DXFree((Pointer)foundname);
1085     return rc;
1086 }
1087 
DXLoadMDFString(char * cp)1088 Error DXLoadMDFString(char *cp)
1089 {
1090     return ExParseMDF(cp);
1091 }
1092 
1093