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 #include "../base/defines.h"
11 
12 #include <sys/types.h>
13 
14 #if defined(HAVE_ERRNO_H)
15 #include <errno.h>
16 #endif
17 
18 #if defined(HAVE_DIRENT_H)
19 #include <dirent.h>
20 #endif
21 
22 #if defined(HAVE_IO_H)
23 #include <io.h>
24 #endif
25 
26 #if defined(HAVE_SYS_STAT_H)
27 #include <sys/stat.h>
28 #endif
29 
30 #include <stdio.h>
31 
32 #define Ark Ark
33 #include <dx/arch.h>
34 #undef Ark
35 
36 #include "lex.h"
37 #include "MacroDefinition.h"
38 #include "MacroNode.h"
39 #include "ListIterator.h"
40 #include "DXApplication.h"
41 #include "Network.h"
42 #include "ErrorDialogManager.h"
43 #include "WarningDialogManager.h"
44 #include "ParseMDF.h"
45 #include "ParameterDefinition.h"
46 #include "ToolSelector.h"
47 #include "EditorWindow.h"
48 #include "SaveMacroCommand.h"
49 
50 #define OLD_DUMMY_DESCRIPTION_STRING "Generated dummy input"
51 
52 #if HAVE_REGCOMP && HAVE_REGEX_H
53 /* prefer POSIX style regcomp(3) over obsolete versions */
54 extern "C" {
55 #include <regex.h>
56 }
57 #undef HAVE_RE_COMP
58 #undef HAVE_FINDFIRST
59 #elif  defined(HAVE_RE_COMP)
60 #undef HAVE_REGCMP
61 #undef HAVE_REGCOMP
62 #undef HAVE__FINDFIRST
63 extern "C" char *re_comp(char *s);
64 extern "C" int re_exec(char *);
65 #elif defined(HAVE_REGCMP)
66 #undef HAVE_REGCOMP
67 #undef HAVE__FINDFIRST
68 extern "C" char *regcmp(...);
69 extern "C" char *regex(char *, char *, ...);
70 #elif HAVE_REGCOMP && HAVE_REGEXP_H
71 extern "C" {
72 #include <regexp.h>
73 }
74 #undef HAVE_RE_COMP
75 #undef HAVE__FINDFIRST
76 #elif  defined(HAVE_RE_COMP)
77 #undef HAVE__FINDFIRST
78 extern "C" char *re_comp(char *s);
79 extern "C" int re_exec(char *);
80 #endif
81 
MacroDefinition(boolean system)82 MacroDefinition::MacroDefinition(boolean system) :
83     NodeDefinition()
84 {
85     this->systemMacro = system;
86     this->fileName = NULL;
87     this->body = NULL;
88     this->initialRead = FALSE;
89     this->updatingServer = FALSE;
90     this->category = 0;
91     if (!system)
92 	this->saveCmd = new SaveMacroCommand("saveMacroCommand",
93                                          theDXApplication->getCommandScope(),
94                                          TRUE,
95                                          this);
96 }
~MacroDefinition()97 MacroDefinition::~MacroDefinition()
98 {
99     if (this->saveCmd) delete this->saveCmd;
100 
101     if (this->body)
102     {
103 	this->body->setDefinition(NULL);
104 	theDXApplication->macroList.removeElement(this->body);
105 	if (!this->body->isDeleted())
106 	    delete this->body;
107 	this->body = NULL;
108     }
109     if (this->fileName)
110 	delete[] this->fileName;
111 }
112 
finishDefinition()113 void MacroDefinition::finishDefinition()
114 {
115 }
116 
117 
newNode(Network * net,int instance)118 Node *MacroDefinition::newNode(Network *net, int instance)
119 {
120     MacroNode *d = new MacroNode(this, net, instance);
121     return d;
122 }
123 
reference(MacroNode * n)124 void MacroDefinition::reference(MacroNode *n)
125 {
126     ListIterator li(this->referencingNodes);
127     Node *listNode;
128     while ( (listNode = (MacroNode*)li.getNext()) )
129 	if (n == listNode)
130 	    return;
131     this->referencingNodes.appendElement(n);
132 }
133 
dereference(MacroNode * n)134 void MacroDefinition::dereference(MacroNode *n)
135 {
136     ListIterator li(this->referencingNodes);
137     Node *listNode;
138     while ( (listNode = (MacroNode*)li.getNext()) )
139     {
140 	if (n == listNode)
141 	{
142 	    this->referencingNodes.deleteElement(li.getPosition()-1);
143 	    break;
144 	}
145     }
146 //    if (this->referencingNodes.getSize() == 0 && this->body != NULL)
147 //    {
148 //	theDXApplication->macroList.removeElement(this->body);
149 //	delete this->body;
150 //	this->body = NULL;
151 //    }
152 }
153 
printNetworkBody(FILE * f,PrintType ptype)154 boolean MacroDefinition::printNetworkBody(FILE *f, PrintType ptype)
155 {
156     if (!this->loadNetworkBody())
157 	return FALSE;
158     return this->body->printNetwork(f,ptype);
159 }
loadNetworkBody()160 boolean MacroDefinition::loadNetworkBody()
161 {
162     if (this->body == NULL)
163     {
164 	this->body = theDXApplication->newNetwork();
165 	this->body->setDefinition(this);
166 	this->initialRead = TRUE;
167 	boolean r = this->body->readNetwork(this->fileName);
168 	this->initialRead = FALSE;
169         if (!r) {
170             this->body->setDefinition(NULL);
171             Network *n = this->body;
172             this->body = NULL;
173             delete n;
174 	    return FALSE;
175         } else {
176             theDXApplication->macroList.appendElement(this->body);
177         }
178     }
179     return TRUE;
180 }
updateServer()181 boolean MacroDefinition::updateServer()
182 {
183     if (!this->loadNetworkBody())
184 	return FALSE;
185 
186     if (this->body && !this->updatingServer) {
187 	//
188 	// There is a recursive loop between DXExecCtl::updateMacros(),
189 	// Network::sendValues(), MacroNode::sendValues() and
190  	// this->updateServer() that we try and avoid here.
191 	//
192 	this->updatingServer = TRUE;
193 	theDXApplication->getExecCtl()->updateMacros(FALSE);
194 	this->updatingServer = FALSE;
195     }
196     return (this->body != NULL);
197 }
198 
setNodeDefinitions(MacroDefinition * newDef)199 boolean MacroDefinition::setNodeDefinitions(MacroDefinition *newDef)
200 {
201     ListIterator li(this->referencingNodes);
202     MacroNode *n;
203 
204     while ( (n = (MacroNode*)li.getNext()) )
205     {
206 	n->setDefinition(newDef);
207 	n->updateDefinition();
208 	if (newDef != this)
209 	    newDef->reference(n);
210     }
211 
212     return TRUE;
213 }
214 
setFileName(const char * n)215 void MacroDefinition::setFileName(const char *n)
216 {
217     char *fileName = DuplicateString(n);
218     if (this->fileName)
219 	delete[] this->fileName;
220     this->fileName = fileName;
221 }
222 
LoadMacroFile(FILE * f,const char * fileName,boolean replace,boolean * wasMacro,boolean asSystemMacro)223 boolean MacroDefinition::LoadMacroFile(FILE *f,
224 				       const char *fileName,
225 				       boolean replace,
226 				       boolean *wasMacro,
227 					boolean asSystemMacro)
228 {
229 
230     if (theDXApplication->inDebugMode())
231 	printf("read macro from %s\n", fileName);
232 
233     char *p, line[1000];
234     boolean inMDF = FALSE;
235     int lineNo = 0;
236     MacroDefinition *md = NULL;
237     NodeDefinition *nd = NULL;
238     MacroDefinition *oldMd = NULL;
239     SymbolManager  *symbolManager = theNodeDefinitionDictionary->
240 					getSymbolManager();
241     // statefull-ness.  Remember which INPUT was added most recently so
242     // that the OPTIONS keyword can associate with it.  Ensure the
243     // most_recent_param is set to NULL if INPUT isn't followed
244     // by an OPTIONS keyword.
245     ParameterDefinition* most_recent_param = NULL;
246 
247 #define EQUAL_STRING_SECOND_LEN(s1,s2) (EqualSubstring((s1), (s2), STRLEN(s2)))
248     while(fgets(line, sizeof(line), f) == line)
249     {
250 	// Convert possible dos-land carriage returns (ctrl-M) to white space
251 	int len = STRLEN(line);
252 	if (len > 1 && line[len-2] == '\15')
253 	{
254 	    line[len-2] = line[len-1];
255 	    line[len-1] = '\0';
256 	    len--;
257 	}
258 	lineNo++;
259 	if (EQUAL_STRING_SECOND_LEN(line, "// Begin MDF"))
260 	{
261 	    inMDF = TRUE;
262 	}
263 	else if (inMDF && EQUAL_STRING_SECOND_LEN(line, "// End MDF"))
264 	{
265 	    inMDF = FALSE;
266 	    break;
267 	}
268 	else if (!inMDF && EQUAL_STRING_SECOND_LEN(line, "// MODULE"))
269 	{
270 	    if (theDXApplication->inDebugMode())
271 		printf("Macro rejected\n");
272 	    if (wasMacro)
273 		*wasMacro = FALSE;
274 	    goto error;
275 	}
276 	else if (inMDF && EQUAL_STRING_SECOND_LEN(line, "// MODULE"))
277 	{
278 	    if (wasMacro)
279 		*wasMacro = TRUE;
280 
281 	    char name[1000];
282 	    int items_parsed = sscanf(line, "// MODULE %[^\n]", name);
283 	    if (items_parsed != 1)
284 	    {
285 		ErrorMessage("Invalid MODULE comment at line %d of %s.",
286 		    lineNo, fileName);
287 		goto error;
288 	    }
289 
290 	    int index = 0;
291 	    if (!IsRestrictedIdentifier(name, index) ||
292 		(index != STRLEN(name) && !IsWhiteSpace(name, index)))
293 	    {
294 		ErrorMessage("Invalid macro name: %s must start with a letter "
295 			     "and contain only letters and numbers "
296 			     "at line %d of %s.",
297 		    name, lineNo, fileName);
298 		goto error;
299 	    }
300 
301 	    if (EqualString("main", name))
302 	    {
303 		ErrorMessage("The macro name must not be \"main\" "
304 			     "at line %d of %s.", lineNo, fileName);
305 		goto error;
306 	    }
307 
308 	    nd =
309 		(NodeDefinition*)theNodeDefinitionDictionary->
310 		    findDefinition(name);
311 	    if (nd)
312 	    {
313 		if (!nd->isDerivedFromMacro())
314 		{
315 		    ErrorMessage("Standard module \"%s\" cannot be redefined.\n"
316 				 "Macro file \"%s\" not loaded.",
317 			name, fileName);
318 		    goto error;
319 		}
320 		else if (!replace)
321 		{
322 //		    WarningMessage("Macro \"%s\" is already defined.\n"
323 //				   "Macro file \"%s\" not loaded.",
324 //			name, fileName);
325 		    goto error;
326 		}
327 		oldMd = (MacroDefinition*)nd;
328 	    }
329 	    md = new MacroDefinition(asSystemMacro);
330 	    md->setName(name);
331 	    md->setFileName(fileName);
332 	    if (nd)
333 		md->setNextInstance(nd->newInstanceNumber());
334 	}
335 	else if (inMDF && EQUAL_STRING_SECOND_LEN(line, "// CATEGORY"))
336 	{
337 	    char cat[1000];
338 	    int items_parsed = sscanf(line, "// CATEGORY %[^\n]", cat);
339 	    if (items_parsed != 1)
340 	    {
341 		ErrorMessage("Invalid CATEGORY comment at line %d of %s.",
342 		    lineNo, fileName);
343 		goto error;
344 	    }
345 	    md->setCategory(symbolManager->registerSymbol(cat));
346 	}
347 	else if (inMDF && EQUAL_STRING_SECOND_LEN(line, "// DESCRIPTION"))
348 	{
349 	    char desc[1000];
350 	    int items_parsed = sscanf(line, "// DESCRIPTION %[^\n]", desc);
351 	    if (items_parsed != 1)
352 	    {
353 		ErrorMessage("Invalid DESCRIPTION comment at line %d of %s.",
354 		    lineNo, fileName);
355 		goto error;
356 	    }
357 	    md->setDescription(desc);
358 	}
359 	else if (inMDF && EQUAL_STRING_SECOND_LEN(line, "// INPUT"))
360 	{
361 	    char name[1000];
362 	    char types[1000];
363 	    char deflt[1000];
364 	    char descr[1000];
365 	    int items_parsed = sscanf(line,
366 		    "// INPUT %[^;]; %[^;]; %[^;]; %[^\n]",
367 		    name, types, deflt, descr);
368 	    if (items_parsed != 3 && items_parsed != 4)
369 	    {
370 		ErrorMessage("Invalid INPUT comment at line %d of %s.",
371 		    lineNo, fileName);
372 		goto error;
373 	    }
374 
375 	    int index = 0;
376 	    // ... to avoid millions of purify umrs
377 	    int len = STRLEN(name);
378 	    name[len] = name[len+1] = '\0';
379 	    if (!IsIdentifier(name, index) ||
380 		(index != STRLEN(name) && !IsWhiteSpace(name, index) &&
381 			name[index] != '['))
382 	    {
383 		ErrorMessage("Invalid parameter name at line %d of %s.",
384 		    lineNo, fileName);
385 		goto error;
386 	    }
387 	    name[index++] = '\0';
388   	    int visattr = 1;
389 	    if ( (p = strstr(&name[index],"visible:")) ) {
390 		p += strlen("visible:");
391 		visattr = atoi(p);
392 	    }
393 
394 	    ParameterDefinition *pd = new
395 		ParameterDefinition(symbolManager->registerSymbol(name));
396 	    if (items_parsed == 4) {
397 		if (strstr(descr,DUMMY_DESCRIPTION_STRING) ||
398 		    strstr(descr,OLD_DUMMY_DESCRIPTION_STRING))
399 		    pd->setDummy(TRUE);
400 		else
401 		    pd->setDescription(descr);
402 	    }
403 	    switch (visattr) {
404 	  	case 0:	pd->setDefaultVisibility(FALSE); break;
405 	  	case 1:	pd->setDefaultVisibility(TRUE); break;
406 	  	case 2:	pd->setViewability(FALSE); break;
407 	    }
408 	    pd->markAsInput();
409 	    if (!ParseMDFTypes(pd, types, lineNo))
410 	    {
411 		delete pd;
412 		goto error;
413 	    }
414 
415 	    if (*deflt == '(')
416 	    {
417 		pd->setDescriptiveValue(deflt);
418 		if (EqualString(deflt,"(none)"))
419 		    pd->setRequired();
420 	    }
421 	    else if (!pd->setDefaultValue(deflt))
422 	    {
423 		ErrorMessage(
424 		    "Invalid default parameter value at line %d of %s.",
425 		    lineNo, fileName);
426 
427 		delete pd;
428 		pd = NULL;
429 		goto error;
430 	    }
431 	    md->addInput(pd);
432 	    most_recent_param = pd;
433 	}
434 	else if (most_recent_param &&  EQUAL_STRING_SECOND_LEN(line, "// OPTIONS"))
435 	{
436 	    // must convert trailing \n to whitespace because
437 	    // ParseMDFOptions expects it that way.
438 	    int len = STRLEN(line);
439 	    if (line[len-1] == '\n') line[len-1] = '\0';
440 	    if (!ParseMDFOptions (most_recent_param, &line[11])) {
441 		ErrorMessage(
442 		    "Invalid parameter options values at line %d of %s.",
443 		    lineNo, fileName);
444 	    }
445 	}
446 	else if (inMDF && EQUAL_STRING_SECOND_LEN(line, "// OUTPUT"))
447 	{
448 	    char name[1000];
449 	    char types[1000];
450 	    char descr[1000];
451 	    int items_parsed = sscanf(line,
452 		    "// OUTPUT %[^;]; %[^;]; %[^\n]",
453 		    name, types, descr);
454 	    if (items_parsed != 2 && items_parsed != 3)
455 	    {
456 		ErrorMessage("Invalid OUTPUT comment at line %d of %s.",
457 		    lineNo, fileName);
458 		goto error;
459 	    }
460 
461 	    int index = 0;
462 	    // ... to avoid millions of purify umrs
463 	    int len = STRLEN(name);
464 	    name[len] = name[len+1] = '\0';
465 	    if (!IsIdentifier(name, index) ||
466 		(index != STRLEN(name) && !IsWhiteSpace(name, index) &&
467 			name[index] != '['))
468 	    {
469 		ErrorMessage("Invalid parameter name at line %d of %s.",
470 		    lineNo, fileName);
471 		goto error;
472 	    }
473 
474 	    name[index++]='\0';
475   	    int visattr = 1;
476 	    if ( (p = strstr(&name[index],"visible:")) ) {
477 		p += strlen("visible:");
478 		visattr = atoi(p);
479 	    }
480 
481 	    ParameterDefinition *pd = new
482 		ParameterDefinition(symbolManager->registerSymbol(name));
483 	    if (items_parsed == 3) {
484 		if (strstr(descr,DUMMY_DESCRIPTION_STRING) ||
485 		    strstr(descr,OLD_DUMMY_DESCRIPTION_STRING))
486 		    pd->setDummy(TRUE);
487 		else
488 		    pd->setDescription(descr);
489 	    }
490 	    switch (visattr) {
491 	  	case 0:	pd->setDefaultVisibility(FALSE); break;
492 	  	case 1:	pd->setDefaultVisibility(TRUE); break;
493 	  	case 2:	pd->setViewability(FALSE); break;
494 	    }
495 	    pd->markAsOutput();
496 	    if (!ParseMDFTypes(pd, types, lineNo))
497 	    {
498 		delete pd;
499 		goto error;
500 	    }
501 
502 	    md->addOutput(pd);
503 	}
504 	else if (inMDF)
505 	{
506 	    WarningMessage("Encountered unrecognized MDF line "
507 			   "at line %d of `%s', ignored.",
508 		lineNo, fileName);
509 	}
510 	if (!EQUAL_STRING_SECOND_LEN(line, "// INPUT")) {
511 	    most_recent_param = NULL;
512 	}
513     }
514 
515     if (!md)
516 	goto error;
517 
518     md->completeDefinition();
519 
520     if (!asSystemMacro) {
521 	if (oldMd)
522 	{
523 	    oldMd->setNodeDefinitions(md);
524 	    ToolSelector::RemoveTool(oldMd->getCategorySymbol(),
525 				     oldMd->getNameSymbol());
526 	    //
527 	    // Check if the macro is changed.
528 	    //
529 	    if (oldMd->body &&oldMd->saveCmd &&
530 		oldMd->body->saveToFileRequired())
531 	    {
532 		((SaveMacroCommand*)oldMd->saveCmd)->setNext(NULL);
533 		oldMd->saveCmd->execute();
534 	    }
535 	    else
536 		delete oldMd;
537 	}
538     }
539 
540     theNodeDefinitionDictionary->replaceDefinition(md->getNameString(), md);
541 
542     if (!asSystemMacro) {
543 	ToolSelector::AddTool(md->getCategorySymbol(),
544 			      md->getNameSymbol(),
545 			      (void *)md);
546 	ToolSelector::UpdateCategoryListWidget();
547     }
548 
549 
550     if (theDXApplication->inDebugMode())
551 	printf("Macro %s accepted\n", md->getNameString());
552 
553     return TRUE;
554 
555 error:
556     if (md)
557 	delete md;
558     return FALSE;
559 }
560 
561 //
562 // Load all .net files in the given directory that are macros.
563 // If replace is TRUE, then replace any current definitions with the
564 // new one, otherwise ignore the .net file.
565 // If errmsg is not NULL and an error occurs then, no error messages are
566 // posted, and instead a string buffer is allocated to hold the error
567 // message that would have been posted and returned.  The returned
568 // string must be freed by the caller.
569 //
LoadMacroDirectories(const char * path,boolean replace,char ** errmsg,boolean asSystemMacro)570 boolean MacroDefinition::LoadMacroDirectories(const char *path,
571 					boolean replace, char **errmsg,
572 					boolean asSystemMacro)
573 {
574    boolean wasEncoded;
575    boolean return_code = TRUE;
576 
577     if (path == NULL)
578 	return TRUE;
579 
580 #ifndef DXD_NON_UNIX_ENV_SEPARATOR
581 #define SEP_CHAR ':'
582 #else
583 #define SEP_CHAR ';'
584 #endif
585 
586     char *originalString = DuplicateString(path);
587     char *sptr = originalString;
588 
589     if (errmsg)
590 	*errmsg = NULL;
591 
592     while(sptr)
593     {
594 	char *nsptr = sptr;
595 	char *sep = strchr(nsptr, SEP_CHAR);
596 	if (sep)
597 	{
598 	    *sep = '\0';
599 	    sptr = sep + 1;
600 	}
601 	else
602 	{
603 	    sptr = NULL;
604 	}
605 
606 #if defined(HAVE_OPENDIR) && defined(HAVE_DIRENT_H)
607         DIR* d = opendir(nsptr);
608 	if (!d)
609 #elif defined(HAVE_SYS_STAT_H)
610 	struct STATSTRUCT b;
611 	int d = STATFUNC(nsptr, &b);
612 	if (d == -1)
613 #else
614 	No directory tools?
615 #endif
616 
617 	{
618  	    char *errtxt = "Failed opening directory %s: %s";
619 	    if (errmsg) {
620 		int size = STRLEN(errtxt) + STRLEN(nsptr) + 256;
621 		char *p;
622 		if (!*errmsg) {
623 		    *errmsg = (char*)MALLOC(size);
624 		    p = *errmsg;
625 		} else {
626 		    int errmsg_size = STRLEN(*errmsg);
627 		    *errmsg = (char*)REALLOC(*errmsg,errmsg_size + size + 2);
628 		    p = *errmsg + errmsg_size;
629 		    *p = '\n';
630 		    p++;
631 		}
632 		sprintf(p,errtxt,nsptr, strerror(errno));
633 	    } else {
634 		ErrorMessage(errtxt, nsptr, strerror(errno));
635 	    }
636 	    return_code = FALSE;
637 	}
638 	else
639 	{
640 #if defined(HAVE_REGCOMP) && defined(HAVE_REGEX_H)
641 
642 	    regex_t net_file;
643 	    ASSERT(regcomp(&net_file, ".[.]*\\.net$", REG_NOSUB) == 0);
644 
645 	    struct dirent *entry;
646 	    while ( (entry = readdir(d)) )
647 	    {
648 	        boolean exists = regexec(&net_file, entry->d_name, 0, NULL, 0);
649 		if (exists == 0)
650 
651 #elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H)
652 
653 	    char *net_file = (char *)regcomp(".[.]*\\.net$");
654 	    ASSERT(net_file != NULL);
655 
656 	    struct dirent *entry;
657 	    while (entry = readdir(d))
658 	    {
659 		boolean exists = regexec((regexp *)net_file, entry->d_name);
660 		if (exists)
661 
662 #elif defined(HAVE_REGCMP)
663 
664 	    char *net_file = regcmp(".[.]*\\.net$", NULL);
665 	    ASSERT(net_file != NULL);
666 
667 	    struct dirent *entry;
668 	    while (entry = readdir(d))
669 	    {
670 		boolean exists = regex(net_file, entry->d_name) != NULL;
671 		if (exists)
672 
673 #elif defined(HAVE_RE_COMP)
674 
675 	    char *net_file = re_comp(".[.]*\\.net$");
676 	    ASSERT(net_file == NULL);
677 
678 	    struct dirent *entry;
679 	    while (entry = readdir(d))
680 	    {
681 		boolean exists = re_exec(entry->d_name) > 0;
682 		if (exists)
683 
684 #elif defined(HAVE__FINDFIRST)
685 
686 	    char *srch_string2 = new char[STRLEN(nsptr) + 10];
687 	    strcpy(srch_string2,nsptr);
688 	    if (strlen(srch_string2)>0) {
689 		char c = srch_string2[strlen(srch_string2)-1];
690 		if (c != '/' && c != '\\' && c != ':')
691 		    strcat(srch_string2, "/");
692 	    }
693 	    strcat(srch_string2, "*.net");
694 
695             struct _finddata_t entry;
696             long handle = _findfirst(srch_string2,&entry);
697             int exists = (handle == -1L) ? -1: 0;
698 	    while (exists == 0)
699 #endif
700 	    {
701 		    char path[1000];
702 		    strcpy(path, nsptr);
703 		    strcat(path, "/");
704 
705 #if defined(HAVE__FINDFIRST)
706                     strcat(path, entry.name);
707 #else
708                     strcat(path, entry->d_name);
709 #endif
710 
711 		    char *ignore = NULL;
712 		    FILE *f = Network::OpenNetworkFILE(path, &wasEncoded, &ignore);
713 		    if (ignore) delete[] ignore;
714 		    if (f == NULL)
715 		    {
716 			char *errtxt = "Failed to load macro file %s: %s";
717 			if (errmsg) {
718 			    int size = STRLEN(errtxt) + STRLEN(path) + 256;
719 			    char *p;
720 			    if (!*errmsg) {
721 				*errmsg = (char*)MALLOC(size);
722 				p = *errmsg;
723 			    } else {
724 				int errmsg_size = STRLEN(*errmsg);
725 				*errmsg = (char*)REALLOC(*errmsg,
726 							errmsg_size + size + 2);
727 				p = *errmsg + errmsg_size;
728 				*p = '\n';
729 				p++;
730 			    }
731 			    sprintf(p,errtxt,path, strerror(errno));
732 			} else {
733 			    ErrorMessage(errtxt, path, strerror(errno));
734 	    		}
735 			return_code = FALSE;
736 		    }
737 		    else
738 		    {
739 			MacroDefinition::LoadMacroFile(f, path,
740 					replace, NULL, asSystemMacro);
741 			Network::CloseNetworkFILE(f, wasEncoded);
742 		    }
743 #if defined(HAVE__FINDFIRST)
744                     exists=_findnext(handle,&entry);
745 	    }
746             _findclose(handle);
747 	    delete[] srch_string2;
748 #elif defined(HAVE_REGCOMP) && defined(HAVE_REGEX_H)
749 		}
750 	    }
751 	    closedir(d);
752 	    regfree(&net_file);
753 #elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H)
754 		}
755 	    }
756 	    closedir(d);
757 #elif defined(HAVE_RE_COMP)
758 		}
759 	    }
760 	    closedir(d);
761 #elif defined(HAVE_REGCMP)
762 		}
763 	    }
764 	    closedir(d);
765 #elif defined(HAVE_REGCMP)
766 		}
767 	    }
768 	    free(net_file);
769 	    closedir(d);
770 #endif
771 	}
772     }
773 
774     delete[] originalString;
775     return return_code;
776 }
777 
778 //
779 // If errmsg is not NULL and an error occurs then, no error messages are
780 // posted, and instead a string buffer is allocated to hold the error
781 // message that would have been posted and returned.  The returned
782 // string must be freed by the caller.
783 //
784 boolean MacroDefinition::LoadMacro(const char *fileName, char **errmsg,
785 					boolean asSystemMacro)
786 {
787     char *netFile = Network::FilenameToNetname(fileName);
788     boolean return_code = TRUE;
789     boolean wasEncoded;
790 
791     if (errmsg)
792 	*errmsg = NULL;
793 
794     FILE *f = Network::OpenNetworkFILE(netFile, &wasEncoded, errmsg);
795 
796 
797     if (f == NULL) {
798 	return_code = FALSE;
799     } else {
800 	boolean wasMacro;
801 	MacroDefinition::LoadMacroFile(f, netFile, TRUE,
802 						&wasMacro, asSystemMacro);
803 	Network::CloseNetworkFILE(f, wasEncoded);
804 	if (!wasMacro) {
805 	    char *errtxt = "File %s doesn't contain a macro and was not loaded";
806 	    if (errmsg) {
807 		*errmsg = new char[STRLEN(errtxt) + STRLEN(netFile)];
808 		sprintf(*errmsg,errtxt,netFile);
809 	    } else {
810 		ErrorMessage(errtxt, netFile);
811 	    }
812 	    return_code = FALSE;
813 	}
814     }
815     delete netFile;
816 
817     return return_code;
818 }
819 
820 void MacroDefinition::openMacro()
821 {
822     if (this->updateServer()) {
823 	EditorWindow *e = this->body->getEditor();
824 	if (e == NULL)
825 	    e = theDXApplication->newNetworkEditor(this->body);
826 	if (e) {
827 	    e->manage();
828 	    XMapRaised(XtDisplay(e->getRootWidget()),
829 			XtWindow(e->getRootWidget()));
830 	}
831     }
832 }
833 
834 boolean MacroDefinition::removeIODef(List *l, ParameterDefinition *pd)
835 {
836     boolean result = l->removeElement((void*)pd);
837     if (!this->initialRead)
838 	this->setNodeDefinitions(this);
839     return result;
840 }
841 boolean MacroDefinition::addIODef(List *l, ParameterDefinition *pd)
842 {
843     boolean result = TRUE;
844     if (!this->initialRead) {
845         result = this->NodeDefinition::addIODef(l, pd) &&
846 		 this->setNodeDefinitions(this);
847     }
848     return result;
849 }
850 boolean MacroDefinition::replaceIODef(List *l,
851 				      ParameterDefinition *newPd,
852 				      ParameterDefinition *pd)
853 {
854     int position = l->getPosition((void*)pd);
855     if (position == 0)
856 	return FALSE;
857     boolean result = l->removeElement((void*)pd);
858     if (result)
859     {
860 	result = l->insertElement((void*)newPd, position);
861     }
862 
863     int  i;
864     //
865     // Remove trailing DUMMY parameters.
866     //
867     if (result) {
868 	for (i = l->getSize(); (i > 0); i--)
869 	{
870 	    pd = (ParameterDefinition*)l->getElement(i);
871 	    if (pd->isDummy()) {
872 		if (l->removeElement((void*)pd))
873 		    delete pd;
874 	    } else
875 		break;
876 	}
877     }
878     if (!this->initialRead)
879 	this->setNodeDefinitions(this);
880 
881     return result;
882 }
883 
884 //
885 // Get the Nth input that is not a dummy parameter definition.
886 //
887 ParameterDefinition *MacroDefinition::getNonDummyIODefinition(List *l, int n)
888 {
889    ASSERT(n > 0);
890    ASSERT(l);
891 
892    ParameterDefinition *pd=NULL;
893    ListIterator iterator(*l);
894    int count = 0;
895 
896    while ((count != n) &&
897 	  (pd = (ParameterDefinition*)iterator.getNext())) {
898 	if (!pd->isDummy())
899 	    count++;
900    }
901    if (count != n)
902 	pd = NULL;
903 
904    return pd;
905 }
906 //
907 // Find the first available spot to place a new parameter in the given
908 // list (expected to be either inputDefs or outputDefs). If there are dummy
909 // parameters in the list, then the index of the first dummy is returned.
910 // If no dummies, then N+1 is returned, where N is the current number of
911 // items in the list.
912 //
913 int MacroDefinition::getFirstAvailableIOPosition(List *l)
914 {
915    ASSERT(l);
916 
917    ParameterDefinition *pd;
918    ListIterator iterator(*l);
919    int n = 0;
920    boolean found_dummy = FALSE;
921 
922    while ((pd = (ParameterDefinition*)iterator.getNext())) {
923 	n++;
924 	if (pd->isDummy()) {
925 	   found_dummy = TRUE;
926 	   break;
927 	}
928    }
929    if (!found_dummy)
930 	n++;
931 
932    ASSERT(n>0);
933    return n;
934 }
935 
936 boolean MacroDefinition::setNetwork(Network *net)
937 {
938     this->body = net;
939     this->initialRead = FALSE;
940 
941     return TRUE;
942 }
943 
944