earlybrowserreborn - Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 roytam 1  
2 /*
3  *  These are the WWW - Midas Interface routines
4  */
5  
6 #include <signal.h>
7 #define EF 23
8 #define WWWMASTER
9 #include "midasnet.h"
10 #include "SGMLHyper.h"
11 #include "SGMLCompositeText.h"
12 #include "SGMLListText.h"
13 #include "SGMLAnchorText.h"
14 #include "SGMLContainerText.h"
15 #include "SGMLMarkerText.h"
16 #include "SGMLFormattedText.h"
17 #include "SGMLPlainText.h"
18 #include "GhostviewPlus.h"
19 #include "ps.h"
20  
21 #include <Xm/MessageB.h>
22 #include <X11/Xatom.h>
23 #include <string.h>
24  
25 #define HASHSIZE 337
26 #define DEADNODE ((WWWNode *) -1)
27  
28 static WWWNode *HashTable[HASHSIZE];
29 static WWWFile *WWWGateway;
30 static WWWNode *WWWToLink = NULL;
31 WWWNode *WWWFromLink = DEADNODE;
32 static Pixmap circlePixmap;
33  
34 /* Ugly static variable that keeps track of where we are in processing a document */
35  
36 static int WWWnextid = 0;
37 static WWWNode *WWWlastnode;
38 static WWWNode *WWWparent;
39 static GC messageGC;
40  
41 static XrmQuark localProtocol;
42 static XrmQuark httpProtocol;
43 static XrmQuark gopherProtocol;
44 static XrmQuark telnetProtocol;
45 static XrmQuark ftpProtocol;
46 static XrmQuark fileProtocol;
47 static XrmQuark historyProtocol;
48 static XrmQuark ingotProtocol;
49 static XrmQuark waisProtocol;
50  
51 static XrmQuark getMethod;
52 static XrmQuark postMethod;
53  
54 XrmQuark htmlDestination;
55 XrmQuark textDestination;
56 XrmQuark psDestination;
57 XrmQuark gifDestination;
58 XrmQuark xbmDestination;
59 XrmQuark fileDestination;
60 XrmQuark multimediaDestination;
61 XrmQuark unsupportDestination;
62  
63 extern XtAppContext appl_context;
64  
65 static List *cleanUp = NULL;  
66 static List *cleanUpTempfile = NULL;
67 static WWWNode *FirstVisitedNode = NULL;
68 static WWWNode *historyNode;
69 static WWWNode *YouAreHereNode = NULL;
70  
71 char password[64];
72  
73 extern Widget WWWFetchDocumentHTTP1();
74 extern Widget WWWPostDocumentHTTP1();
75 extern Widget WWWFetchDocumentHTTP();
76 extern Widget WWWFetchDocumentGOPHER();
77 extern Widget WWWFetchDocumentFTP();
78  
79 /*
80  * Application resources
81  *
82  */
83  
84 #define WWWINHERIT_FONT    NULL
85 #define WWWINHERIT_UNDERLINE -999
86 #define WWWINHERIT_OUTLINE 2
87 #define WWWINHERIT_UNDERLINEHEIGHT 999
88 #define WWWINHERIT_UNDERLINESTYLE 999
89 #define WWWINHERIT_COLOR NULL  
90 #define WWWINHERIT_QUARK NULL
91 #define WWWINHERIT_SIZE 0
92  
93 #define WWWNvisitedFontSize        "visitedFontSize"
94 #define WWWNvisitedFontSpacing     "visitedFontSpacing"
95 #define WWWNvisitedFontWidth       "visitedFontWidth"
96 #define WWWNvisitedFontSlant       "visitedFontSlant"
97 #define WWWNvisitedFontWeight      "visitedFontWeight"
98 #define WWWNvisitedFontRegistry    "visitedFontRegistry"
99 #define WWWNvisitedFontFamily      "visitedFontFamily"
100  
101 #define WWWNvisitedColor           "visitedColor"
102 #define WWWNvisitedUnderline       "visitedUnderline"
103 #define WWWNvisitedOutline         "visitedOutline"
104 #define WWWNvisitedUnderlineHeight "visitedUnderlineHeight"
105 #define WWWNvisitedUnderlineStyle  "visitedUnderlineStyle"
106  
107 #define WWWNsensitiveFontSize        "sensitiveFontSize"
108 #define WWWNsensitiveFontSpacing     "sensitiveFontSpacing"
109 #define WWWNsensitiveFontWidth       "sensitiveFontWidth"
110 #define WWWNsensitiveFontSlant       "sensitiveFontSlant"
111 #define WWWNsensitiveFontWeight      "sensitiveFontWeight"
112 #define WWWNsensitiveFontRegistry    "sensitiveFontRegistry"
113 #define WWWNsensitiveFontFamily      "sensitiveFontFamily"
114  
115 #define WWWNsensitiveColor           "sensitiveColor"
116 #define WWWNsensitiveUnderline       "sensitiveUnderline"
117 #define WWWNsensitiveOutline         "sensitiveOutline"
118 #define WWWNsensitiveUnderlineHeight "sensitiveUnderlineHeight"
119 #define WWWNsensitiveUnderlineStyle  "sensitiveUnderlineStyle"
120  
121 #define WWWNwaitingTimeOut         "waitingTimeOut"
122 #define WWWNupdateTimeOut          "updateTimeOut"
123 #define WWWCTimeOut                "Timeout"                           
124  
125 #define WWWNdefaultHTTPPort        "defaultHTTPPort"
126 #define WWWNdefaultHTTPProtocol    "defaultHTTPProtocol"
127 #define WWWNdefaultGopherPort      "defaultGopherPort"
128 #define WWWNdefaultFTPPort         "defaultFTPPort"
129 #define WWWCHTTPProtocol           "Protocol"
130 #define WWWCPort                   "Port"
131 #define WWWNhelp                   "help"
132 #define WWWCHelp                   "Help"  
133  
134 #define WWWNsignpostName           "signpostName"
135 #define WWWCSignpostName           "SignpostName"
136  
137 #define WWWNwaisGatewayName        "waisGatewayName"
138 #define WWWCWaisGatewayName        "WaisGatewayName"
139 #define WWWNwaisGatewayPort        "waisGatewayPort"
140 #define WWWCWaisGatewayPort        "WaisGatewayPort"
141  
142 #define WWWNokPorts                "okPorts"
143 #define WWWCOkPorts                "OkPorts"
144  
145 #define WWWNfiletypes              "filetypes"
146 #define WWWCFiletypes              "Filetypes"         
147  
148 #define WWWNmimetypes              "mimetypes"
149 #define WWWCMimetypes              "Mimetypes"
150  
151 #define WWWNmimeencode             "mimeencode"
152 #define WWWCMimeencode             "Mimeencode"
153  
154 #define WWWNusertypes              "usertypes"
155 #define WWWCUsertypes              "Usertypes"
156  
157 #define WWWNuserMimetypes          "userMimetypes"
158 #define WWWCUserMimetypes          "UserMimetypes"
159  
160 #define WWWNuserMimeencode         "userMimeencode"
161 #define WWWCUserMimeencode         "UserMimeencode"
162  
163 #define WWWNforeground             "foreground"
164 #define WWWCForeground             "Foreground"
165  
166 #define WWWNbackground             "background"
167 #define WWWCBackground             "Background"
168  
169 #define WWWNfont                   "font"
170 #define WWWCFont                   "Font"
171  
172 #define Offset(field) XtOffsetOf(WWWResources,field)
173  
174 static XtResource resources[] = {
175  
176     {WWWNvisitedFontFamily, SGMLCFontFamily, SGMLRQuark, sizeof(XrmQuark),
177     Offset (visited_rendition.family), XtRImmediate, SGMLINHERIT_QUARK},
178  
179     {WWWNvisitedFontWeight, SGMLCFontWeight, SGMLRQuark, sizeof(XrmQuark),
180     Offset (visited_rendition.weight), XtRImmediate, SGMLINHERIT_QUARK},
181  
182     {WWWNvisitedFontSlant, SGMLCFontSlant, SGMLRQuark, sizeof(XrmQuark),
183     Offset (visited_rendition.slant), XtRImmediate, SGMLINHERIT_QUARK},
184  
185     {WWWNvisitedFontWidth, SGMLCFontWidth, SGMLRQuark, sizeof(XrmQuark),
186     Offset (visited_rendition.width), XtRImmediate, SGMLINHERIT_QUARK},
187  
188     {WWWNvisitedFontSpacing, SGMLCFontSpacing, SGMLRQuark, sizeof(XrmQuark),
189     Offset (visited_rendition.spacing), XtRImmediate, SGMLINHERIT_QUARK},
190  
191     {WWWNvisitedFontRegistry, SGMLCFontRegistry, SGMLRQuark, sizeof(XrmQuark),
192     Offset (visited_rendition.registry), XtRImmediate, SGMLINHERIT_QUARK},
193  
194     {WWWNvisitedFontSize, SGMLCFontSize, XtRInt, sizeof(int),
195     Offset (visited_rendition.size), XtRImmediate, SGMLINHERIT_SIZE},
196  
197     {WWWNvisitedColor, SGMLCColor, XtRPixel, sizeof (Pixel),
198     Offset(visited_rendition.color),XtRString, WWWINHERIT_COLOR},
199  
200     {WWWNvisitedOutline,SGMLCOutline,XtRBoolean,sizeof(Boolean),
201     Offset(visited_rendition.outline),XtRImmediate,(XtPointer) WWWINHERIT_OUTLINE},
202  
203     {WWWNvisitedUnderline,SGMLCUnderline,XtRInt,sizeof(int),
204     Offset(visited_rendition.underline),XtRImmediate,(XtPointer) WWWINHERIT_UNDERLINE},
205  
206     {WWWNvisitedUnderlineHeight, SGMLCUnderlineHeight, XtRDimension, sizeof(Dimension),
207     Offset(visited_rendition.underline_height),XtRImmediate,(XtPointer) WWWINHERIT_UNDERLINEHEIGHT},
208  
209     {WWWNvisitedUnderlineStyle, SGMLCUnderlineStyle, SGMLRLineStyle, sizeof(int),
210     Offset(visited_rendition.underline_style),XtRImmediate,(XtPointer) WWWINHERIT_UNDERLINESTYLE},
211  
212     {WWWNsensitiveFontFamily, SGMLCFontFamily, SGMLRQuark, sizeof(XrmQuark),
213     Offset (sensitive_rendition.family), XtRImmediate, SGMLINHERIT_QUARK},
214  
215     {WWWNsensitiveFontWeight, SGMLCFontWeight, SGMLRQuark, sizeof(XrmQuark),
216     Offset (sensitive_rendition.weight), XtRImmediate, SGMLINHERIT_QUARK},
217  
218     {WWWNsensitiveFontSlant, SGMLCFontSlant, SGMLRQuark, sizeof(XrmQuark),
219     Offset (sensitive_rendition.slant), XtRImmediate, SGMLINHERIT_QUARK},
220  
221     {WWWNsensitiveFontWidth, SGMLCFontWidth, SGMLRQuark, sizeof(XrmQuark),
222     Offset (sensitive_rendition.width), XtRImmediate, SGMLINHERIT_QUARK},
223  
224     {WWWNsensitiveFontSpacing, SGMLCFontSpacing, SGMLRQuark, sizeof(XrmQuark),
225     Offset (sensitive_rendition.spacing), XtRImmediate, SGMLINHERIT_QUARK},
226  
227     {WWWNsensitiveFontRegistry, SGMLCFontRegistry, SGMLRQuark, sizeof(XrmQuark),
228     Offset (sensitive_rendition.registry), XtRImmediate, SGMLINHERIT_QUARK},
229  
230     {WWWNsensitiveFontSize, SGMLCFontSize, XtRInt, sizeof(int),
231     Offset (sensitive_rendition.size), XtRImmediate, SGMLINHERIT_SIZE},
232  
233     {WWWNsensitiveColor, SGMLCColor, XtRPixel, sizeof (Pixel),
234     Offset(sensitive_rendition.color),XtRString, WWWINHERIT_COLOR},
235  
236     {WWWNsensitiveOutline,SGMLCOutline,XtRBoolean,sizeof(Boolean),
237     Offset(sensitive_rendition.outline),XtRImmediate,(XtPointer) WWWINHERIT_OUTLINE},
238  
239     {WWWNsensitiveUnderline,SGMLCUnderline,XtRInt,sizeof(int),
240     Offset(sensitive_rendition.underline),XtRImmediate,(XtPointer) WWWINHERIT_UNDERLINE},
241  
242     {WWWNsensitiveUnderlineHeight, SGMLCUnderlineHeight, XtRDimension, sizeof(Dimension),
243     Offset(sensitive_rendition.underline_height),XtRImmediate,(XtPointer) WWWINHERIT_UNDERLINEHEIGHT},
244  
245     {WWWNsensitiveUnderlineStyle, SGMLCUnderlineStyle, SGMLRLineStyle, sizeof(int),
246     Offset(sensitive_rendition.underline_style),XtRImmediate,(XtPointer) WWWINHERIT_UNDERLINESTYLE},
247  
248     {WWWNdefaultHTTPPort, WWWCPort, XtRInt, sizeof(int),
249     Offset(default_HTTP_port),XtRImmediate,(XtPointer) 80},
250  
251     {WWWNdefaultHTTPProtocol, WWWCHTTPProtocol, XtRString, sizeof(char *),
252     Offset(default_HTTP_protocol),XtRString,(XtPointer) "0.9"},
253  
254     {WWWNdefaultGopherPort, WWWCPort, XtRInt, sizeof(int),
255     Offset(default_Gopher_port),XtRImmediate,(XtPointer) 70},
256  
257     {WWWNdefaultFTPPort, WWWCPort, XtRInt, sizeof(int),
258     Offset(default_FTP_port),XtRImmediate,(XtPointer) 21},
259  
260     {WWWNokPorts, WWWCOkPorts, "List", sizeof(List *),
261     Offset(ok_ports),XtRString,(XtPointer) "21 70 71 80 81 82 83 84 85 79 13 43"},
262  
263     {WWWNhelp, WWWCHelp, XtRString, sizeof(char *),
264     Offset(help),XtRString,(XtPointer) "http://slacvx.slac.stanford.edu:80/midasv20/"},
265  
266     {WWWNwaitingTimeOut, WWWCTimeOut, XtRInt, sizeof(int),
267     Offset(waiting_time_out),XtRImmediate,(XtPointer) 2000},
268  
269     {WWWNupdateTimeOut, WWWCTimeOut, XtRInt, sizeof(int),
270     Offset(update_time_out),XtRImmediate,(XtPointer) 200},
271  
272     {WWWNfiletypes, WWWCFiletypes, "List", sizeof(List *),
273     Offset(filetypes),XtRImmediate,(XtPointer) NULL},  
274  
275     {WWWNmimetypes, WWWCMimetypes, "List", sizeof(List *),
276     Offset(mimetypes),XtRImmediate,(XtPointer) NULL},  
277  
278     {WWWNmimeencode, WWWCMimeencode, "List", sizeof(List *),
279     Offset(mimeencode),XtRImmediate,(XtPointer) NULL},  
280  
281     {WWWNusertypes, WWWCUsertypes, "List", sizeof(List *),
282     Offset(usertypes),XtRImmediate,(XtPointer) NULL},  
283  
284     {WWWNuserMimetypes, WWWCUserMimetypes, "List", sizeof(List *),
285     Offset(usermimetypes),XtRImmediate,(XtPointer) NULL},
286  
287     {WWWNuserMimeencode, WWWCUserMimeencode, "List", sizeof(List *),
288     Offset(usermimeencode),XtRImmediate,(XtPointer) NULL},
289  
290     {WWWNsignpostName, WWWCSignpostName, XtRAtom, sizeof(Atom),
291     Offset(signpost),XtRString,(XtPointer) "midaswww"},  
292  
293     {WWWNwaisGatewayName, WWWCWaisGatewayName, SGMLRQuark, sizeof(XrmQuark),
294     Offset(wais_gateway_node),XtRString,(XtPointer) "info.cern.ch"},  
295  
296     {WWWNwaisGatewayPort, WWWCWaisGatewayPort, XtRInt, sizeof(int),
297     Offset(wais_gateway_port),XtRImmediate,(XtPointer) 8001},      
298  
299     {WWWNforeground, WWWCForeground, XtRPixel, sizeof(Pixel),
300     Offset(foreground),XtRImmediate,(XtPointer) 0},      
301  
302     {WWWNbackground, WWWCBackground, XtRPixel, sizeof(Pixel),
303     Offset(background),XtRImmediate,(XtPointer) 1},        
304  
305     {WWWNfont, WWWCFont, XtRFontStruct, sizeof(XFontStruct *),
306     Offset(font),XtRString,(XtPointer) "fixed"},
307 };    
308  
309 #undef Offset
310  
311 static WWWFile *ParseFile();
312  
313 /*
314  * Calculates a hash id from a WWWFile
315  * -----------------------------------
316  */
317 static int WWWHash(file)
318 WWWFile *file;
319 {
320    return ((int) file->protocol +
321            (int) file->node     +
322            (int) file->file     +
323            (int) file->anchor   +
324            file->port           ) % HASHSIZE;
325 }
326 char *WWWAsciiFile(pfile)
327 WWWFile *pfile;
328 {
329   char *buffer, *p;
330  
331   int l = 10 + strlen(XrmQuarkToString(pfile->protocol));
332   if (pfile->file)   l += strlen(XrmQuarkToString(pfile->file));
333   if (pfile->node)   l += strlen(XrmQuarkToString(pfile->node));
334   if (pfile->port)   l += 6;
335   if (pfile->anchor) l += strlen(XrmQuarkToString(pfile->anchor));
336  
337   p = buffer = XtMalloc(l);
338  
339   sprintf(buffer,"%s:",XrmQuarkToString(pfile->protocol));
340   buffer += strlen(buffer);
341  
342   if (pfile->node)
343     {
344       sprintf(buffer,"//%s",XrmQuarkToString(pfile->node));
345       buffer += strlen(buffer);
346     }  
347  
348   if (pfile->port)
349     {  
350       sprintf(buffer,":%d",pfile->port);
351       buffer += strlen(buffer);
352     }
353  
354   if (pfile->file)
355     {    
356       sprintf(buffer,"%s",XrmQuarkToString(pfile->file));
357       buffer += strlen(buffer);
358     }
359  
360   if (pfile->anchor)
361     {
362       sprintf(buffer,"#%s",XrmQuarkToString(pfile->anchor));
363       buffer += strlen(buffer);
364     }
365   return p;
366 }
367  
368 /*
369  * Called to mark an anchor as visited or sensitive
370  */
371 static void WWWBeenThere(w,visited)
372 Widget w;
373 Boolean visited;
374 {
375   Arg arglist[20];
376   int n=0;
377   SGMLRendition *rendition;
378  
379   if (visited) rendition = &appResources.visited_rendition;
380   else         rendition = &appResources.sensitive_rendition;
381  
382   if (rendition->color != WWWINHERIT_COLOR)
383   {
384     XtSetArg(arglist[n],SGMLNcolor,rendition->color); n++;
385   }
386   if (rendition->family != WWWINHERIT_QUARK)
387   {
388     XtSetArg(arglist[n],SGMLNfontFamily,rendition->family); n++;
389   }
390    if (rendition->slant != WWWINHERIT_QUARK)
391   {
392     XtSetArg(arglist[n],SGMLNfontSlant,rendition->slant); n++;
393   }
394    if (rendition->width != WWWINHERIT_QUARK)
395   {
396     XtSetArg(arglist[n],SGMLNfontWidth,rendition->width); n++;
397   }
398    if (rendition->registry != WWWINHERIT_QUARK)
399   {
400     XtSetArg(arglist[n],SGMLNfontRegistry,rendition->registry); n++;
401   }
402    if (rendition->weight != WWWINHERIT_QUARK)
403   {
404     XtSetArg(arglist[n],SGMLNfontWeight,rendition->weight); n++;
405   }
406     if (rendition->spacing != WWWINHERIT_QUARK)
407   {
408     XtSetArg(arglist[n],SGMLNfontSpacing,rendition->spacing); n++;
409   }
410    if (rendition->size != WWWINHERIT_SIZE)
411   {
412     XtSetArg(arglist[n],SGMLNfontSize,rendition->size); n++;
413   }
414    if (rendition->outline != WWWINHERIT_OUTLINE)
415   {
416     XtSetArg(arglist[n],SGMLNoutline,rendition->outline); n++;
417   }
418   if (rendition->underline != WWWINHERIT_UNDERLINE)
419   {
420     XtSetArg(arglist[n],SGMLNunderline,rendition->underline); n++;
421   }
422   if (rendition->underline_height != WWWINHERIT_UNDERLINEHEIGHT)
423   {
424     XtSetArg(arglist[n],SGMLNunderlineHeight,rendition->underline_height); n++;
425   }    
426   if (rendition->underline_style != WWWINHERIT_UNDERLINESTYLE)
427   {
428     XtSetArg(arglist[n],SGMLNunderlineStyle,rendition->underline_style); n++;
429   }
430   if (n) XtSetValues(w,arglist,n);
431 }
432 /*
433  *  Delete a file structure
434  *  -----------------------
435  */
436 void FreeFile(file)
437 WWWFile *file;
438 {
439   XtFree((char *)file);
440 }
441 /*
442  *  Create a copy of a file structure
443  *  ---------------------------------
444  */
445 static WWWFile *CopyFile(file)
446 WWWFile *file;
447 {
448   WWWFile *new = XtNew(WWWFile);
449  
450   *new = *file;
451   return new;
452 }
453 static int CompareFile(file1,file2)
454 WWWFile *file1;
455 WWWFile *file2;
456 {
457   return memcmp(file1,file2,sizeof(WWWFile));
458 }
459 /*
460  * Find a node
461  * -----------------------
462  */
463  
464 static WWWNode *WWWFindNode(file)
465 WWWFile *file;
466 {
467   int hashid = WWWHash(file);
468   WWWNode *a = HashTable[hashid];
469  
470   for (; a != NULL; a = a->hashclash) if (!CompareFile(a->file,file)) return a;
471  
472   return NULL;
473 }
474 /*
475  * Find (or create) a node
476  * -----------------------
477  */
478  
479 static WWWNode *WWWCreateNode(file,parent)
480 WWWFile *file;
481 WWWNode *parent;
482 {
483   int hashid;
484   WWWNode *a = WWWFindNode(file);
485  
486   if (!a)
487     {
488       int hashid  = WWWHash(file);
489  
490       a = XtNew(WWWNode);
491  
492       a->file = CopyFile(file);
493       a->title  = "Untitled";
494  
495       a->parent = parent ? parent : a;
496       a->prev = NULL;
497       a->next = NULL;
498       a->up   = NULL;
499       a->down = NULL;
500       a->chain= NULL;  
501       a->route= NULL;
502       a->visited  = FALSE;
503       a->redirect = NULL;
504  
505       a->icon  = NULL;
506       a->picture = NULL;
507  
508       a->widgets = NULL;
509       a->num_widgets = 0;
510       a->alloc_widgets = 0;
511       a->id = 0;
512  
513       a->hashclash = HashTable[hashid];
514       HashTable[hashid] = a;
515     }
516   return a;
517 }
518 /*
519  * Find (or create) a node and if necessary its parent
520  * ---------------------------------------------------
521  */
522 static WWWNode *WWWCreateNodeAndParent(file)
523 WWWFile *file;
524 {
525    WWWNode *parent;
526  
527    if (file->anchor)
528      {
529        XrmQuark temp = file->anchor;
530        file->anchor = NULL;      
531        parent = WWWCreateNode(file,NULL);
532        file->anchor = temp;
533      }
534    else parent = NULL;
535  
536    return WWWCreateNode(file,parent);
537 }
538 /*
539  * Locate a visited node in the history tree
540  */
541 static Widget LocateVisitedNode(w,node)
542 Widget w;
543 WWWNode *node;
544 {
545   Arg arglist[1];
546   char *href;
547   WWWFile *file;
548   int result = 1;
549  
550   XtSetArg(arglist[0],SGMLNhref,&href);
551   XtGetValues(w,arglist,1);
552  
553   if (href && *href)
554     {  
555       file = ParseFile(href,&WWWFileDefault);
556       result = CompareFile(file,node->file);
557       FreeFile(file);
558     }
559   return result ? NULL : w;
560 }
561 static Widget InsertBeforeWidget;
562 static int InsertBefore(w)
563 Widget w;
564 {
565   Arg arglist[2];
566   Cardinal num_children, p;
567   WidgetList children;
568  
569   XtSetArg(arglist[0],XtNnumChildren,&num_children);
570   XtSetArg(arglist[1],XtNchildren,&children);
571   XtGetValues(XtParent(w),arglist,2);
572  
573   for ( p = 0 ; p < num_children ; p++) if (*children++ == InsertBeforeWidget) return p;
574  
575   return num_children;
576 }
577 /*
578  * If the history node is visible, then add the new node to
579  * the history display.
580  */
581  
582 static void WWWUpdateHistory(node)
583 WWWNode *node;
584 {
585   Arg arglist[2];
586   int i;
587  
588   node = node->parent;
589   if (node == historyNode) return;  
590   if (node == YouAreHereNode) return;
591  
592   /*
593    * Get rid of old you are here marker
594    */
595  
596   for (i = 0 ; i < historyNode->num_widgets ; i++)
597     {
598       Widget c, w = historyNode->widgets[i];
599       if (w->core.being_destroyed) continue;
600       c = MidasTraceWidgetTree(w,"...img");
601       if (c) XtDestroyWidget(c);
602     }
603  
604   YouAreHereNode = node;
605  
606   if (!node->visited)
607     {
608      for (i = 0 ; i < historyNode->num_widgets ; i++)
609         {
610           Widget w = historyNode->widgets[i];
611           Widget r, ul = NULL;
612  
613           /*
614            * Start by trying to find the node's parent (if it has one) in the
615            * history tree.  
616            */
617  
618           if (node->up)
619             {
620               r = MidasScanWidgetTree(w,"...a",LocateVisitedNode,
621                                         (XtPointer) node->up->parent);
622             }
623           else r = NULL;
624  
625           /*
626            * If so the scan to find if the parent node already has a list of  
627            * children. If not create one.
628            */
629  
630           if (r)
631             {
632               Widget parent = XtParent(r);
633               int j, num_children;
634               WidgetList children;
635  
636               XtSetArg(arglist[0],XtNnumChildren,&num_children);
637               XtSetArg(arglist[1],XtNchildren,&children);
638               XtGetValues(parent,arglist,2);
639  
640               for (j=0; children[j++] != r; );
641               for (; j < num_children ; j++ )
642                 if (!strcmp(XtName(children[j]),"ul")) { ul = children[j]; break; }
643  
644               if (!ul)
645                 {
646                   XtSetArg(arglist[0],SGMLNbulletType,SGMLBULLET_LINKEDARROW);
647                   ul = XtCreateWidget("ul",sGMLListTextObjectClass,parent,arglist,1);
648                   MidasSetupWidget(ul);
649                   SGMLCompositeTextInsertChild(ul);
650                 }
651             }
652           else
653             {
654               ul = MidasTraceWidgetTree(w,"...ul");
655             }
656            /*
657             * Now create the entry in the list for the new node.
658             */
659             {
660               Widget m,a,q,c,h;
661               char *filename = WWWAsciiFile(node->file);
662  
663               WWWparent = historyNode;
664               WWWlastnode = historyNode->down;
665  
666               m = SGMLCreateWidget("li",sGMLMarkerTextObjectClass,ul,NULL,0);
667  
668               XtSetArg(arglist[0],SGMLNpixmap,circlePixmap);
669               c = SGMLCreateWidget("img",sGMLMarkerTextObjectClass,ul,arglist,1);
670  
671               XtSetArg(arglist[0],SGMLNhref,filename);
672               a = SGMLCreateWidget("a",sGMLAnchorTextObjectClass,ul,arglist,1);
673  
674               XtSetArg(arglist[0],SGMLNtext,node->title);
675               q = SGMLCreateWidget("",sGMLFormattedTextObjectClass,a,arglist,1);
676  
677               for (h = ul; !SGMLIsHyper(h) ; h = XtParent(h));
678               SGMLHyperShowObject(h,c,FALSE);
679               XtFree(filename);
680             }
681         }
682     }
683   else /* node is already visited */
684     {
685       for (i = 0 ; i < historyNode->num_widgets ; i++)
686         {
687           Widget w = historyNode->widgets[i];
688           Widget r, h;
689  
690           /*
691            * Find the node
692            */
693  
694           r = MidasScanWidgetTree(w,"...a",LocateVisitedNode,(XtPointer) node);
695           if (r)
696             {
697               Widget c;
698               InsertBeforeWidget = r;
699               XtSetArg(arglist[0],XtNinsertPosition,InsertBefore);              
700               XtSetValues(XtParent(r),arglist,1);
701  
702               XtSetArg(arglist[0],SGMLNpixmap,circlePixmap);
703               c = SGMLCreateWidget("img",sGMLMarkerTextObjectClass,XtParent(r),arglist,1);
704  
705               XtSetArg(arglist[0],XtNinsertPosition,NULL);              
706               XtSetValues(XtParent(r),arglist,1);
707  
708               for (h = r; !SGMLIsHyper(h) ; h = XtParent(h));
709               SGMLHyperShowObject(h,c,FALSE);
710            }
711         }
712     }
713 }
714 /*
715  * Reset all visited node
716  * ----------------------
717  */
718 static void WWWReset()
719 {
720   int i, nw;
721   WidgetList w;
722   WWWNode *n, *m;
723  
724   for (i=0;i<HASHSIZE;i++)
725     {
726       m = HashTable[i];  
727       while (m)
728         {
729           if (m->visited)
730             {
731               m->visited = FALSE;
732               n = m->up;
733               while(n)
734               {
735                 w = n->widgets;
736                 nw = n->num_widgets;
737                 for (; nw-- > 0; w++) WWWBeenThere(*w,FALSE);
738                 n = n->chain;
739               }
740             }
741           m = m->hashclash;
742         } /* while */
743     }    /* for */
744 }
745 /*
746  * Search a given string
747  */
748 static void WWWSearch(w,exp,sensitive,cont)
749 Widget w;
750 char *exp;
751 Boolean sensitive;
752 Boolean cont;
753 {
754   Widget result;
755  
756   if (*exp == '\0')
757     {
758       MidasBeep(50,w);
759       return;
760     }
761   result = SGMLHyperSearch((SGMLHyperWidget)w,exp,sensitive,cont);
762   if (result)
763    SGMLHyperShowObject((SGMLHyperWidget)w,result,TRUE);
764   else MidasBeep(50,w);
765 }
766  
767 /*
768  * Mark a node as visited
769  * ----------------------
770  */
771 static void WWWVisitNode(node)
772 WWWNode *node;
773 {
774   static WWWNode *LastVisitedNode = NULL;
775  
776   if (!node->visited)
777     {
778       WWWNode *src = node->up;  
779  
780       for ( ; src != NULL; src = src->chain)
781         {
782            WidgetList w = src->widgets;
783            int nw = src->num_widgets;
784            for (; nw-- > 0; w++) WWWBeenThere(*w,TRUE);
785         }
786       node->visited = TRUE;
787  
788       if (LastVisitedNode) LastVisitedNode->route = node;
789       else                 FirstVisitedNode = node;
790       LastVisitedNode = node;
791     }
792 }
793 static void WWWDetachWidgetFromNode(w,node)
794 Widget w;
795 WWWNode *node;
796 {
797   int i;
798   for (i=0 ; i < node->num_widgets; i++)
799     if (node->widgets[i] == w) node->widgets[i] = node->widgets[--node->num_widgets];
800 }
801 static void WWWAttachWidgetToNode(node,w)
802 WWWNode *node;
803 Widget w;
804 {
805    if (node->num_widgets == node->alloc_widgets)
806      node->widgets = (Widget *) XtRealloc((char *) node->widgets, (node->alloc_widgets += 5) * sizeof(Widget));
807    node->widgets[node->num_widgets++] = w;
808    XtAddCallback(w,XtNdestroyCallback,(XtCallbackProc) WWWDetachWidgetFromNode,(XtPointer)node);
809 }
810 /*
811  * Initialize a DataSource structure
812  * ---------------------------------
813  */
814 void WWWInitDataSource(data,mb,buffer,bsize,readRoutine,s)
815 DataSource *data;
816 MessageBlock *mb;
817 char    *buffer;
818 int     bsize;
819 ReadRoutine readRoutine;
820 int     s;
821 {
822   data->nleft = 0;
823   data->flag = &mb->flag;
824   data->read = 0;
825   data->length = 0;
826   data->eof = FALSE;
827   data->mb = mb;
828   data->buffer = buffer;
829   data->bsize = bsize;
830   data->socket = s;
831   data->readRoutine = readRoutine;
832 }
833  
834 /*
835  * Provides buffering for the TCP/IP packets
836  * -----------------------------------------
837  */
838 void ClearFlag(flag)
839 int *flag;
840 {
841   *flag = 0;
842 }
843 int GetCharacter(data)
844 DataSource *data;
845 {
846   XtInputMask mask;
847  
848   if (data->eof) return EOF;
849   if (data->nleft == 0)
850     {
851       /*
852        * Check if there are any events to process
853        */
854  
855 #ifdef MULTINET
856       int rc;
857       short iosb[4];
858       XtInputId id;
859  
860       if (*data->flag == 1) goto UserAbort;  
861       if (data->readRoutine == socket_read)
862         {
863           rc = SYS$QIO(EF,data->socket,IO$_RECEIVE,iosb,0,0,data->buffer,data->bsize,0,0,0,0);
864           id = XtAppAddInput(appl_context,EF,(XtPointer) iosb,
865                              (XtInputCallbackProc) ClearFlag,(XtPointer) data->flag);
866  
867           for (*data->flag = -1; *data->flag < 0;)
868             {
869               MidasFetchDispatchEvent();
870             }
871           XtRemoveInput(id);      
872  
873           if (*data->flag == 1)
874             {
875               Sys$Cancel(data->socket);
876               goto UserAbort;
877             }
878           data->nleft = iosb[1];
879         }
880       else
881         {
882           data->nleft = (data->readRoutine)(data->socket,data->buffer,data->bsize);
883           if (data->nleft < 0) printf("read error %s\n",strerror(errno));
884         }
885 #else
886 #ifdef ADDINPUTOK
887       XtInputId id;
888       if (*data->flag == 1) goto UserAbort;  
889       id = XtAppAddInput(appl_context,data->socket,(XtPointer)XtInputReadMask,
890                          (XtInputCallbackProc) ClearFlag,(XtPointer) data->flag);
891  
892       for (*data->flag = -1; *data->flag < 0;)
893         {
894           MidasFetchDispatchEvent();
895         }
896       XtRemoveInput(id);      
897  
898       if (*data->flag == 1) goto UserAbort;
899  
900 #endif
901       data->nleft = (data->readRoutine)(data->socket,data->buffer,data->bsize);
902       if (data->nleft < 0) printf("read error %s\n",strerror(errno));
903 #endif
904       data->next = data->buffer;
905       data->read += data->nleft;
906       data->mb->newMessage = TRUE;
907       data->mb->length = data->length;
908       data->mb->read   = data->read;
909       *data->mb->message = '@';
910       while (XtAppPending(appl_context)) MidasFetchDispatchEvent();
911       if (*data->flag == 1) goto UserAbort;
912     }
913   if (data->nleft-- > 0) return *(data->next++);
914   data->eof = TRUE;
915   return EOF;
916  
917 UserAbort:
918  
919   data->eof = TRUE;
920   strcpy(data->mb->message,AbortMessage);
921   return EOF;
922 }
923 WWWFiletype *WWWGetFiletype(filename,defaultType)
924 char *filename;
925 WWWFiletype *defaultType;
926 /*
927  * Given a filename, return a filetype structure based on the file's extension.
928  * The returned structure must be freed using WWWFreeFiletype
929  */
930 {
931   char *copy;
932   WWWFiletype *result = XtNew(WWWFiletype);
933   int i;
934  
935   if (filename)
936     {
937       copy = strrchr(filename,'/');
938       if (copy) copy = XtNewString(copy+1);
939       else      copy = XtNewString(filename);
940     }
941   else copy = "";
942  
943   for (i=0; ; i++)
944     {
945       ListItem *item;
946       WWWFiletype *temp;
947       char *ext = strrchr(copy,'.');
948  
949       if (ext)
950         {
951  
952           /* Note: we first try a case sensitive search, but if that fails we convert
953            *       the file extension to lowercase and try again.
954            */
955  
956           ext++;
957           item = MidasFindItemInList(appResources.filetypes,ext);
958           if (!item)
959             {
960               char *p;
961               for (p = ext; *p; p++) *p = tolower(*p);
962               item = MidasFindItemInList(appResources.filetypes,ext);
963             }
964         }
965       else item = NULL;
966  
967       if (item)  temp = (WWWFiletype *) item->Pointer;
968       else       temp = defaultType;
969  
970       if (i == 0)
971         {
972           *result = *temp;
973           result->Command = XtNewString(temp->Command);
974         }
975       else
976         {
977           result->IconName = temp->IconName;  
978           result->Destination = temp->Destination;
979           if (temp->Command)
980             {
981               result->Command = XtRealloc(result->Command,
982                                 strlen(result->Command)+strlen(temp->Command)+1);
983               strcat(result->Command,temp->Command);
984             }
985         }
986       if (!ext || !temp->Filter) break;
987       *--ext = '\0';
988     }
989  
990   result->UserData = copy;
991  
992   return result;
993 }
994 void WWWFreeFiletype(old)
995 WWWFiletype *old;
996 {
997   XtFree(old->Command);
998   XtFree(old->UserData);
999   XtFree((char *)old);
1000 }
1001 static void DoCleanUp()
1002 {
1003   ListItem *item;
1004  
1005   for (item = cleanUp->First; item ; item = item->Next)
1006     {
1007       unlink(item->Entry);
1008       GhostviewDisableInterpreter((Widget) item->Pointer);
1009     }
1010 }
1011 static void DestroyGhostview(w,item)
1012 Widget w;
1013 ListItem *item;
1014 {
1015   unlink(item->Entry);
1016   item->Pointer = NULL;
1017   MidasRemoveItemFromList(cleanUp,item);
1018 }
1019 static void MapGhostview(w,gv)
1020 Widget w;
1021 Widget gv;
1022 {
1023    Arg arglist[2];
1024    int n = 0;
1025    int pages;
1026  
1027    XtSetArg(arglist[n],XtNpages,&pages); n++;
1028    XtGetValues(gv,arglist,n); n = 0;
1029  
1030    XtSetArg(arglist[n],XtNuseBackingPixmap,TRUE); n++;
1031    if (pages == 1) { XtSetArg(arglist[n],XtNcurrentPage,&pages); n++; }
1032    XtSetValues(gv,arglist,n); n = 0;
1033  
1034    if (pages <= 0) GhostviewEnableInterpreter(gv);  
1035 }  
1036 static void UnmapGhostview(w,gv)
1037 Widget w;
1038 Widget gv;
1039 {
1040    Arg arglist[2];
1041    int n = 0;
1042  
1043    GhostviewDisableInterpreter(gv);
1044  
1045    XtSetArg(arglist[n],XtNuseBackingPixmap,FALSE); n++;
1046    XtSetArg(arglist[n],XtNcurrentPage,0);  n++;
1047    XtSetValues(gv,arglist,n);
1048  
1049    if (XtHasCallbacks(w,SGMLNmapCallback) == XtCallbackHasNone)
1050      XtAddCallback(w,SGMLNmapCallback,(XtCallbackProc) MapGhostview,(XtPointer) gv);
1051 }  
1052 static void TrapGhostviewOutput(w,errors,buf)
1053 Widget w;
1054 char *buf;
1055 Widget errors;
1056 {
1057   Widget ge = MidasTraceWidgetTree(w,"^^^WWWMain###WWWGhostviewErrors");
1058   Widget text = MidasTraceWidgetTree(ge,"...WWWGhostscriptErrorText");
1059  
1060 #ifndef DEBUG
1061   if (strstr(buf,"Warning:")) return;
1062 #endif  
1063  
1064   XmTextInsert(text,XmTextGetLastPosition(text),buf);
1065   MidasPopup(ge);
1066 }
1067 static void TrapGhostviewMessage(w,errors,buf)
1068 Widget w;
1069 char *buf;
1070 Widget errors;
1071 {
1072    int n = 0 , pages;
1073    Arg arglist[10];
1074  
1075    XtSetArg(arglist[n],XtNpages,&pages); n++;
1076    XtGetValues(w,arglist,n);
1077  
1078    if (!strcmp(buf,"Page"))
1079      {
1080        if (pages == 0) MidasQueueCommand(w,"Set Sensitive .^^^WWWMain...Next_Page True");
1081      }
1082    else if (!strcmp(buf,"Done"))
1083      {
1084        if (pages == 0) MidasQueueCommand(w,"Set Sensitive .^^^WWWMain...Next_Page False");
1085      }
1086    else TrapGhostviewOutput(w,errors,buf);
1087 }
1088 Widget WWWLoadPS(w,mb,GetChar,data)
1089 Widget w;
1090 MessageBlock *mb;
1091 int (*GetChar)();
1092 DataSource *data;
1093 {
1094   Widget result, gv, container;
1095   Arg arglist[10];
1096   int n=0;
1097   int c;
1098 #ifndef VMS
1099   char buffer[L_tmpnam];
1100   char *temp = tmpnam(buffer);
1101   FILE *tmp = fopen(temp,"w");
1102 #else
1103   char buffer[256];
1104   FILE *tmp = fopen("sys$scratch:midaswww.tmp;","w");
1105   char *temp = fgetname(tmp,buffer);
1106 #endif  
1107   int pages;
1108   struct document *doc;
1109   ListItem *item;
1110  
1111   if (!tmp)
1112     {
1113       sprintf(mb->message,"Error creating temporary file: %s\n",strerror(errno));
1114       mb->help_code = "temp_create_failed";
1115       return NULL;
1116     }
1117   /*
1118    * Some old http servers prepend <plaintext> to .ps documents
1119    * so scan for first % to start real postscript doc
1120    */
1121   while ((c = GetChar(data)) != '%' && c != EOF);  
1122   if (c == EOF)
1123     {
1124       strcpy(mb->message,"No leading % found in .ps file");
1125       mb->help_code = "bad_ps";
1126       return NULL;
1127    }  
1128  
1129   fputc('%',tmp);
1130   while ((c = GetChar(data)) != EOF && putc(c,tmp) != EOF);
1131   if (ferror(tmp))
1132     {
1133       sprintf(mb->message,"Error writing temporary file: %s\n",strerror(errno));
1134       mb->help_code = "temp_write_failed";
1135       unlink(temp);
1136       fclose(tmp);
1137       return NULL;
1138     }
1139   fclose(tmp);
1140  
1141   if (!cleanUp)
1142     {
1143       cleanUp = MidasCreateEmptyList("cleanup");
1144       atexit(DoCleanUp);
1145     }
1146   item = MidasAddItemToList(cleanUp,temp);
1147  
1148   result = XtCreateWidget("gv_manager",sGMLCompositeTextObjectClass,w,NULL,0);
1149  
1150   XtSetArg(arglist[n],XtNfile,temp); n++;
1151   gv = XtCreateWidget("ghostview",ghostviewPlusWidgetClass,w,arglist,n);
1152   item->Pointer = (XtPointer) gv;  
1153  
1154   container = MidasTraceWidgetTree(result,".container");
1155  
1156   XtAddCallback(gv,XtNoutputCallback,(XtCallbackProc) TrapGhostviewOutput,(XtPointer) NULL);
1157   XtAddCallback(gv,XtNmessageCallback,(XtCallbackProc) TrapGhostviewMessage,(XtPointer) NULL);
1158   XtAddCallback(gv,XtNdestroyCallback,(XtCallbackProc) DestroyGhostview,(XtPointer) item);
1159   XtAddCallback(result,SGMLNunmapCallback,(XtCallbackProc) UnmapGhostview,(XtPointer) gv);
1160   MidasSetupWidget(result);
1161  
1162   n = 0;
1163   XtSetArg(arglist[n],XtNpages,&pages); n++;
1164   XtSetArg(arglist[n],XtNdoc,&doc); n++;
1165   XtGetValues(gv,arglist,n);
1166  
1167   if (pages>1)
1168     {
1169       WWWFile *here = CopyFile(data->mb->file);
1170       Widget child;
1171       char *buffer = XtMalloc(pages*40+400);
1172       char *b = buffer;
1173       int i, page;
1174       struct page *p = doc->pages;
1175  
1176       sprintf(b,"This is a multipage Postscript document, select page:<ul>\n");
1177       b += strlen(b);
1178  
1179       for (page=1; page<=pages; page++, p++)
1180         {
1181           WWWNode *src;
1182           char temp[8];
1183           if (doc->pageorder == DESCEND) i = pages - page + 1;
1184           else                           i = page;
1185  
1186           if (strcmp(p->label,"?")) sprintf(b,"<li><a href=\"#%d\">Page %s</a>\n",i,p->label);
1187           else                      sprintf(b,"<li><a href=\"#%d\">Page %d</a>\n",i,i);
1188           b += strlen(b);
1189  
1190           sprintf(temp,"%d",i);
1191           here->anchor = XrmStringToQuark(temp);
1192  
1193           src = WWWCreateNode(here,WWWparent);
1194  
1195           if (WWWlastnode) WWWlastnode->next = src;
1196           src->prev = WWWlastnode;
1197           WWWlastnode = src;
1198           WWWAttachWidgetToNode(src,container);
1199         }  
1200       FreeFile(here);
1201  
1202       sprintf(b,"</ul><h2>Note</h2>Once in the document you can use the <b>Next</b> and <b>Previous</b> buttons to go to ");
1203       b += strlen(b);  
1204       sprintf(b,"adjacent pages, and the <b>Go Back</b> button to return to this index.");      
1205  
1206       child = SGMLHyperSetText(result,buffer);
1207       SGMLCompositeTextInsertChild(child);
1208       XtFree(buffer);
1209     }    
1210   else if (pages == 1)
1211     {
1212       n = 0;
1213       XtSetArg(arglist[n],XtNcurrentPage,1); n++;
1214       XtSetValues(gv,arglist,n);
1215     }  
1216  
1217   return result;
1218 }
1219 static Widget WWWLoadFile(w,mb,GetChar,data,filetype)
1220 MessageBlock *mb;
1221 Widget w;
1222 int (*GetChar)();
1223 WWWFiletype *filetype;
1224 {
1225   int c;
1226   FILE *out;
1227   char *fname = filetype->UserData;
1228  
1229   out = fopen(fname,"w");
1230   if (out==0)
1231     {
1232       sprintf(mb->message,"Cannot open file %s",fname);
1233       mb->help_code = "file_open_failed";
1234       return NULL;
1235     }
1236  
1237   while ((c = GetChar(data)) != EOF && putc(c,out) != EOF);
1238   if (ferror(out))
1239     {
1240       sprintf(mb->message,"Error writing file %s: %s\n",fname,strerror(errno));
1241       mb->help_code = "file_write_failed";
1242       unlink(fname);
1243     }
1244   else sprintf(mb->message,"File saved as <b>%s</b>",fname);  
1245  
1246   fclose(out);
1247   return NULL;
1248 }
1249 static void DestroyTempfile(w,i)
1250 Widget w;
1251 ListItem *i;
1252 {
1253   if (i->Entry != NULL)
1254     unlink(i->Entry);
1255   MidasRemoveItemFromList(cleanUpTempfile,i);
1256 }
1257 static void DoCleanUpTempfile()
1258 {
1259   ListItem *item;
1260  
1261   for (item = cleanUpTempfile->First; item; item = item->Next)
1262     {
1263       if(item->Entry == NULL)
1264         continue;
1265       unlink(item->Entry);
1266     }
1267 }
1268 static void ReLoadMultimedia(w,command)
1269 Widget w;
1270 char *command;
1271 {
1272 #ifdef VMS
1273   int flags = 1; /* nowait */
1274   int iss;
1275   struct {
1276      unsigned short len;
1277      unsigned char  type;
1278      unsigned char  class;
1279      char           *ptr;
1280    } command_D = {0,14,1,0};
1281  
1282   command_D.len = strlen(command);
1283   command_D.ptr = command;
1284   iss = Lib$Spawn(&command_D,&NULL,&NULL,&flags);
1285 #else
1286   system(command);
1287 #endif
1288 }
1289 static Widget WWWLoadMultimedia(w,mb,filetype,tmpf)
1290 Widget w;
1291 MessageBlock *mb;
1292 WWWFiletype *filetype;
1293 char *tmpf;
1294 {
1295   char *buff = XtMalloc(128);
1296   char *name = XrmQuarkToString(mb->file->file);
1297   char *command = filetype->Command;
1298   char *dest = XrmQuarkToString(filetype->Destination);
1299   Widget result;
1300   Widget anchor;
1301   XtPointer state = SGMLHyperOpen(w);
1302   char temp[256];
1303   ListItem *item;
1304 #ifdef VMS
1305   int flags = 1; /* nowait */
1306   int iss;
1307   struct {
1308      unsigned short len;
1309      unsigned char  type;
1310      unsigned char  class;
1311      char           *ptr;
1312    } command_D = {0,14,1,0};
1313  
1314   sprintf(buff,"%s %s",command,tmpf);
1315 #else
1316   sprintf(buff,"%s %s &",command,tmpf);
1317 #endif
1318  
1319   sprintf(temp,"<title>%s %s</title><h1><img pixmap=Movie>Multimedia<img pixmap=Sound></h1>%s <b>%s</b> file %s. ",command,name,command,dest,name);
1320   SGMLHyperWrite(state,temp);
1321   sprintf(temp,"<p>Click <a href=\"#null\"><img pixmap=Movie></a>to Play again");
1322  
1323   SGMLHyperWrite(state,temp);
1324   result = SGMLHyperClose(state);
1325  
1326   if (!cleanUpTempfile)
1327     {
1328       cleanUpTempfile = MidasCreateEmptyList("cleanuptempfile");
1329       atexit(DoCleanUpTempfile);
1330     }
1331   if (filetype->Tempfile)
1332     item = MidasAddItemToList(cleanUpTempfile,tmpf);
1333   else
1334     item = MidasAddItemToList(cleanUpTempfile,NULL);
1335   item->Pointer = buff;
1336   XtAddCallback(result,XtNdestroyCallback,(XtCallbackProc) DestroyTempfile,(XtPointer)item);
1337  
1338   anchor = MidasTraceWidgetTree(result,"...a.");
1339   XtAddCallback(anchor,SGMLNactivateCallback,(XtCallbackProc) ReLoadMultimedia,buff);
1340 #ifdef VMS
1341   command_D.len = strlen(buff);
1342   command_D.ptr = buff;
1343   iss = Lib$Spawn(&command_D,&NULL,&NULL,&flags);
1344   if (iss != 1)
1345     {
1346       sprintf(mb->message,"Error creating subprocess for playing multimedia");
1347       mb->help_code = "vms_multimedia_error";
1348       return NULL;
1349     }
1350 #else
1351   system(buff);
1352 #endif
1353   return result;
1354 }
1355  
1356 static Widget WWWLoadXBM(w,mb,GetChar,data)
1357 MessageBlock *mb;
1358 Widget w;
1359 int (*GetChar)();
1360 XtPointer data;
1361 {
1362   int c, xhot, yhot, iss;  
1363   unsigned int width, height;
1364   Pixmap bitmap;
1365   Widget result;
1366   char *temp;
1367  
1368  
1369   if (!WWWWriteToTempfile(mb,GetChar,data,&temp,NULL)) return NULL;
1370  
1371   iss = XReadBitmapFile(XtDisplay(w),XtWindow(w),temp,&width,&height,&bitmap,&xhot,&yhot);
1372   if (iss != BitmapSuccess)
1373     {
1374       strcpy(mb->message,"Bitmap read failed");
1375       mb->help_code = "bitmap_read_failed";
1376       result = NULL;
1377     }
1378   else
1379    {
1380       result = (Widget) bitmap;
1381       mb->help_code = "pixmap";
1382    }
1383   unlink(temp);  
1384   XtFree(temp);
1385   return result;
1386 }
1387 Widget WWWLoadGIF(w,mb,GetChar,data)
1388 MessageBlock *mb;
1389 Widget w;
1390 int (*GetChar)();
1391 XtPointer data;
1392 {
1393   char buffer[256];
1394   Widget result;
1395   GIFImage *gifImage = GIFLoadFile(w,buffer,GetChar,data);
1396  
1397   if (gifImage)
1398     {
1399       result = (Widget) gifImage;
1400       mb->help_code = "image";
1401     }
1402   else
1403     {    
1404       sprintf(mb->message,"Error loading GIF file: %s<p>",buffer);
1405       mb->help_code = "gif_load_error";
1406       result = NULL;
1407     }
1408  
1409   return result;
1410 }
1411 static int WWWBuildFile(data)
1412 DataSource *data;
1413 {
1414   static char prefix[] = "<plaintext>\n";
1415  
1416   if (data->nleft == 0)
1417     {
1418       if (data->read == 0)
1419         {
1420           data->next = prefix;
1421           data->nleft = strlen(prefix);
1422           data->read += data->nleft;
1423         }  
1424       else
1425        {
1426           return GetCharacter(data);
1427        }
1428     }  
1429   if (data->nleft-- > 0) return *(data->next++);
1430   data->eof = TRUE;
1431   return EOF;
1432 }
1433 int WWWWriteToTempfile(mb,GetChar,data,tmp,ext)
1434 MessageBlock *mb;
1435 int (*GetChar)();
1436 DataSource *data;
1437 char **tmp;
1438 char *ext;
1439 {
1440   int c;
1441 #ifndef VMS
1442   char buffer[L_tmpnam];
1443   char *tmpn = tmpnam(buffer);
1444   FILE *tmpfd;
1445   char *temp = XtMalloc(strlen(tmpn)+ (ext?strlen(ext):0) + 2);
1446  
1447   strcpy(temp,tmpn);
1448   if (ext)
1449      strcat(temp,ext);
1450   tmpfd = fopen(temp,"w");
1451 #else
1452   char buffer[256];
1453   char *s = "sys$scratch:midaswww";
1454   char *tmpn = XtMalloc(strlen(s) + (ext?strlen(ext):4) + 2);
1455   FILE *tmpfd;
1456   char *temp;
1457  
1458   strcpy(tmpn,s);
1459   if (ext)
1460     {
1461       strcat(tmpn,ext);
1462       strcat(tmpn,";");
1463     }
1464   else
1465     strcat(tmpn,".tmp;");
1466  
1467   tmpfd = fopen(tmpn,"w");
1468   XtFree(tmpn);
1469   temp = XtNewString(fgetname(tmpfd,buffer));
1470 #endif
1471  
1472   if(!tmpfd)
1473     {
1474       sprintf(mb->message,"Error creating temporary file: %s\n",strerror(errno));
1475       mb->help_code = "temp_create_failed";
1476       return FALSE;
1477     }
1478  
1479   for ( c = GetChar(data); !data->eof ; c = GetChar(data))  putc(c,tmpfd);
1480   if (ferror(tmpfd))
1481     {
1482       sprintf(mb->message,"Error writing temporary file: %s\n",strerror(errno));
1483       mb->help_code = "temp_write_failed";
1484       unlink(temp);
1485       fclose(tmpfd);
1486       return FALSE;
1487     }
1488   fclose(tmpfd);
1489   *tmp = temp;
1490   return TRUE;
1491 }
1492  
1493 Widget WWWLoadByFiletype(w,mb,filetype,GetChar,data)
1494 Widget w;
1495 MessageBlock *mb;
1496 WWWFiletype *filetype;
1497 int (*GetChar)();
1498 DataSource *data;
1499 {
1500   Widget result;
1501   FILE *ptr;  
1502   int pds[2];
1503   int stderrds;
1504   int fd;
1505   char *tempfile;
1506  
1507   if (filetype->Tempfile)
1508     {
1509       if (!WWWWriteToTempfile(mb,GetChar,data,&tempfile,NULL)) return NULL;
1510  
1511       if (filetype->Shellflag)
1512         {
1513           if ((fd = open(tempfile,O_RDONLY,0)) < 0)
1514             {
1515               sprintf(mb->message,"Cannot open temporary file <p> Reason: %s\n",strerror(errno));
1516               mb->help_code = "file_open_failed";
1517               result = NULL;
1518               goto cleanup;
1519             }
1520           else
1521             {
1522               socket_close(data->socket);
1523               data->socket = fd;
1524               data->nleft = 0;
1525               data->read = 0;
1526               data->eof = FALSE;
1527             }
1528         }
1529     }
1530   else
1531       tempfile = XrmQuarkToString(mb->file->file);
1532  
1533   if (filetype->Command && filetype->Shellflag)
1534     {
1535 #ifndef VMS
1536        if (pipe(pds) <0)
1537          {
1538            sprintf(mb->message,"pipe error: can't create a pipe");
1539            mb->help_code = "pipe_fail";
1540            result = NULL;
1541            goto cleanup;
1542           }
1543  
1544        close(0);          /* close stdin */
1545        dup(data->socket); /* data socket becomes stdin */
1546  
1547        close(2);                    /* close stderr */
1548        stderrds = dup(pds[1]);      /* write end of pipe pds becomes stderr */
1549  
1550        ptr = popen(filetype->Command, "r");
1551  
1552        close(pds[1]);   /* we don't need the write-end of pipe in the parent process */
1553        close(stderrds); /* close the stderr descriptor */
1554        dup(1);          /* stderr become stdout */
1555        if (ptr == NULL)
1556          {
1557            sprintf(mb->message,"Call to popen failed for command: <tt>%s</tt>",filetype->Command);
1558            mb->help_code = "pipe_fail";    
1559            close(pds[0]);
1560            result = NULL;
1561            goto cleanup;
1562          }
1563        data->socket = fileno(ptr); /* ??? */
1564 #else
1565        mb->help_code = "no_vms_pipes";    
1566        sprintf(mb->message,"Sorry, pipes not supported (yet) under VMS.<p>Unable to execute command: <tt>%s</tt>",filetype->Command);
1567        result = NULL;
1568        goto cleanup;
1569 #endif  
1570     }
1571   else ptr = NULL;
1572  
1573   if      (filetype->Destination == psDestination  ) result = WWWLoadPS(w,mb,GetChar,data);
1574   else if (filetype->Destination == htmlDestination) result = SGMLHyperLoadText(w,GetChar,data);
1575   else if (filetype->Destination == gifDestination ) result = WWWLoadGIF(w,mb,GetChar,data);
1576   else if (filetype->Destination == textDestination) result = SGMLHyperLoadText(w,WWWBuildFile,data); /* VERY SILLY */
1577   else if (filetype->Destination == fileDestination) result = WWWLoadFile(w,mb,GetChar,data,filetype);
1578   else if (filetype->Destination == xbmDestination ) result = WWWLoadXBM(w,mb,GetChar,data);
1579   else if (filetype->Destination == multimediaDestination) result = WWWLoadMultimedia(w,mb,filetype,tempfile);
1580   else if (filetype->Destination == unsupportDestination)
1581     {
1582       sprintf(mb->message,"The file type is not supported by HTTP/1.0 at this moment");
1583       mb->help_code = "unknown_destination";
1584       result = NULL;
1585     }
1586   else
1587     {
1588       sprintf(mb->message,"Unknown destination %s",XrmQuarkToString(filetype->Destination));
1589       mb->help_code = "unknown_destination";
1590       result = NULL;
1591      }
1592  
1593 #ifndef VMS
1594   if (ptr)
1595     {
1596       int iss = pclose(ptr);
1597  
1598       if (iss && result == NULL)
1599         {
1600           int n, i;
1601           char buff[512], reason[100];
1602  
1603           if      (WIFSTOPPED(iss) ) sprintf(reason, "Stopped, signal %d"     ,WSTOPSIG(iss));
1604           else if (WIFEXITED(iss)  ) sprintf(reason, "Exited, status = %d"    ,WEXITSTATUS(iss));
1605           else if (WIFSIGNALED(iss)) sprintf(reason, "Terminated, signal = %d",WTERMSIG(iss));
1606           else                       strcpy(reason,"Unknown");  
1607  
1608           if ((n = read(pds[0],buff,sizeof(buff))) > 0)
1609             {
1610               buff[n > 511 ? 511 : n] = '\0';
1611  
1612               sprintf(mb->message+strlen(mb->message),"<p>Error while processing file <p>Command: <tt>%s</tt><p>Reason: %s<p>Messages: <pre>%s</pre>",
1613                       filetype->Command,reason,buff);                                      
1614               mb->help_code = "file_process_fail";
1615             }
1616           else
1617             {
1618               sprintf(mb->message+strlen(mb->message),"<p>Error while processing file <p>Command: <tt>%s</tt><p>Reason: %s",
1619                       filetype->Command,reason);
1620               mb->help_code = "unknown";
1621             }
1622         }
1623     }
1624   if (filetype->Command) close(pds[0]);
1625 #endif
1626  
1627 cleanup:
1628   if (filetype->Tempfile && filetype->Shellflag)
1629     {
1630       close(fd);
1631       unlink(tempfile);
1632       XtFree(tempfile);
1633     }
1634   return result;
1635 }
1636 /*
1637  * Fetch the local document
1638  * ------------------------
1639  */
1640  
1641 Widget WWWFetchDocumentLOCAL(w,file,mb)
1642 Widget w;
1643 WWWFile *file;
1644 MessageBlock *mb;
1645 {
1646   Widget result;
1647   WWWFiletype *filetype;
1648   char *name = XrmQuarkToString(file->file);
1649   DataSource data;
1650   char buffer[8096];
1651  
1652   int f = open(name,O_RDONLY,0);
1653   if (f < 0)  
1654     {
1655       sprintf(mb->message,"Cannot open file %s <p> Reason: %s",name,strerror(errno));
1656       mb->help_code = "file_open_failed";
1657       return NULL;
1658     }  
1659   WWWInitDataSource(&data,mb,buffer,sizeof(buffer),read,f);
1660  
1661   mb->flag = -1;  
1662  
1663   filetype = WWWGetFiletype(name,appResources.localDefaultFiletype);
1664   /* Since it is Local, don't need to use any tempfile */
1665   filetype->Tempfile = FALSE;
1666   result = WWWLoadByFiletype(w,mb,filetype,GetCharacter,&data);
1667  
1668   WWWFreeFiletype(filetype);
1669   close(f);
1670   return result;
1671 }  
1672 /*
1673  * Make a TCPIP connection
1674  *
1675  * If this routine fails it returns 0 with an error message in mb->message
1676  * Otherwise it returns the socket number on which the connection has been made.
1677  */
1678 int TCPIPConnect(file,mb)
1679 WWWFile *file;
1680 MessageBlock *mb;
1681 {
1682   static struct sockaddr_in server;
1683   static XrmQuark prevnode = NULL;
1684   char *node = XrmQuarkToString(file->node);
1685   int port = file->port;
1686   int s = 0;
1687   int enable = 1;
1688   int disable = 0;
1689   XtInputId id,id2;
1690  
1691 /*
1692  * Check that the port is valid
1693  */
1694  
1695   if (port < 1024)
1696     {
1697       char buf[20];
1698       List *ok = appResources.ok_ports;
1699       ListItem *item;
1700  
1701       sprintf(buf,"%d",port);
1702       item = MidasFindItemInList(ok,buf);
1703  
1704       if (!item)
1705         {
1706           sprintf(mb->message,"Connection to %s port %d disallowed",node,port);
1707           mb->help_code = "secure_port";
1708           return 0;
1709         }
1710     }
1711   /*
1712    * For sites with overloaded name servers (such as SLAC), name translation
1713    * can be very slow, so we add some caching here.
1714    */
1715   if (file->node != prevnode || prevnode == NULL) /* common case */
1716     {
1717       static List *nodeList = NULL;
1718       struct _cache { int length ; XtPointer data; } *cache;
1719       ListItem *item;
1720  
1721       if (!nodeList) nodeList = MidasCreateEmptyList("nodeList");
1722  
1723       item = MidasFindItemInList(nodeList,node);
1724       if (!item)
1725         {
1726           struct hostent *hp;
1727  
1728           hp = gethostbyname(node);
1729           if (hp == 0)
1730             {
1731               sprintf(mb->message,"Host %s unknown",node);
1732               mb->help_code = "unknown_host";
1733               return 0;
1734             }
1735  
1736           item = MidasAddItemToList(nodeList,node);
1737           cache = (struct _cache *) XtMalloc(hp->h_length + sizeof(int));
1738           cache->length = hp->h_length;
1739           bcopy(hp->h_addr, &cache->data, hp->h_length);
1740           item->Pointer = (XtPointer) cache;
1741         }
1742       cache = (struct _cache *) item->Pointer;
1743       bcopy(&cache->data,&server.sin_addr, cache->length);
1744       prevnode = file->node;
1745     }
1746  
1747   server.sin_family = AF_INET;
1748   server.sin_port = htons(port);
1749  
1750   /*
1751    * Sockets 0,1,2 are no good, just get another one
1752    */
1753  
1754   for (;s >= 0 && s <= 2; ) s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1755   if (s < 0)
1756     {
1757       sprintf(mb->message,"Socket create failed<p> Reason: %s",strerror(errno));
1758       mb->help_code = "socket_create_failed";
1759       return 0;
1760     }                                  
1761  
1762  
1763   mb->newMessage = TRUE;
1764   sprintf(mb->message,"Waiting to connect to: %s port %d",node,port);
1765  
1766 #ifdef MULTINET
1767     {
1768       int rc;
1769       short iosb[4];
1770       XtInputId id;
1771  
1772       mb->flag = -1;
1773       rc = SYS$QIO(EF,s,IO$_CONNECT,iosb,0,0,&server,sizeof(server),0,0,0,0);
1774       id = XtAppAddInput(appl_context,EF,(XtPointer) iosb,(XtInputCallbackProc) ClearFlag,(XtPointer) &mb->flag);
1775  
1776       for (; mb->flag < 0;)
1777         {
1778           MidasFetchDispatchEvent();
1779         }
1780       XtRemoveInput(id);
1781  
1782       if (mb->flag == 1)
1783         {
1784           Sys$Cancel(s);
1785         }
1786       else if (iosb[0] != 1) mb->flag = -1;
1787     }
1788 #else /* MULTINET */
1789 #ifndef NO_NBIO
1790   socket_ioctl(s,FIONBIO,&enable);
1791 #endif
1792   mb->flag = connect(s,&server,sizeof(server));
1793   if (mb->flag<0 && (socket_errno == EWOULDBLOCK || socket_errno == EINPROGRESS))
1794     {
1795 #ifdef ADDINPUTOK
1796       XtInputId id = XtAppAddInput(appl_context,s,(XtPointer) XtInputWriteMask,(XtInputCallbackProc) ClearFlag,(XtPointer) &mb->flag);
1797       for (;mb->flag < 0;)
1798         {
1799           MidasFetchDispatchEvent();
1800         }
1801       XtRemoveInput(id);
1802 #endif
1803     }
1804 #ifndef NO_NBIO
1805   socket_ioctl(s,FIONBIO,&disable);
1806 #endif
1807 #endif
1808   if (mb->flag == 1)
1809     {
1810       strcpy(mb->message,AbortMessage);
1811       socket_close(s);
1812       return 0;
1813     }
1814   if (mb->flag < 0)  
1815     {
1816       sprintf(mb->message,"Cannot connect to %s port %d",node,port);
1817       mb->help_code = "cannot_connect";
1818       socket_close(s);
1819       return 0;
1820     }
1821   return s;
1822 }
1823 /*
1824  * This routine writes a message over the TCPIP connection, and then waits until a
1825  * response is available (or the user aborts) before returning.
1826  *
1827  * If this routine fails it returns 0 with an error message in mb->message and closes the socket
1828  * Otherwise it returns the socket number on which the connection has been made.
1829  */
1830 int TCPIPWriteAndWaitForResponse(s,file,mb,command)
1831 int s;
1832 WWWFile *file;
1833 MessageBlock *mb;
1834 char *command;
1835 {
1836   char *node = XrmQuarkToString(file->node);
1837   int port = file->port;
1838   XtInputId id;
1839  
1840   mb->newMessage = TRUE;
1841   sprintf(mb->message,"Waiting to write to %s port %d",node,port);
1842  
1843 #ifdef MULTINET
1844     {
1845       int rc;
1846       short iosb[4];
1847  
1848       mb->flag = -1;
1849  
1850       rc = SYS$QIO(EF,s,IO$_SEND,iosb,0,0,command,strlen(command),0,0,0,0);
1851       id = XtAppAddInput(appl_context,EF,(XtPointer) iosb,(XtInputCallbackProc) ClearFlag,(XtPointer) &mb->flag);
1852       for (; mb->flag < 0;)
1853         {
1854           MidasFetchDispatchEvent();
1855         }
1856       XtRemoveInput(id);
1857       if (mb->flag == 1)
1858         {
1859           Sys$Cancel(s);
1860           strcpy(mb->message,AbortMessage);
1861           socket_close(s);
1862           return 0;
1863         }
1864       else if (iosb[0] != 1) mb->flag = -1;
1865     }
1866 #else /* MULTINET */
1867     {  
1868       void (*oldhandler)() =  signal(SIGPIPE,SIG_IGN);
1869  
1870       mb->flag = -1;
1871  
1872 #ifdef ADDINPUTOK
1873       id = XtAppAddInput(appl_context,s,(XtPointer) XtInputWriteMask,(XtInputCallbackProc) ClearFlag,(XtPointer) &mb->flag);
1874  
1875       for (;mb->flag < 0;)
1876         {
1877            MidasFetchDispatchEvent();
1878         }
1879       XtRemoveInput(id);
1880  
1881       if (mb->flag == 1)
1882         {
1883           strcpy(mb->message,AbortMessage);
1884           socket_close(s);
1885           return 0;
1886         }
1887 #endif
1888       mb->flag = socket_write(s,command,strlen(command));
1889       signal(SIGPIPE,oldhandler);
1890     }
1891 #endif
1892  
1893   if (mb->flag < 0)  
1894     {
1895       sprintf(mb->message,"Cannot connect to %s port %d",node,port);
1896       mb->help_code = "cannot_connect";
1897       socket_close(s);
1898       return 0;
1899     }
1900   mb->newMessage = TRUE;
1901   sprintf(mb->message,"Waiting for response from %s port %d",node,port);
1902  
1903   mb->flag = -1;
1904  
1905 #ifdef ADDINPPUTOK
1906   id = XtAppAddInput(appl_context,s,(XtPointer) XtInputReadMask,(XtInputCallbackProc) ClearFlag,(XtPointer) &mb->flag);
1907  
1908   for (;mb->flag < 0;)
1909      {
1910        MidasFetchDispatchEvent();
1911      }
1912   XtRemoveInput(id);  
1913 #endif
1914  
1915   if (mb->flag == 1)
1916     {
1917       strcpy(mb->message,AbortMessage);
1918       socket_close(s);
1919       return 0;
1920     }
1921  
1922   return 1;
1923 }
1924 Boolean WWWAskForFileDestination(w,mb,filetype,binaryAllowed)
1925 Widget w;
1926 MessageBlock *mb;
1927 WWWFiletype *filetype;
1928 Boolean binaryAllowed;
1929 {
1930   Arg arglist[10];
1931   int n = 0;
1932   char *fname;
1933   MidasShell *ms = MidasGetShell(w);
1934   Widget fd = MidasTraceWidgetTree(ms->Widget,".WWWMain###WWWSaveFileAs");
1935   Widget binary = MidasTraceWidgetTree(fd,"...WWWSaveAsBinary");
1936  
1937   if (binaryAllowed)
1938     {
1939       XtSetArg(arglist[n],XmNsensitive,TRUE); n++;
1940       XtSetArg(arglist[n],XmNset,filetype->Binary); n++;
1941       XtSetValues(binary,arglist,n);
1942     }
1943   else XtSetSensitive(binary,FALSE);  
1944  
1945   MidasSetIngotString(fd,"file",filetype->UserData);
1946  
1947   fname = MidasQueryUser(fd);
1948  
1949   if (!fname || *fname == '\0')
1950     {
1951       strcpy(mb->message,AbortMessage);
1952       XtFree(fname);
1953       return FALSE;
1954     }
1955  
1956   filetype->UserData = (XtPointer) fname;
1957   if (binaryAllowed)
1958     {
1959       n = 0;
1960       XtSetArg(arglist[n],XmNset,&filetype->Binary); n++;
1961       XtGetValues(binary,arglist,n);
1962     }  
1963   return TRUE;
1964 }
1965  
1966 /*
1967  * Generate a history listing
1968  */
1969  
1970 /*
1971  *  Dump the hash table
1972  *  -------------------
1973  */
1974 static void WWWDumpTree(a,unique_id,state)
1975 WWWNode *a;
1976 int unique_id;
1977 XtPointer state;
1978 {
1979   WWWNode *child;
1980   WWWFile *pfile = a->file;
1981   char *p = WWWAsciiFile(pfile);
1982   int sublist = 0;
1983  
1984   a->id = unique_id;
1985  
1986   if (a == YouAreHereNode) SGMLHyperWrite(state,"<li><img pixmap=circle><a href=\"");
1987   else                     SGMLHyperWrite(state,"<li><a href=\"");
1988   SGMLHyperWrite(state,p);
1989   SGMLHyperWrite(state,"\">");
1990   SGMLHyperWrite(state,a->title);
1991   SGMLHyperWrite(state,"\n</a>");
1992   XtFree(p);    
1993  
1994   for (child = a->down; child; child = child->next)
1995     {
1996       WWWNode *b = child->down;
1997       if (!b) continue;
1998       b = b->parent;
1999       if (b->visited && b->id != unique_id)
2000         {
2001           if (!sublist++) SGMLHyperWrite(state,"<ul \"bulletType\"=linkedArrow>\n");
2002           WWWDumpTree(b,unique_id,state);
2003         }
2004     }
2005   if (sublist) SGMLHyperWrite(state,"</ul>\n");
2006 }
2007  
2008 static void WWWDumpHistory(state,first)
2009 XtPointer state;
2010 WWWNode *first;
2011 {
2012   static int unique_id = 0;
2013   WWWNode *a;
2014  
2015   unique_id++; /* Unique id for this scan */  
2016  
2017   for (a = first; a ; a = a->route)    
2018     {
2019       WWWNode *b = a->parent;
2020       if (b->id != unique_id && b != historyNode) WWWDumpTree(b,unique_id,state);
2021     }
2022 }
2023  
2024 static Widget WWWFetchDocumentHISTORY(w,file,mb)
2025 Widget w;
2026 WWWFile *file;
2027 MessageBlock *mb;
2028 {
2029   XtPointer state = SGMLHyperOpen(w);
2030  
2031   SGMLHyperWrite(state,"<title>History</title><nocache>\n");
2032   SGMLHyperWrite(state,"<h1>Documents visited this session</h1><ul \"bulletType\"=arrow>\n");
2033   WWWDumpHistory(state,FirstVisitedNode);
2034   SGMLHyperWrite(state,"</ul>");
2035   return SGMLHyperClose(state);
2036 }
2037 static Widget WWWFetchDocumentINGOT(w,file,mb)
2038 Widget w;
2039 WWWFile *file;
2040 MessageBlock *mb;
2041 {
2042   MidasOperand result;
2043  
2044   result = MidasGetIngot(w,XrmQuarkToString(file->file));
2045   return SGMLHyperSetText(w,(char *) result.Value.P);
2046 }
2047  
2048 /*
2049  * Open a telnet connection
2050  */
2051  
2052 Widget WWWFetchDocumentTELNET(w,file,mb)
2053 Widget w;
2054 WWWFile *file;
2055 MessageBlock *mb;
2056 {
2057    char buffer[256];
2058    char *ptr = XrmQuarkToString(file->node);
2059    char *legal = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_";
2060    char *cpt;
2061    char *hostname;
2062    char *username;
2063 #ifdef VMS
2064    int flags = 1; /* nowait */
2065    int iss;
2066    short len;
2067    char result[256];
2068    struct {
2069      unsigned short len;
2070      unsigned char  type;
2071      unsigned char  class;
2072      char           *ptr;
2073    } command_D = {0,14,1,0},
2074      device_D = {0,14,1,0},
2075      input_D = {9,14,1,"SYS$INPUT"},
2076      msg_D = {sizeof(buffer),14,1,buffer};
2077 #endif
2078    if (!ptr || *ptr == '\0')
2079      {
2080         strcpy(mb->message,"No node specifed for TELNET conection");
2081         mb->help_code = "telnet_no_node";
2082         return NULL;
2083      }  
2084    cpt = strchr(ptr,'@');
2085    hostname = XtMalloc(strlen(ptr)+1);
2086    username = XtMalloc(strlen(ptr)+1);
2087  
2088    if (cpt)
2089      {
2090        int i;
2091        cpt++;
2092        strcpy(hostname,cpt);
2093        for (i=0;i<=strlen(ptr);i++)
2094          if ((username[i] = ptr[i]) == '@') break;
2095        username[i] = '\0';
2096       }
2097    else strcpy(hostname,ptr);
2098  
2099    /*
2100     * Security fix, check for bad characters that can cause shell shock
2101     */
2102  
2103    if (strspn(hostname,legal) != strlen(hostname))
2104      {
2105         strcpy(mb->message,"Illegal character in telnet node specification");
2106         mb->help_code = "telnet_illegal_char";
2107         return NULL;
2108      }  
2109 #ifdef VMS
2110    /*
2111     * These commands are right for MULTINET, others?
2112     */
2113  
2114    sprintf(buffer,"*title : TELNET %s\n",XrmQuarkToString(file->node));
2115  
2116    iss = DECwTermPort(NULL,NULL,buffer,result,&len);
2117    if (iss != 1)
2118      {
2119         Lib$Sys_GetMsg(&iss,&len,&msg_D);
2120         buffer[len] = '\0';
2121         sprintf(mb->message,"Error creating DECTERM for telnet connection:<p>%s",buffer);
2122         mb->help_code = "vms_telnet_error";
2123         return NULL;
2124      }
2125  
2126    if (file->port) sprintf(buffer,"telnet/port=%d %s",file->port,XrmQuarkToString(file->node));
2127    else            sprintf(buffer,"telnet %s"        ,XrmQuarkToString(file->node));
2128  
2129    command_D.len = strlen(buffer);
2130    command_D.ptr = buffer;
2131  
2132    device_D.len = len;
2133    device_D.ptr = result;
2134  
2135    Sys$Alloc(&device_D,0,0,0,0);
2136    iss = Lib$Spawn(&command_D,&device_D,&device_D,&flags);
2137    Sys$Dalloc(&device_D,0);
2138    if (iss != 1)
2139      {
2140         Lib$Sys_GetMsg(&iss,&len,&msg_D);
2141         buffer[len] = '\0';
2142         sprintf(mb->message,"Error creating subprocess for telnet connection:<p>%s",buffer);
2143         mb->help_code = "vms_telnet_error";
2144  
2145         return NULL;
2146      }
2147 #else  
2148    if (file->port) sprintf(buffer,"xterm -e telnet %s %d &",hostname,file->port);
2149    else            sprintf(buffer,"xterm -e telnet %s &",hostname);
2150  
2151    system(buffer);
2152 #endif
2153  
2154    if (cpt)
2155     {
2156       if (file->port) sprintf(mb->message,"<nocache><title>Telnet %s</title><h1><img pixmap=telnet>Telnet %s</h1>Opened telnet connection to <b>%s port %d</b><p>Login Username <b>%s</b>",hostname,hostname,hostname,file->port,username);
2157       else sprintf(mb->message,"<nocache><title>Telnet %s</title><h1><img pixmap=telnet>Telnet %s</h1> Opened telnet connection to <b>%s</b><p>Login Username <b>%s</b>",hostname,hostname,hostname,username);
2158      }
2159    else
2160     {
2161       if (file->port) sprintf(mb->message,"<nocache><title>Telnet %s</title><h1><img pixmap=telnet>Telnet %s</h1> Opened telnet connection to <b>%s port %d</b>",hostname,hostname,hostname,file->port);
2162        else sprintf(mb->message,"<nocache><title>Telnet %s</title><h1><img pixmap=telnet>Telnet %s</h1> Opened telnet connection to <b>%s</b>",hostname,hostname,hostname);
2163      }
2164  
2165    XtFree(hostname);
2166    XtFree(username);
2167    return NULL;
2168 }
2169  /*
2170  * Fetch the document using appropriate protocol
2171  * ---------------------------------------------
2172  */
2173 static Widget WWWFetchDocumentByProtocol(w,infile,mb,title)
2174 Widget w;
2175 WWWFile *infile;
2176 MessageBlock *mb;
2177 char *title;
2178 {
2179   WWWFile temp;           /* When the port is 0 it is changed to the default port by  */
2180   WWWFile *file = &temp;  /* the fetch document routines, so need to make copy, or    */
2181   temp = *infile;         /* it causes problems with the hashing of nodes             */
2182  
2183   if      (file->protocol == httpProtocol   )
2184     {
2185       if (file->method == getMethod)
2186         {
2187           if (!strcmp(appResources.default_HTTP_protocol,"1.0"))
2188             return WWWFetchDocumentHTTP1(w,file,mb);
2189           else return WWWFetchDocumentHTTP(w,file,mb);
2190         }
2191       else if (file->method == postMethod) return WWWPostDocumentHTTP1(w,file,mb);
2192       else
2193         {
2194            sprintf(mb->message,"Unknown method %s",XrmQuarkToString(file->method));
2195            mb->help_code = "bad_method";
2196            return NULL;
2197         }  
2198     }
2199   else if (file->protocol == localProtocol  ) return WWWFetchDocumentLOCAL   (w,file,mb);
2200   else if (file->protocol == gopherProtocol ) return WWWFetchDocumentGOPHER  (w,file,mb,title);
2201   else if (file->protocol == ftpProtocol    ) return WWWFetchDocumentFTP     (w,file,mb);
2202   else if (file->protocol == fileProtocol   ) return WWWFetchDocumentFTP     (w,file,mb);
2203   else if (file->protocol == historyProtocol) return WWWFetchDocumentHISTORY (w,file,mb);
2204   else if (file->protocol == ingotProtocol  ) return WWWFetchDocumentINGOT   (w,file,mb);
2205   else if (file->protocol == telnetProtocol ) return WWWFetchDocumentTELNET  (w,file,mb);
2206   else if (file->protocol == waisProtocol   )
2207     {
2208       WWWFile actual;
2209       char *p =  WWWAsciiFile(file);
2210       char *new = p + 6; /* +6 to remove wais:/ */
2211  
2212       actual.protocol = httpProtocol;
2213       actual.node     = appResources.wais_gateway_node;
2214       actual.port     = appResources.wais_gateway_port;
2215       actual.file = XrmStringToQuark(new);
2216       actual.keyword = NULL;
2217       actual.anchor = NULL;
2218       actual.method = getMethod;
2219  
2220       XtFree(p);
2221  
2222       if (!strcmp(appResources.default_HTTP_protocol,"1.0"))
2223         return WWWFetchDocumentHTTP1(w,&actual,mb);
2224       else return WWWFetchDocumentHTTP(w,&actual,mb);
2225     }  
2226   else
2227     {
2228       WWWFile actual;  
2229       char *p =  WWWAsciiFile(file);
2230       char *new = XtMalloc(strlen(p)+2);
2231  
2232       strcpy(new,"/");
2233       strcat(new,p);
2234  
2235       actual.protocol = WWWGateway->protocol;
2236       actual.node     = WWWGateway->node;
2237       actual.port     = WWWGateway->port;
2238       actual.file = XrmStringToQuark(new);
2239       actual.keyword = NULL;
2240       actual.anchor = NULL;
2241       actual.method = getMethod;
2242  
2243       XtFree(p);
2244       XtFree(new);
2245  
2246       if (!strcmp(appResources.default_HTTP_protocol,"1.0"))
2247         return WWWFetchDocumentHTTP1(w,&actual,mb);
2248       else return WWWFetchDocumentHTTP(w,&actual,mb);
2249     }
2250 }  
2251 /*
2252  * Sets up handling for the popup message box
2253  */
2254 static void SetFlag(w,flag)
2255 Widget w;
2256 int *flag;  
2257 {
2258   *flag = 1;
2259 }  
2260 static void DrawMessage(w,mb,reason)
2261 Widget w;
2262 XmDrawingAreaCallbackStruct *reason;
2263 MessageBlock *mb;
2264 {
2265   char name[512];
2266   char *p;
2267   Boolean update = (reason == NULL);
2268   Display *dpy = XtDisplay(w);
2269   Window wind = XtWindow(w);
2270  
2271   if (mb->height == 0)
2272     {
2273       int ascent, descent, dir;
2274       XCharStruct overall;
2275  
2276       XTextExtents(appResources.font,mb->message,strlen(mb->message),&dir,&ascent,&descent,&overall);
2277       mb->height = ascent + descent;
2278       mb->ascent = ascent;
2279       mb->width =  overall.width;
2280     }
2281   if (!update)
2282     {
2283       p = WWWAsciiFile(mb->file);
2284       if (strlen(p) > 150) strcpy(p+140," ...");
2285  
2286       sprintf(name,"Getting %s",p);
2287       XDrawImageString(dpy,wind,messageGC,0,mb->ascent,name,strlen(name));
2288     }
2289   if (*mb->message != '@')
2290     {  
2291       p = mb->message;
2292       if (mb->halffull > 0) XClearArea(dpy,wind,5,mb->ascent+2*mb->height,400,mb->height,FALSE);
2293       mb->halffull = 0;
2294     }
2295   else if (mb->length == 0)
2296     {
2297       p = name;
2298       sprintf(name,"Read %d bytes",mb->read);
2299       if (mb->halffull > 0) XClearArea(dpy,wind,5,mb->ascent+2*mb->height,400,mb->height,FALSE);
2300       mb->halffull = 0;
2301     }  
2302   else
2303     {  
2304       int halffull = mb->read < mb->length ? 400*mb->read/mb->length : 400;
2305       int halfempty = 400 - halffull;
2306  
2307       sprintf(name,"Read %d of %d bytes",mb->read,mb->length);
2308       p = name;
2309  
2310       if (halffull)  XFillRectangle(dpy,wind,messageGC,5,mb->ascent+2*mb->height,halffull,mb->height);
2311       if (halfempty)
2312         {
2313           if (halffull < mb->halffull)
2314             XClearArea(dpy,wind,5+halffull,mb->ascent+2*mb->height,halfempty,mb->height,FALSE);
2315           XDrawRectangle(dpy,wind,messageGC,5,mb->ascent+2*mb->height,400,mb->height);
2316         }
2317       mb->halffull = halffull;
2318     }
2319   if (update)
2320     {
2321       int l = strlen(p);
2322       int width = XTextWidth(appResources.font,p,l);
2323       XDrawImageString(dpy,wind,messageGC,0,mb->ascent+mb->height,p,l);
2324       if (width < mb->width)
2325         XClearArea(dpy,wind,width,mb->height,mb->width - width,mb->height,FALSE);  
2326       mb->width = width;  
2327     }
2328   else XDrawImageString(dpy,wind,messageGC,0,mb->ascent+mb->height,p,strlen(p));
2329   XFlush(dpy);
2330 }
2331 static void SetMessage(mb)
2332 MessageBlock *mb;
2333 {
2334   if (mb->newMessage)
2335     {
2336       Widget w = mb->draw;
2337       mb->newMessage = FALSE;
2338       if (mb->height)
2339         {
2340           DrawMessage(w,mb,NULL);
2341         }
2342     }  
2343   mb->id = XtAppAddTimeOut(appl_context,appResources.update_time_out,(XtTimerCallbackProc) SetMessage,(XtPointer) mb);
2344 }
2345 static void PopupMessage(mb)
2346 MessageBlock *mb;
2347 {
2348   if (*mb->message != '\0')
2349     {
2350       SetMessage(mb);
2351       MidasPopup(mb->widget);
2352       XFlush(XtDisplay(mb->widget));
2353     }
2354   else
2355     {
2356       int time =  appResources.waiting_time_out;
2357       if (time<100) time = 100;
2358       mb->id = XtAppAddTimeOut(appl_context,time,(XtTimerCallbackProc) PopupMessage,(XtPointer) mb);    
2359     }
2360 }
2361 static Widget WWWFetchDocument(w,file,include,title)
2362 Widget w;
2363 WWWFile *file;
2364 List *include;
2365 char *title;
2366 {
2367   MessageBlock mb;
2368   MidasShell *ms = MidasGetShell(w);
2369   MidasOperand Popup;
2370   Widget result;
2371   Widget info, draw;
2372   static Boolean first = TRUE;
2373  
2374   Popup = MidasGetIngot(ms->Widget,"Popup");
2375  
2376   if (!Popup.Value.P)
2377     {
2378       ListItem *item;
2379       char *fixedMessage = (char *) XtMalloc(sizeof(mb.message));
2380  
2381       info = MidasFetch("WWWWaiting",ms->Widget);
2382  
2383       Popup.Value.P = (XtPointer) info;
2384       Popup.Type = "Widget";
2385       Popup.Dynamic = FALSE;
2386       MidasSetIngot(ms->Widget,"Popup",Popup);
2387     }
2388   else info = (Widget) Popup.Value.P;
2389  
2390   draw = MidasTraceWidgetTree(info,".XmDrawingArea");
2391   XtAddCallback(draw,XmNexposeCallback,(XtCallbackProc) DrawMessage,(XtPointer) &mb);
2392   XtAddCallback(info,XmNunmapCallback,(XtCallbackProc) SetFlag,(XtPointer) &mb.flag);
2393   XtAddGrab(info,TRUE,FALSE);
2394  
2395   mb.flag = -1;
2396   mb.file = file;
2397   mb.parent = ms->Widget;
2398   mb.help_code = "";
2399   strcpy(mb.message,"");
2400   mb.widget = info;
2401   mb.draw = draw;
2402   mb.height = 0;
2403   mb.width = 0;
2404   mb.halffull = 0;
2405  
2406   if (appResources.waiting_time_out < 0) mb.id = NULL;
2407   else
2408     {
2409       int timeout = first ? 10000 : appResources.waiting_time_out;        
2410       mb.id = XtAppAddTimeOut(appl_context,timeout,(XtTimerCallbackProc) PopupMessage,(XtPointer) &mb);
2411     }
2412   first = FALSE;  
2413  
2414   result = WWWFetchDocumentByProtocol(w,file,&mb,title);
2415  
2416   if (!result)
2417     {
2418       if (!*mb.help_code) result = SGMLHyperSetText(w,mb.message);
2419       else
2420         {
2421           XtPointer t = SGMLHyperOpen(w);
2422           SGMLHyperWrite(t,"<nocache><h2>Error:</h2>");
2423           SGMLHyperWrite(t,mb.message);
2424           SGMLHyperWrite(t,"<p>For more information see <a href=");
2425           SGMLHyperWrite(t,appResources.help);
2426           SGMLHyperWrite(t,mb.help_code);
2427           SGMLHyperWrite(t,".html>help</a>.");
2428           result = SGMLHyperClose(t);        
2429         }
2430     }
2431   else
2432     {
2433        ListItem *item;
2434  
2435        /*
2436         * Make sure the document is in the expected format, if not make it so
2437         *
2438         */
2439  
2440        if (strcmp(mb.help_code,""))
2441          {
2442            Widget child;
2443            Arg arglist[10];
2444            int n=0;      
2445  
2446            if (!strcmp(mb.help_code,"image"))  
2447              {
2448                GIFImage *image = (GIFImage *) result;
2449                XtSetArg(arglist[n],SGMLNimage, image); n++;
2450              }  
2451            else if (!strcmp(mb.help_code,"pixmap"))
2452              {
2453                Pixmap pm = (Pixmap) result;
2454                XtSetArg(arglist[n],SGMLNpixmap, pm); n++;
2455              }  
2456            XtSetArg(arglist[n],SGMLNdeleteImageWhenDone, TRUE); n++;
2457  
2458            result = XtCreateWidget("default",sGMLCompositeTextObjectClass,(Widget)w,NULL,0);
2459            child = XtCreateWidget("image",sGMLMarkerTextObjectClass,(Widget)result,arglist,n);
2460            SGMLCompositeTextInsertChild(child);
2461          }
2462  
2463        /*
2464         * If the document was successfully fetched, then maybe there are included documents
2465         * to deal with (currently this only supports IMG tags).
2466         */
2467        for (item = include->First; item && mb.flag != 1 ; item = item->Next)
2468          {
2469             Arg arglist[1];
2470             ImageLink *il = (ImageLink *) item->Pointer;
2471             WWWNode *node = il->Node;
2472  
2473             if (!node->icon && !node->picture)
2474               {
2475                 WWWFile *incf = node->file;
2476                 Widget xxx;
2477  
2478                 mb.help_code = "";
2479                 xxx = WWWFetchDocumentByProtocol(w,incf,&mb,NULL);
2480  
2481                 if (!xxx) printf("Error processing <img> tag: %s\n",mb.message);
2482                 else if (!strcmp(mb.help_code,"image") ) node->picture = (GIFImage *) xxx;
2483                 else if (!strcmp(mb.help_code,"pixmap")) node->icon    = (Pixmap) xxx;
2484                 else
2485                   {
2486                     char *name = WWWAsciiFile(incf);
2487                     printf("Document: %s\nnot in format suitable for <img> tag\n",name);
2488                     XtDestroyWidget(xxx);
2489                     XtFree(name);
2490                   }  
2491                 if (mb.flag == 1) break; /* User aborted document fetch */
2492               }  
2493             if (node->icon)
2494               {
2495                 unsigned int depth;
2496  
2497                 XtSetArg(arglist[0],SGMLNpixmap,node->icon);
2498                 XtSetValues(il->Widget,arglist,1);
2499  
2500                 XtSetArg(arglist[0],SGMLNpixmapDepth,&depth);
2501                 XtGetValues(il->Widget,arglist,1);
2502  
2503                 if (depth == 1)
2504                   {
2505                     XtSetArg(arglist[0],SGMLNoutline,0);
2506                     XtSetValues(il->Widget,arglist,1);
2507                   }
2508               }
2509             else if (node->picture)
2510               {
2511                 XtSetArg(arglist[0],SGMLNimage,node->picture);
2512                 XtSetValues(il->Widget,arglist,1);
2513               }  
2514          }
2515     }
2516   MidasEmptyList(include); /* Always tidy up after you have finished playing */
2517   XtRemoveCallback(info,XmNunmapCallback,(XtCallbackProc) SetFlag,(XtPointer) &mb.flag);
2518   XtRemoveCallback(draw,XmNexposeCallback,(XtCallbackProc) DrawMessage,(XtPointer) &mb);
2519   if (mb.id) XtRemoveTimeOut(mb.id);
2520  
2521   if (XtIsManaged(info))
2522     {
2523       /* We have sometimes seen problems where managing and the rapidly
2524        * unmanaging a window cause protocol errors (or worse) due to expose events
2525        * arriving after the window has been unmanaged. To overcome this problem
2526        * we now wait here until we receive the UnmapNotify event from the shell
2527        * We also ignore any expose events for the info or draw widget while waiting
2528        * since there is no point in processing them.  
2529        */
2530       Boolean unmap;
2531  
2532       XtUnmanageChild(info);
2533       XFlush(XtDisplay(info));
2534       for (unmap = TRUE; unmap ;) /* Wait until we get the UnMapNotify event */
2535         {
2536           XEvent event;
2537           XtAppNextEvent(appl_context,&event);
2538           if (event.type == Expose)
2539             {
2540               if (event.xexpose.window != XtWindow(info) &&
2541                   event.xexpose.window != XtWindow(draw))
2542                 XtDispatchEvent(&event);
2543             }
2544           else if (event.type == UnmapNotify)
2545             {
2546               if (event.xunmap.window == XtWindow(XtParent(info))) unmap = FALSE;
2547               XtDispatchEvent(&event);
2548             }
2549           else XtDispatchEvent(&event);
2550         }
2551     }
2552   XtRemoveGrab(info);
2553   return result;
2554 }
2555 static void WWWSetTitle(node,title)
2556 WWWNode *node;
2557 char *title;
2558 {
2559   node->parent->title = XtNewString(title);
2560   WWWUpdateHistory(node);
2561   /*
2562    * The logic (sic) here is that if you have visited a sub-node you have
2563    * also visited the node's parent, but NOT vice_versa
2564    */
2565   WWWVisitNode(node);
2566   if (node != node->parent) WWWVisitNode(node->parent);
2567 }
2568 static void WWWZapNode(w,node)
2569 Widget w;
2570 WWWNode *node;
2571 {
2572    int n=0;
2573 /*
2574  * Remove a cached document
2575  */
2576  
2577   node = node->parent;
2578  
2579   for (n = 0; n < node->num_widgets; n++)
2580     {
2581       Widget t = node->widgets[n];
2582       if (SGMLIsCompositeText(t) && XtParent(t) == w)
2583         {    
2584            WWWDetachWidgetFromNode(t,node);  
2585            XtDestroyWidget(t);
2586            break;  
2587         }
2588     }    
2589 }
2590 /*
2591  * Display a new node in a widget
2592  * ------------------------------
2593  */
2594 static Widget WWWDisplayNode(w,node,include,title)
2595 Widget w;
2596 WWWNode *node;
2597 List *include;
2598 char *title;
2599 {
2600    Widget new;
2601    int n=0;
2602 /*
2603  *  The document may already be cached in the widget?
2604  */    
2605  
2606   for (n = 0; n < node->num_widgets; n++)
2607     {
2608       Widget t = node->widgets[n];
2609       if (SGMLIsCompositeText(t) && XtParent(t) == w)
2610         {    
2611            SGMLHyperManageChild(t);
2612            return t;  
2613         }
2614     }    
2615 /*
2616  *  Otherwise we have to fetch it.
2617  */
2618  
2619   WWWlastnode = NULL;
2620   WWWparent = node;
2621  
2622   new = WWWFetchDocument(w,node->file,include,title);
2623   WWWAttachWidgetToNode(node,new);
2624 /*
2625  * Check for a redirection request.
2626  */
2627   if (node->redirect)
2628     {
2629       MidasOperand Temp;
2630       Temp.Value.P = (XtPointer) node->redirect;
2631       Temp.Dynamic = FALSE;
2632       Temp.Type = "WWWNode";
2633       MidasSetIngot(w,"WWWFileBase",Temp);
2634       new = WWWDisplayNode(w,node->redirect,include,title);
2635     }
2636  
2637   SGMLHyperManageChild(new);
2638   return new;
2639 }
2640 /*
2641  * Simplify a file by dealing with /. and /..
2642  * ----------------------------------------------------------------
2643  */
2644 static void SimplifyFile(filename)
2645 char *filename;
2646 {
2647     char * p;
2648     char * q;
2649     if (filename[0] && filename[1])     /* Bug fix 12 Mar 93 TBL */
2650      for(p=filename+2; *p; p++) {
2651         if (*p=='/') {
2652             if ((p[1]=='.') && (p[2]=='.') && (p[3]=='/' || !p[3] )) {
2653                 for (q=p-1; (q>=filename) && (*q!='/'); q--); /* prev slash */
2654                 if (q[0]=='/' && 0!=strncmp(q, "/../", 4)
2655                         &&!(q-1>filename && q[-1]=='/')) {
2656                     strcpy(q, p+3);     /* Remove  /xxx/..      */
2657                     if (!*filename) strcpy(filename, "/");
2658                     p = q-1;            /* Start again with prev slash  */
2659                 }               /*   xxx/.. leave it!   */
2660             } else if ((p[1]=='.') && (p[2]=='/' || !p[2])) {
2661                 strcpy(p, p+2);                 /* Remove a slash and a dot */
2662             }
2663         }
2664     }
2665 }
2666 /*
2667  *  parse a character file specification to create a file structure
2668  * ----------------------------------------------------------------
2669  */  
2670 static WWWFile *ParseFile(infile,def)
2671 char *infile;
2672 WWWFile *def;
2673 {
2674   char *p, *node, *oldfile, *newfile, *anchor, *protocol;
2675   char *directory, *port;
2676   WWWFile *result = CopyFile(def);
2677   char *file = XtNewString(infile);
2678   char *orig = file;
2679  
2680   result->anchor = NULL; /* NOT inherited from default */
2681   result->method = getMethod;
2682  
2683   /* strip leading and trailing space`s */  
2684  
2685   if (!file) file = "";
2686   else
2687     {
2688       for (; isspace(*file); ) file++;
2689       for (p = file + strlen(file); isspace(*--p); ) *p = '\0';
2690     }
2691  
2692   node = strstr(file,"/");
2693   protocol = strchr(file,':');
2694   if (protocol && (protocol < node || node == 0))
2695     {
2696       *protocol = '\0';
2697       result->protocol = XrmStringToQuark(file);
2698       if (result->protocol != def->protocol)
2699         {    
2700           result->node = NULL;
2701           result->port = 0;
2702         }  
2703       file = protocol + 1;
2704     }
2705   else protocol = NULL;
2706  
2707   if (strncmp(file,"//",2))
2708     {
2709       if (*file == '/' || protocol)
2710         {
2711           SimplifyFile(file);
2712  
2713           if (anchor = strchr(file,'#'))
2714           {
2715             *anchor++ = '\0';
2716             result->anchor = XrmStringToQuark(anchor);
2717           }
2718           result->file = XrmStringToQuark(file);
2719         }
2720       else
2721         {
2722           Boolean absolute;
2723           /*
2724            * This is kind of a mess, comes from trying to support the UW home page
2725            * without breaking access to local files from the command line. Maybe we shouldn't
2726            * worry about the UW home page?? Problem stems from fact that relative local addresses should
2727            * be left unchanges, while relative global addresses must be converted to absolute addresses.
2728            */
2729           if (result->protocol != localProtocol) oldfile = XtNewString(result->file?XrmQuarkToString(result->file):"/");
2730           else                                   oldfile = XtNewString(result->file?XrmQuarkToString(result->file):"");
2731           absolute = (*oldfile == '/');
2732  
2733           if (anchor = strchr(file,'#'))
2734           {
2735             *anchor++ = '\0';
2736             result->anchor = XrmStringToQuark(anchor);
2737           }
2738  
2739           for (;*file;file += 3)
2740             {
2741               char *p = strrchr(oldfile,'/');
2742               if (!p)  p = oldfile;
2743               *p = '\0';
2744               if (strncmp(file,"../",3)) break;
2745             }
2746           newfile = strcpy((char *) XtMalloc(strlen(oldfile) + strlen(file) + 2),oldfile);
2747           if ((absolute || *oldfile) && *file)strcat(newfile,"/");
2748           strcat(newfile,file);
2749           SimplifyFile(newfile);
2750  
2751           result->file = XrmStringToQuark(newfile);
2752           XtFree(newfile);
2753           XtFree(oldfile);
2754         }
2755     }
2756   else
2757     {
2758       char *p;
2759  
2760       node = file + 2;
2761  
2762       directory = strchr(node,'/');  
2763       if (directory) *(directory) = '\0';
2764  
2765       port = strchr(node,':');
2766       if (port)
2767         {
2768           *(port++) = '\0';
2769           result->port = atoi(port);  
2770         }
2771       else result->port = 0; /* Not inherited if node present */
2772  
2773       for (p=node; *p != '\0'; p++) if (isupper(*p)) *p = tolower(*p);  
2774       result->node = XrmStringToQuark(node);
2775  
2776       if (directory)
2777         {
2778           *(directory) = '/';
2779  
2780           if (anchor = strchr(directory,'#'))
2781             {
2782               *anchor++ = '\0';
2783               result->anchor = XrmStringToQuark(anchor);
2784             }
2785         }
2786  
2787       result->file = XrmStringToQuark(directory);
2788  
2789     }
2790  
2791   XtFree(orig);
2792   return result;
2793 }
2794 static void WWWFreeUnusedColors(id)
2795 XtIntervalId *id;
2796 {
2797   *id = NULL;
2798   fastAllocFreeUnusedColors();
2799 }
2800 static void WWWGet(ww,node,include,title)
2801 Widget ww;
2802 WWWNode *node;
2803 List *include;
2804 char *title;
2805 {
2806   static XtIntervalId id = NULL;
2807   WWWLink *link;
2808   Widget new;
2809  
2810   if (id) XtRemoveTimeOut(id);
2811  
2812   new = WWWDisplayNode(ww,node->parent,include,title);
2813   WWWToLink = node;
2814  
2815   id = XtAppAddTimeOut(appl_context,10000,(XtTimerCallbackProc) WWWFreeUnusedColors,(XtPointer) &id);
2816  
2817   if (strcmp(XtName(new),"gv_manager"))
2818     {
2819       if (node != node->parent)
2820         {
2821           WidgetList w = node->widgets;
2822           int nw = node->num_widgets;
2823  
2824           for (; nw-- > 0; w++)
2825             {
2826                Widget p;
2827                for (p = *w; p != NULL; p = XtParent(p))
2828                  if (p == ww)
2829                    {
2830                      SGMLHyperShowObject(ww,*w,FALSE);
2831                      return;
2832                    }
2833                SGMLHyperShowObject(ww,NULL,FALSE);
2834             }
2835         }
2836       else SGMLHyperShowObject(ww,NULL,FALSE);
2837     }
2838   else
2839     {  
2840       if (node != node->parent)
2841         {
2842           WidgetList w = node->widgets;
2843           int nw = node->num_widgets;
2844  
2845           for (; nw-- > 0; w++)
2846             {
2847                Widget p;
2848                for (p = *w; p != NULL; p = XtParent(p))
2849                  if (p == ww)
2850                    {
2851                      if (SGMLIsContainerText(*w))
2852                        {
2853                          Arg arglist[1];
2854                          char *page = XrmQuarkToString(node->file->anchor);
2855                          Widget gv;
2856  
2857                          XtSetArg(arglist[0],SGMLNchild,&gv);
2858                          XtGetValues(*w,arglist,1);
2859  
2860                          XtSetArg(arglist[0],XtNcurrentPage,atoi(page));
2861                          XtSetValues(gv,arglist,1);
2862  
2863                          XtSetArg(arglist[0],SGMLNmanaged,TRUE);
2864                          XtSetValues(*w,arglist,1);
2865                        }
2866                      else
2867                        {
2868                          Widget ct = *w;
2869                          for (; SGMLIsCompositeText(XtParent(ct)) ; ct = XtParent(ct))
2870                            {
2871                              Arg arglist[1];
2872  
2873                              XtSetArg(arglist[0],SGMLNmanaged,TRUE);
2874                              XtSetValues(ct,arglist,1);
2875                            }
2876                          SGMLHyperShowObject(ww,*w,FALSE);
2877                        }  
2878                      return;
2879                    }
2880             }
2881         }
2882       else
2883         {
2884           Widget Index = MidasTraceWidgetTree(new,".default");
2885           if (Index)
2886             {
2887               Arg arglist[1];
2888               XtSetArg(arglist[0],SGMLNmanaged,TRUE);
2889               XtSetValues(Index,arglist,1);
2890               SGMLHyperShowObject(ww,NULL,FALSE);
2891             }
2892         }
2893     }    
2894 }
2895 static void WWWRedirect(node1,node2)
2896 WWWNode *node1,*node2;
2897 {
2898    node1->redirect = node2;
2899 }
2900 static void WWWDeleteHistory(list)
2901 List *list;
2902 {
2903   ListItem *item = MidasFindItemInListPos(list,0);
2904   if (item) MidasRemoveItemFromList(list,item);
2905 }
2906 static void WWWAddHistory(list,title)
2907 List *list;
2908 char *title;
2909 {
2910   ListItem *item;
2911   WWWLink *link = XtNew(WWWLink);
2912   link->from = WWWFromLink;
2913   link->to = WWWToLink;
2914  
2915   if (WWWFromLink == DEADNODE)
2916     {
2917       item = MidasFindItemInListPos(list,0);
2918       if (item) link->from = ((WWWLink *) item->Pointer)->to;
2919       else      link->from = NULL;
2920     }
2921   if (WWWFromLink)
2922     {
2923       item = MidasAddItemToListPos(list,title,0);
2924       item->Pointer = (XtPointer) link;
2925     }  
2926  
2927   WWWFromLink = DEADNODE;
2928   WWWToLink = NULL;
2929 }
2930 static void WWWLinkHistory(node)
2931 WWWNode *node;
2932 {
2933   WWWFromLink = node;
2934 }
2935 static void WWWPut(w,stuff)
2936 Widget w;
2937 char *stuff;
2938 {
2939   Widget t = SGMLHyperSetText(w,stuff);
2940   SGMLHyperManageChild(t);
2941 }
2942 static void WWWDump(w,file)
2943 Widget w;
2944 char *file;
2945 {
2946   char *dump;
2947   FILE *out = fopen(file,"w");
2948   if (out==NULL) MidasError("Could not open file %s",file);
2949  
2950   dump = SGMLHyperGetText(w,TRUE);
2951   fputs(dump,out);
2952   fclose(out);
2953   XtFree(dump);
2954 }
2955 /*
2956  * Called whenever a new node is created as the result of some user initiated action.
2957  * Examples includee typing a keyword in the ISINDEX area, clicking on an ISMAP image
2958  * or filling in a form.
2959  *
2960  */
2961 static MidasOperand WWWCreateNodeFromKeyword(parent,reference,keyword,method)
2962 WWWNode *parent;
2963 WWWNode *reference;
2964 char *keyword;
2965 char *method;
2966 {
2967   MidasOperand Temp;
2968   WWWNode *src, *dest, *p;
2969   WWWFile *new = CopyFile(reference->file);
2970   WWWFile *pnew;
2971   XrmQuark Method = XrmStringToQuark(method);
2972   char buffer[12], *q;
2973  
2974   parent = parent->parent; /* Make sure we really have parent */
2975   pnew = CopyFile(parent->file);
2976  
2977   sprintf(buffer,"@@%d",WWWnextid++);  
2978   pnew->anchor = XrmStringToQuark(buffer);
2979  
2980   src = WWWCreateNode(pnew,parent);
2981  
2982   if (Method == getMethod)
2983     {
2984       char *buffer = (char *) XtMalloc(strlen(XrmQuarkToString(new->file)) + strlen(keyword) + 5);
2985       strcpy(buffer,XrmQuarkToString(new->file));
2986       if (q = strchr(buffer,'?')) *q = '\0';
2987       strcat(buffer,"?");
2988       strcat(buffer,keyword);
2989       new->file = XrmStringToQuark(buffer);
2990       new->anchor = NULL;
2991       XtFree(buffer);
2992     }
2993   else
2994     {
2995       new->keyword = XrmStringToQuark(keyword); /* bad idea, creates large number of quarks */
2996       new->anchor = NULL;
2997       new->method = Method;
2998     }
2999   dest = WWWCreateNodeAndParent(new);
3000   FreeFile(new);
3001   FreeFile(pnew);
3002  
3003   src->next = parent->down;
3004   parent->down = src;  
3005   src->down = dest;
3006  
3007   p = dest->up;
3008   for (; p != NULL; p = p->chain) if (p==src) break;
3009   if (p == NULL)
3010     {
3011       src->chain = dest->up;
3012       dest->up = src;
3013     }
3014  
3015   Temp.Value.P = (XtPointer) src;
3016   Temp.Dynamic = FALSE;
3017   Temp.Type = "WWWNode";
3018  
3019   return Temp;
3020 }
3021 /*
3022  *  Called when a new anchor is created
3023  *  -----------------------------------
3024  */
3025 static void WWWCreateAnchor(w,doc)
3026 Widget w;
3027 WWWFile *doc;
3028 {
3029   char *href, *name, buffer[12];
3030   WWWNode *src  = NULL;
3031   WWWNode *dest = NULL;
3032   WWWFile *here = CopyFile(doc);
3033  
3034   Arg arglist[10];
3035   int n=0;
3036  
3037   XtSetArg(arglist[n],SGMLNhref,&href); n++;
3038   XtSetArg(arglist[n],SGMLNname,&name); n++;
3039   XtGetValues(w,arglist,n);
3040  
3041   /*
3042    *  Create a source node, if the node has no name then make
3043    *  one up.
3044    */
3045  
3046   if (!name || !(*name))
3047     {
3048       name = buffer;
3049       sprintf(buffer,"@@%d",WWWnextid++);  
3050     }
3051  
3052   here->anchor = XrmStringToQuark(name);
3053  
3054   /*
3055    *  Problem .. what if multiple named anchors in document have same name.
3056    *  solution....make up a new name.
3057    */  
3058  
3059   src = WWWFindNode(here);
3060   if (src)
3061     {
3062       Widget parent = XtParent(w);
3063       Widget *ww = src->widgets;
3064       int nw = src->num_widgets;
3065  
3066       for ( ; !XtIsWidget(parent) ; parent = XtParent(parent) );
3067       for ( ; nw-- ; ww++)
3068         {
3069           Widget p2 = XtParent(*ww);
3070           for ( ; !XtIsWidget(p2) ; p2 = XtParent(p2) );
3071           if (p2 == parent)
3072             {
3073               sprintf(buffer,"@@%d",WWWnextid++);  
3074               here->anchor = XrmStringToQuark(buffer);
3075               break;
3076             }
3077         }  
3078     }
3079  
3080   src = WWWCreateNode(here,WWWparent);
3081   FreeFile(here);
3082  
3083   if (!WWWparent->down) WWWparent->down = src;  
3084   if (WWWlastnode) WWWlastnode->next = src;
3085   src->prev = WWWlastnode;
3086   WWWlastnode = src;
3087   WWWAttachWidgetToNode(src,w);
3088  
3089   n=0;
3090   XtSetArg(arglist[n],SGMLNuserdata,(XtPointer) src); n++;
3091   XtSetValues(w,arglist,n);
3092  
3093   /*
3094    * If this node points somewhere then create a destination node
3095    */  
3096  
3097   if (href && *href)
3098     {
3099       WWWNode *p;  
3100       here = ParseFile(href,doc);
3101  
3102       dest = WWWCreateNodeAndParent(here);
3103  
3104       FreeFile(here);
3105  
3106       src->down = dest;
3107  
3108       p = dest->up;
3109       for (; p != NULL; p  = p->chain) if (p==src) break;
3110       if (p == NULL)
3111         {
3112           src->chain = dest->up;
3113           dest->up = src;
3114         }
3115       if (dest->visited) WWWBeenThere(w,TRUE);
3116       else               WWWBeenThere(w,FALSE);
3117     }
3118 }
3119 /*
3120  * It should be possible to do this directly from MIDAS so this routine
3121  * should go away as soon as it actually is.
3122  */
3123 static void WWWCopyBackgroundColor(src,dest)
3124 Widget src;
3125 Widget dest;
3126 {
3127   Pixel bg;
3128   Arg arglist[1];
3129  
3130   XtSetArg(arglist[0],XmNbackground,&bg);
3131   XtGetValues(src,arglist,1);
3132   XtSetArg(arglist[0],XmNbackground,bg);
3133   XtSetValues(dest,arglist,1);
3134 }
3135 static MidasOperand WWWSource(w)
3136 Widget w;
3137 {
3138   MidasOperand Temp;
3139   char *dump = SGMLHyperGetText(w,TRUE);
3140  
3141   Temp.Value.P = dump;
3142   Temp.Dynamic = TRUE;
3143   Temp.Type = MString;
3144  
3145   return Temp;
3146 }
3147 static MidasOperand WWWBack(list)
3148 List *list;
3149 {
3150   MidasOperand Temp;
3151  
3152   ListItem *item = MidasFindItemInListPos(list,0);
3153   WWWLink *link = (WWWLink *) item?item->Pointer:NULL;
3154   Temp.Value.P = link ? (XtPointer) link->from : NULL;
3155  
3156   Temp.Dynamic = FALSE;
3157   Temp.Type = "WWWNode";
3158  
3159   return Temp;
3160 }
3161 static MidasOperand WWWPrev(node)
3162 WWWNode *node;
3163 {
3164   MidasOperand Temp;
3165  
3166   Temp.Value.P = node ? (XtPointer) node->prev : NULL;
3167   Temp.Dynamic = FALSE;
3168   Temp.Type = "WWWNode";
3169  
3170   return Temp;
3171  
3172 }
3173 static MidasOperand WWWNext(node)
3174 WWWNode *node;
3175 {
3176   MidasOperand Temp;
3177  
3178   Temp.Value.P = node ? (XtPointer) node->next : NULL;
3179   Temp.Dynamic = FALSE;
3180   Temp.Type = "WWWNode";
3181  
3182   return Temp;
3183  
3184 }
3185 static MidasOperand WWWParent(node)
3186 WWWNode *node;
3187 {
3188   MidasOperand Temp;
3189  
3190   Temp.Value.P = node ? (XtPointer) node->parent : NULL;
3191   Temp.Dynamic = FALSE;
3192   Temp.Type = "WWWNode";
3193  
3194   return Temp;
3195  
3196 }
3197 static MidasOperand WWWDest(node)
3198 WWWNode *node;
3199 {
3200   MidasOperand Temp;
3201  
3202   Temp.Value.P = node ? (XtPointer) node->down : NULL;
3203   Temp.Dynamic = FALSE;
3204   Temp.Type = "WWWNode";
3205  
3206   return Temp;
3207 }
3208 static MidasOperand WWWRedirectLookup(node)
3209 WWWNode *node;
3210 {
3211   MidasOperand Temp;
3212  
3213   for ( ; node->redirect; node = node->redirect);
3214  
3215   Temp.Value.P = (XtPointer) node;
3216   Temp.Dynamic = FALSE;
3217   Temp.Type = "WWWNode";
3218  
3219   return Temp;
3220 }
3221 static MidasOperand WWWGetNode(w)
3222 Widget w;
3223 {
3224   MidasOperand Temp;
3225   Arg arglist[10];
3226   int n=0;
3227  
3228   XtSetArg(arglist[n],SGMLNuserdata,&Temp.Value.P); n++;
3229   XtGetValues(w,arglist,n);
3230  
3231   Temp.Dynamic = FALSE;
3232   Temp.Type = "WWWNode";
3233  
3234   return Temp;
3235 }
3236 static MidasOperand WWWParse(file,def)
3237 char *file;
3238 WWWFile *def;
3239 {
3240   MidasOperand Temp;
3241  
3242   WWWFile *pfile = ParseFile(file,def);
3243  
3244   Temp.Value.P = (XtPointer) pfile;
3245   Temp.Dynamic = TRUE;
3246   Temp.Type = "WWWFile";
3247  
3248   return Temp;
3249 }
3250  
3251 /*-----------------------------------------------------------------------*/
3252 /* convert a string to lower case                                        */
3253 /*-----------------------------------------------------------------------*/
3254  
3255 static void lowcase(p)
3256 register char *p;
3257 {
3258