1 #include "mrilib.h"
2 #include "xutil.h"
3
4 #ifdef DONT_USE_HTMLWIN /*-------------------- dummy routines ------------------------------*/
5
new_MCW_htmlwin(Widget w,char * m,void_func * kf,XtPointer kd,MCW_action_items * mai,int nact)6 MCW_htmlwin * new_MCW_htmlwin( Widget w, char *m, void_func *kf, XtPointer kd ,
7 MCW_action_items *mai, int nact){ return NULL ; }
MCW_htmlwin_alter(MCW_htmlwin * hw,char * mmm)8 void MCW_htmlwin_alter( MCW_htmlwin *hw, char *mmm ){ return ; }
9
convert_text_to_html(char * txt)10 char * convert_text_to_html( char *txt ){ return txt; } /* 06 May 2015 */
11
12 #else /*---------- non-dummy routines --------------------*/
13
14 #include "XmHTML/XmHTML.h"
15 #include "debugtrace.h" /* 12 Mar 2001 */
16
17 extern int afni_uses_selenium(void) ;
18 extern int selenium_open_webpage(char *) ;
19
20 XmImageConfig *_xmimage_cfg = NULL ;
21
22
23 static void MCW_htmlwinkill_CB( Widget , XtPointer , XtPointer ) ;
24 static void MCW_htmlwin_CB ( Widget , XtPointer , XtPointer ) ;
25
26 static MCW_action_item HWIN_act[] = {
27 { "Quit" , MCW_htmlwin_CB , NULL , NULL , "Close window" , 0 } ,
28 } ;
29
30 /*----------------------------------------------------------------------------*/
31 /* Not sure what this is for! */
32
armCB(Widget w,XtPointer arg1,XmAnyCallbackStruct * href_data)33 static void armCB( Widget w, XtPointer arg1, XmAnyCallbackStruct *href_data)
34 {
35 XButtonEvent *event;
36
37 event = (XButtonEvent*)href_data->event ;
38 event->window = DefaultRootWindow(XtDisplay(w)) ;
39 event->root = DefaultRootWindow(XtDisplay(w)) ;
40 event->subwindow = DefaultRootWindow(XtDisplay(w)) ;
41 event->send_event = True ;
42
43 XUngrabPointer( XtDisplay(w) , CurrentTime ) ;
44 XSendEvent( XtDisplay(w) , DefaultRootWindow(XtDisplay(w)) ,
45 True , ButtonPressMask , (XEvent *)event ) ;
46 XFlush(XtDisplay(w)) ;
47 }
48
49 /*----------------------------------------------------------------------------*/
50 /* For dealing with clicks on links (anchors). */
51
anchorCB(Widget widget,XtPointer client_data,XmHTMLAnchorCallbackStruct * cbs)52 static void anchorCB( Widget widget, XtPointer client_data,
53 XmHTMLAnchorCallbackStruct *cbs )
54 {
55 ENTRY("anchorCB") ;
56
57 switch( cbs->url_type ){
58
59 case ANCHOR_JUMP: /* internal jumps */
60 cbs->doit = True ; cbs->visited = True ;
61 break ;
62
63 default:
64 case ANCHOR_HTTP:{ /* external http links */
65 static char *webb=NULL ; static int first=1 ;
66 if( first == 1 ){ webb = GetAfniWebBrowser() ; first = 2 ; }
67 if( afni_uses_selenium() ) {
68 selenium_open_webpage(cbs->href);
69 }
70 else{
71 if( webb != NULL ){
72 char *cmd = (char *)malloc( strlen(webb) + strlen(cbs->href) + 32 ) ;
73 sprintf( cmd , "%s '%s' &" , webb , cbs->href ) ;
74 system( cmd ) ; free( cmd ) ;
75 } else if( first == 2 ){
76 INFO_message("No command line Web browser program found in your path.") ;
77 ININFO_message("Set environment variable AFNI_WEB_BROWSER to the full" ) ;
78 ININFO_message("pathname of a browser than can be started from the Unix") ;
79 ININFO_message("command line -- e.g., '/usr/local/bin/mozilla'" ) ;
80 first = 0 ;
81 }
82 }
83 }
84 break ;
85 }
86
87 EXRETURN ;
88 }
89
90 /*----------------------------------------------------------------------------*/
91
92 #undef SSUB
93 #define SSUB(a,b) \
94 do{ char *qqq = string_substitute( mmm , (a) , (b) ) ; \
95 if( qqq != NULL ){ free(mmm) ; mmm = qqq ; } \
96 } while(0)
97
98 #undef NSUB
99 #define NSUB(a) SSUB((a),"\0")
100
101 #undef UOSUB
102 #undef UXSUB
103 #define UOSUB(a) SSUB((a),"<u>")
104 #define UXSUB(a) SSUB((a),"</u>")
105
unfontize(char * msg)106 static char * unfontize( char *msg )
107 {
108 char *mmm , *qqq ;
109
110 if( msg == NULL || *msg == '\0' ) return msg ;
111
112 mmm = strdup(msg) ;
113 NSUB("<small>"); NSUB("</small>");
114 UOSUB("<big>") ; UXSUB("</big>") ;
115 UOSUB("<h1>") ; UXSUB("</h1>") ;
116 UOSUB("<h2>") ; UXSUB("</h2>") ;
117 UOSUB("<h3>") ; UXSUB("</h3>") ;
118 UOSUB("<h4>") ; UXSUB("</h4>") ;
119 UOSUB("<h5>") ; UXSUB("</h5>") ;
120 UOSUB("<h6>") ; UXSUB("</h6>") ;
121 UOSUB("<i>") ; UXSUB("</i>") ;
122 UOSUB("<b>") ; UXSUB("</b>") ;
123 UOSUB("<em>") ; UXSUB("</em>") ;
124 UOSUB("<tt>") ; UXSUB("</tt>") ;
125 SSUB("<font ","<u ") ; UXSUB("</font>") ;
126 SSUB(" ",".") ;
127
128 if( strcmp(mmm,msg) == 0 ){ free(mmm) ; mmm = msg ; }
129 return mmm ;
130 }
131
132 /*----------------------------------------------------------------------------*/
133 /* Mangle a message to be good HTML for display */
134
htmlize(char * msg)135 static char * htmlize( char *msg )
136 {
137 char *mmm=NULL ; int dounf ;
138
139 ENTRY("htmlize") ;
140
141 if( msg == NULL || *msg == '\0' ){
142 msg = strdup("<html><body><p>Dummy\n<p>Message</body></html>") ;
143 RETURN(msg) ;
144 }
145
146 if( strncmp(msg,"<html>",6) == 0 ) RETURN(msg) ; /* already HTML format */
147
148 if( strncmp(msg,"file:",5) == 0 ){ /* read file */
149 char *qqq=AFNI_suck_file(msg+5) ; char *dnam , *repl , *targ ;
150 if( qqq != NULL )
151 mmm = qqq ;
152 else
153 mmm = strdup("<html><body><h1>Dummy</h1><h2>Message</h2></body></html>");
154
155 /* edit file to add base directory to all '<img src=' filenames */
156
157 if( strchr(msg+5,'/') != NULL && strstr(mmm,"<img src=") != NULL ){
158 dnam = strdup(msg+5) ; qqq = THD_trailname(dnam,0) ;
159 if( qqq != NULL && qqq != dnam ){
160 *qqq = '\0' ; /* dnam is now base directory */
161 repl = (char *)malloc(sizeof(char)*(strlen(dnam)+16)) ;
162 targ = "<img src=\"" ; /* string to search for */
163 sprintf( repl , "%s%s" , targ , dnam ) ; /* replacement string */
164 qqq = string_substitute( mmm , targ , repl ) ; /* do the work */
165 if( qqq != NULL ){ free(mmm) ; mmm = qqq ; }
166 free(repl) ;
167 }
168 free(dnam) ;
169 }
170
171 } else if( strncmp(msg,"wami:",5) == 0 ){ /* wami love */
172 mmm = (char *)malloc(sizeof(char)*(strlen(msg)+64)) ;
173 strcpy(mmm,"<html><body>\n") ;
174 strcat(mmm,msg+strlen("wami:")) ;
175 strcat(mmm,"\n</body></html>") ;
176 } else { /* add HTML stuff */
177 mmm = (char *)malloc(sizeof(char)*(strlen(msg)+64)) ;
178 strcpy(mmm,"<html><body>\n") ;
179 strcat(mmm,msg) ;
180 strcat(mmm,"\n</body></html>") ;
181 }
182
183 #ifdef UNFONTIZE_HTMLWIN
184 dounf = 1 ;
185 #else
186 dounf = AFNI_yesenv("AFNI_UNFONTIZE_HTML") ;
187 #endif
188 if( dounf ){
189 char *qqq = unfontize(mmm) ;
190 if( qqq != mmm ){ free(mmm) ; mmm = qqq ; }
191 }
192
193 RETURN(mmm) ;
194 }
195
196 /*----------------------------------------------------------------------------*/
197 /* This is a callback to deal with some refresh problems
198 that should have been handled by the XmHTML library.
199 For now this call is not needed. It looks like the patching
200 of XmHTML did the trick ZSS March 2012 */
201
RefreshHTML_AtEvent(Widget w,XtPointer client_data,XEvent * ev,RwcBoolean * continue_to_dispatch)202 void RefreshHTML_AtEvent( Widget w , XtPointer client_data ,
203 XEvent * ev , RwcBoolean * continue_to_dispatch )
204 {
205 #if 0
206 XmHTMLRefresh(client_data);
207 #endif
208 }
209
210 /*----------------------------------------------------------------------------*/
211 /* Open a window with an XmHTML widget containing msg.
212 If msg starts with "file:", then it indicates a file to read and display.
213 Otherwise, it is the content of the page directly.
214 *//*--------------------------------------------------------------------------*/
215
new_MCW_htmlwin(Widget wpar,char * msg,void_func * kill_func,XtPointer kill_data,MCW_action_item * umai,int nact)216 MCW_htmlwin * new_MCW_htmlwin( Widget wpar , char *msg ,
217 void_func *kill_func , XtPointer kill_data,
218 MCW_action_item *umai, int nact)
219
220 {
221 int wx,hy,xx,yy , xp,yp , scr_width,scr_height , xr,yr , xpr,ypr , ii ;
222 int swid , shi ;
223 Position xroot , yroot ;
224 Screen *scr ;
225 Arg wa[64] ; int na ; Widget ws ;
226 char *wtype = "help" ;
227 MCW_htmlwin *hw ;
228 char *mymsg ;
229 MCW_action_item *mai=NULL;
230 static Pixel afg=(Pixel)0 , afgv=(Pixel)0 ;
231
232 ENTRY("new_MCW_htmlwin") ;
233
234 /*-- sanity check --*/
235
236 if( wpar == NULL || !XtIsRealized(wpar) || msg == NULL || *msg == '\0' )
237 RETURN(NULL) ;
238
239
240 /*-- set position based on parent and screen geometry --*/
241
242 MCW_widget_geom( wpar , &wx,&hy,&xx,&yy ) ; /* geometry of parent */
243 XtTranslateCoords( wpar, 0,0, &xroot,&yroot ) ; /* root coords */
244 xr = (int) xroot ; yr = (int) yroot ;
245
246 scr = XtScreen(wpar) ;
247 scr_width = WidthOfScreen(scr) ;
248 scr_height = HeightOfScreen(scr) ;
249
250 xp = xx+8 ; xpr = xr+8 ;
251 if( xpr+50 > scr_width ){ xp -= 100 ; xpr -= 100 ; } /* too right */
252 else if( xpr+10 < 0 ) { xpr = xp = 1 ; } /* too left */
253
254 yp = yy+hy+8 ; ypr = yr+hy+8 ;
255 if( ypr+50 > scr_height ){ yp = yy-8 ; ypr = yr-100 ;} /* too down */
256 else if( ypr+10 < 0 ) { ypr = yp = 1 ; } /* too up */
257
258 /*-- create a popup shell --*/
259
260 hw = myXtNew(MCW_htmlwin) ;
261 hw->kill_func = kill_func ;
262 hw->kill_data = kill_data ;
263
264 hw->wshell = XtVaCreatePopupShell(
265 wtype , xmDialogShellWidgetClass , wpar ,
266 XmNx , xpr ,
267 XmNy , ypr ,
268 XmNborderWidth , 0 ,
269 XmNborderColor , 0 ,
270 XmNinitialResourcesPersistent , False ,
271 NULL ) ;
272
273 XmAddWMProtocolCallback(
274 hw->wshell ,
275 XmInternAtom( XtDisplay(hw->wshell) , "WM_DELETE_WINDOW" , False ) ,
276 MCW_htmlwinkill_CB , (XtPointer)hw ) ;
277
278 /*-- create a form to hold everything else --*/
279
280 hw->wtop = XtVaCreateWidget(
281 wtype , xmFormWidgetClass , hw->wshell ,
282 XmNborderWidth , 0 ,
283 XmNborderColor , 0 ,
284 XmNtraversalOn , True ,
285 XmNinitialResourcesPersistent , False ,
286 NULL ) ;
287
288 /*-- create action area --*/
289
290 if (!umai) { /* default action item */
291 mai = HWIN_act; nact = 1;
292 for( ii=0 ; ii < nact ; ii++ ){
293 mai[ii].data = (XtPointer)hw ;
294 mai[ii].make_red = 0 ;
295 }
296 mai[nact-1].make_red = 1 ;
297 } else {
298 /* use user preference */
299 mai = umai;
300 }
301
302 hw->wactar = MCW_action_area( hw->wtop , mai , nact ) ;
303
304 XtVaSetValues( hw->wactar ,
305 XmNleftAttachment , XmATTACH_FORM ,
306 XmNrightAttachment, XmATTACH_FORM ,
307 XmNtopAttachment , XmATTACH_FORM ,
308 XmNtopOffset , 4 ,
309 NULL ) ;
310
311 /*-- frame to hold HTML widget --*/
312
313 hw->wframe = XtVaCreateManagedWidget(
314 wtype , xmFrameWidgetClass, hw->wtop ,
315 XmNtopAttachment , XmATTACH_WIDGET ,
316 XmNtopWidget , hw->wactar ,
317 XmNtopOffset , 5 ,
318 XmNleftAttachment , XmATTACH_FORM,
319 XmNleftOffset , 2 ,
320 XmNbottomAttachment, XmATTACH_FORM,
321 XmNbottomOffset , 2 ,
322 XmNrightAttachment , XmATTACH_FORM,
323 XmNrightOffset , 2,
324 XmNshadowType , XmSHADOW_IN,
325 XmNshadowThickness , 5 ,
326 NULL ) ;
327
328 /*---- create HTML area ----*/
329
330 if( afg == (Pixel)0 ){
331 afg = XmHTMLAllocColor( hw->wtop, "#ffdd00", WhitePixelOfScreen(XtScreen(hw->wtop)) ) ;
332 afgv = XmHTMLAllocColor( hw->wtop, "#ffcc99", WhitePixelOfScreen(XtScreen(hw->wtop)) ) ;
333 }
334
335 swid = WidthOfScreen(XtScreen(wpar)) - 222 ; if( swid > 799 ) swid = 799 ;
336 shi = HeightOfScreen(XtScreen(wpar)) - 222 ; if( shi > 899 ) shi = 899 ;
337
338 mymsg = htmlize(msg) ; /* edit the text */
339
340 STATUS("create HTML widget") ;
341
342 hw->whtml = XtVaCreateManagedWidget(
343 wtype , xmHTMLWidgetClass , hw->wframe ,
344 XmNmarginWidth , 8 ,
345 XmNmarginHeight , 8 ,
346 XmNwidth , swid ,
347 XmNheight , shi ,
348 XmNvalue , mymsg,
349 XmNfontFamily , "adobe-helvetica-normal-*" ,
350 XmNfontFamilyFixed , "adobe-courier-normal-*" ,
351 XmNfontSizeFixedList , "14,10" ,
352 XmNanchorButtons , False ,
353 XmNanchorForeground , afg ,
354 XmNanchorVisitedForeground , afgv ,
355 NULL ) ;
356 XtAddCallback( hw->whtml, XmNactivateCallback, (XtCallbackProc)anchorCB, NULL ) ;
357 XtAddCallback( hw->whtml, XmNarmCallback , (XtCallbackProc)armCB , NULL ) ;
358
359 #if 0 /* This was needed to deal with some refreshing problems when the scrollbar
360 was moved. The patch in XmHTML seems to have do the trick. These are
361 left here should we need to reuse them someday */
362 XtInsertEventHandler( hw->whtml , /* notify when */
363 LeaveWindowMask , /* pointer leaves */
364 FALSE , /* this window */
365 RefreshHTML_AtEvent,
366 (XtPointer) hw->whtml ,
367 XtListTail ) ; /* last in queue */
368 XtInsertEventHandler( hw->whtml , /* notify when */
369 EnterWindowMask , /* pointer leaves */
370 FALSE , /* this window */
371 RefreshHTML_AtEvent,
372 (XtPointer) hw->whtml ,
373 XtListTail ) ; /* last in queue */
374 #endif
375
376 STATUS("manage HTML widgets") ;
377
378 XtManageChild( hw->wtop ) ;
379
380 #if 0
381 XtVaSetValues( hw->wshell , XmNwidth,swid , XmNheight,shi , NULL ) ;
382 #endif
383
384 /*--- open the window for viewing, and place it on the screen ---*/
385
386 XtPopup( hw->wshell , XtGrabNone ) ; RWC_sleep(16) ;
387
388 RWC_visibilize_widget( hw->wshell ) ;
389
390 RWC_xineramize( XtDisplay(hw->wshell) ,
391 xpr,ypr,swid,shi , &xpr,&ypr ) ;
392
393 XtVaSetValues( hw->wshell, XmNx,xpr , XmNy,ypr , NULL ) ;
394
395 hw->shell_width = swid ; hw->shell_height = shi ;
396
397 NORMAL_cursorize( hw->wshell ) ;
398
399 #if 0
400 XmHTMLTextSetString( hw->whtml , mymsg ) ;
401 #endif
402
403 if( mymsg != msg ) free(mymsg) ; /* toss the trash */
404
405 STATUS("force HTML redisplay") ;
406
407 RWC_sleep(66) ; XmHTMLRedisplay( hw->whtml ) ; /* force redraw to be safe */
408
409 RETURN(hw) ;
410 }
411
412 /*-------------------------------------------------------------------------*/
413 /* replace the contents of an MCW_htmlwin (not tested) */
414
MCW_htmlwin_alter(MCW_htmlwin * hw,char * mmm)415 void MCW_htmlwin_alter( MCW_htmlwin *hw , char *mmm )
416 {
417 int swid , shi ;
418 char *msg ;
419
420 ENTRY("MCW_htmlwin_alter") ;
421
422 if( hw == NULL || mmm == NULL || *mmm == '\0' ) EXRETURN ;
423 msg = htmlize(mmm) ;
424
425 XmHTMLTextSetString( hw->whtml , msg ) ;
426
427 if( msg != mmm ) free(msg) ;
428
429 EXRETURN ;
430 }
431
432 /*-------------------------------------------------------------------------*/
433 /* Called when the user presses an action button */
434
MCW_htmlwin_CB(Widget w,XtPointer client_data,XtPointer call_data)435 static void MCW_htmlwin_CB( Widget w, XtPointer client_data, XtPointer call_data )
436 {
437 MCW_htmlwin *hw = (MCW_htmlwin *)client_data ;
438 char *wname = XtName(w) ;
439
440 ENTRY("MCW_htmlwin_CB") ;
441
442 if( client_data == NULL ) EXRETURN ;
443
444 if( strcmp(wname,"Quit") == 0 ){
445 if( hw->kill_func != NULL )
446 AFNI_CALL_VOID_1ARG( hw->kill_func , XtPointer , hw->kill_data ) ;
447 XtDestroyWidget( hw->wshell ) ;
448 myXtFree( hw ) ;
449 EXRETURN ;
450 }
451
452 EXRETURN ;
453 }
454
455 /*-------------------------------------------------------------------------*/
456 /* Called when the user kills the window via the window manager controls */
457
MCW_htmlwinkill_CB(Widget w,XtPointer client_data,XtPointer call_data)458 static void MCW_htmlwinkill_CB( Widget w, XtPointer client_data, XtPointer call_data )
459 {
460 MCW_htmlwin *hw = (MCW_htmlwin *) client_data ;
461
462 ENTRY("MCW_htmlwinkill_CB") ;
463
464 if( hw->kill_func != NULL )
465 AFNI_CALL_VOID_1ARG( hw->kill_func , XtPointer , hw->kill_data ) ;
466 XtDestroyWidget( hw->wshell ) ;
467 myXtFree( hw ) ;
468 EXRETURN ;
469 }
470
471 /*--------------------------------------------------------------------*/
472
473 #define HTTP_check(str) ( strncmp((str),"http://",7) == 0 && \
474 !isspace((str)[7]) && \
475 !iscntrl((str)[7]) && \
476 (str)[7] != '\0' && \
477 (str)[7] != '*' && \
478 (str)[7] != '.' )
479
480 #define HTTPS_check(str) ( strncmp((str),"https://",8) == 0 && \
481 !isspace((str)[8]) && \
482 !iscntrl((str)[8]) && \
483 (str)[8] != '\0' && \
484 (str)[8] != '*' && \
485 (str)[8] != '.' )
486
487 #define CHK4(abcd) \
488 ( tolower(buf[hend-4])==abcd[0] && tolower(buf[hend-3])==abcd[1] && \
489 tolower(buf[hend-2])==abcd[2] && tolower(buf[hend-1])==abcd[3] )
490
491 #define EMIT_char(ch) \
492 do{ if( itout == atout ){ \
493 atout = (int)(1.5f*atout+1024); tout = (char *)realloc(tout,atout); \
494 } \
495 tout[itout++] = (ch) ; \
496 } while(0)
497
498 #define EMIT_string(cs) \
499 do{ char *cq ; \
500 for( cq=cs ; *cq != '\0' ; cq++ ) EMIT_char(*cq) ; \
501 } while(0)
502
503 /*----------------------------------------------------------------------------*/
504 /* Convert plain text to HTML, inserting links and a few other miscellany.
505 free() the returned string when done, if you don't mind. [06 May 2015]
506 *//*--------------------------------------------------------------------------*/
507
convert_text_to_html(char * txt)508 char * convert_text_to_html( char *txt )
509 {
510 char *tout=NULL , tbuf[2048] , *tin=txt , cc ;
511 int itout=0 , atout=0 ;
512
513 ENTRY("convert_text_to_html") ;
514
515 if( txt == NULL || *txt == '\0' ) RETURN(tout) ; /* bad input */
516
517 atout = strlen(txt)+1024 ; /* size of output buffer */
518 tout = (char *)malloc(atout) ; itout = 0 ;
519
520 EMIT_string("<html>\n"
521 "<head>\n"
522 "<title>AFNI Papers</title>\n"
523 "</head>\n"
524 "<body>\n<br />\n" ) ;
525
526 #if 0
527 EMIT_string("<center><img src=\"afnigui_logo.jpg\" align=middle></center>\n") ;
528 #endif
529
530 while( *tin != '\0' ){
531
532 #if 0
533 /* some HTML already? just pass it thru */
534
535 if( *tin == '<' && ( isalpha(*(tin+1)) || *(tin+1) == '/') ){ /* "<something...>" */
536 do{
537 EMIT_char(*tin) ; tin++ ;
538 } while( *tin != '>' && *tin != '\0' ) ;
539 if( *tin == '>' ){ EMIT_char(*tin) ; tin++ ; }
540 continue ;
541 }
542 #endif
543
544 /* link to a web page? "http://something" */
545
546 if( HTTP_check(tin) ){
547 int hend , ii ;
548
549 /* scan forward to get to end of 'http://something' string at hend */
550
551 for( hend=7 ; tin[hend] != '\0' && !isspace(tin[hend]) ; hend++ ) ; /*nada*/
552
553 /* insert hyperlink here*/
554
555 EMIT_string("<a href=\"") ;
556 for( ii=0 ; ii < hend ; ii++ ) EMIT_char(tin[ii]);
557 EMIT_string("\">") ;
558 for( ii=0 ; ii < hend ; ii++ ) EMIT_char(tin[ii]);
559 EMIT_string("</a>") ;
560
561 tin += hend ; continue ;
562 }
563
564 /* link to a web page? "https://something" [08 Apr 2016] */
565
566 if( HTTPS_check(tin) ){
567 int hend , ii ;
568
569 /* scan forward to get to end of 'https://something' string at hend */
570
571 for( hend=8 ; tin[hend] != '\0' && !isspace(tin[hend]) ; hend++ ) ; /*nada*/
572
573 /* insert hyperlink here*/
574
575 EMIT_string("<a href=\"") ;
576 for( ii=0 ; ii < hend ; ii++ ) EMIT_char(tin[ii]);
577 EMIT_string("\">") ;
578 for( ii=0 ; ii < hend ; ii++ ) EMIT_char(tin[ii]);
579 EMIT_string("</a>") ;
580
581 tin += hend ; continue ;
582 }
583
584 /* a single normal character */
585
586 cc = *tin ;
587 if( cc == ' ' ) EMIT_string(" ") ; /* put in HTML escapes */
588 else if( cc == '&' ) EMIT_string("&") ; /* for special cases */
589 else if( cc == '<' ) EMIT_string("<") ;
590 else if( cc == '>' ) EMIT_string(">") ;
591 else if( cc == '\n') EMIT_string("<br />\n") ;
592 else EMIT_char (cc) ; /* perfectly normal character */
593 tin++ ;
594 }
595
596 EMIT_string("\n</body></html>\n") ;
597 EMIT_char('\0') ;
598 RETURN(tout) ;
599 }
600
601 #endif /* DONT_USE_HTMLWIN */
602