1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18     Last edit by hansen on Sat Jan 17 00:30:11 2009
19 ****************************************************************************/
20 
21 #ifdef __cplusplus
22 #include <cstdlib>
23 #include <cstdio>
24 #include <cassert>
25 #else
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #endif
30 
31 #include <ctype.h>
32 #include <stdarg.h>
33 
34 #include "tkgate.h"
35 
36 int istruevalue(const char *s);
37 
38 static struct {
39   GCElement *g;			/* Gate on which hyperlink is found */
40   char *link;			/* Text of the link */
41   int isConfirmed;		/* hyperlink is confirmed (mouse up seen) */
42 } pending_jump = {0,0,0};
43 
setPendingLink(GCElement * g,const char * link)44 static void setPendingLink(GCElement *g,const char *link)
45 {
46   pending_jump.g = g;
47   pending_jump.isConfirmed = 0;
48   if (pending_jump.link) free(pending_jump.link);
49   pending_jump.link = link ? strdup(link) : 0;
50 }
51 
52 /*****************************************************************************
53  *
54  * Replace certain special directory identifiers with the actual directory.
55  *
56  *****************************************************************************/
expandSpecialDirs(char * file)57 void expandSpecialDirs(char *file)
58 {
59   const char *home    = Tcl_GetVar(TkGate.tcl,"tkg_gateHome",TCL_GLOBAL_ONLY);
60   char *curFileDir = CurrentFile_getDir(TkGate.circuit->currentFile);
61   char *exampleDir = tkgate_exampledir;
62   char *tutorialDir = tkgate_tutorialdir;
63 
64   strreplace(file,"@T",tutorialDir,1);
65   strreplace(file,"@E",exampleDir,1);
66   strreplace(file,"@H",home,1);
67   strreplace(file,"@C",curFileDir,1);
68   strreplace(file,"@L",TkGate.locale->l_code,1);
69   strreplace(file,"@",home,1);
70 }
71 
Hyperlink_inSimulator()72 int Hyperlink_inSimulator()
73 {
74   if (tkgate_currentMode() == MM_SIMULATE) {
75     const char *result;
76     DoTcl("yesno {%s}",msgLookup("sim.hyperlinkstop"));
77     result = Tcl_GetStringResult(TkGate.tcl);
78     if (!istruevalue(result)) {
79       Hyperlink_cancel();
80       return 1;
81     }
82 
83     DoTcl("Action::editMode");
84   }
85   return 0;
86 }
87 
88 /******************************************************************************
89  *
90  * Invoke a browser handled url.
91  *
92  ******************************************************************************/
Html_invoke_browser(char * protocol,char * file)93 void Html_invoke_browser(char *protocol,char * file)
94 {
95   char url[STRMAX];
96   char command[STRMAX];
97   char *p;
98   FILE *unused;
99   int  sysret;
100 
101   sprintf(url,"%s:%s",protocol,file);
102   sprintf(command,"%s",TkGate.browserCommand);
103 
104   p = strchr(command,'%');
105   if (strncmp(p,"%s",2) == 0) {
106     memmove(p+strlen(url),p+2,strlen(p)-1);
107     memmove(p,url,strlen(url));
108   }
109 
110   if (!fork()) {
111     close(0);
112     close(1);
113     close(2);
114     unused = fopen("/dev/null","r");
115     unused = fopen("/dev/null","w");
116     unused = fopen("/dev/null","w");
117     (void)unused;
118     sysret = system(command);
119     exit(sysret);
120   }
121 }
122 
123 /******************************************************************************
124  *
125  * Invoke an email handled url.
126  *
127  ******************************************************************************/
Html_invoke_email(char * protocol,char * file)128 void Html_invoke_email(char *protocol,char * file)
129 {
130   char url[STRMAX];
131   char command[STRMAX];
132   char *p;
133   FILE *unused;
134   int  sysret;
135 
136   sprintf(url,"%s:%s",protocol,file);
137   sprintf(command,"%s",TkGate.emailCommand);
138 
139   p = strchr(command,'%');
140   if (strncmp(p,"%s",2) == 0) {
141     memmove(p+strlen(url),p+2,strlen(p)-1);
142     memmove(p,url,strlen(url));
143   }
144 
145   if (!fork()) {
146     close(0);
147     close(1);
148     close(2);
149     unused = fopen("/dev/null","r");
150     unused = fopen("/dev/null","w");
151     unused = fopen("/dev/null","w");
152     (void)unused;
153     sysret = system(command);
154     exit(sysret);
155   }
156 }
157 
158 /******************************************************************************
159  *
160  * Invoke a "file:" protocol url.
161  *
162  ******************************************************************************/
Html_invoke_file(char * file)163 void Html_invoke_file(char * file)
164 {
165   char *label = 0;
166 
167   /*
168    * If this appears to be a relative link, prepend the current file directory marker.
169    */
170   if (*file != '/' && *file != '@' && *file != '#') {
171     memmove(file+3,file,strlen(file)+1);
172     memmove(file,"@C/",3);
173   }
174   expandSpecialDirs(file);
175 
176   /*
177    * See if there is a label
178    */
179   label = strchr(file,'#');
180   if (label) *label++ = 0;
181 
182   /*
183    * If there is still a file name, load the file if it is a .v file, or display
184    * it in a viewer if it is any other type of file.
185    */
186   if (*file) {
187     char *ext = strrchr(file,'.');
188     if (!ext) ext = "";
189 
190     if (isVerilogFile(file)) {
191       if (Hyperlink_inSimulator()) return;
192       DoTclL("Action::loadNamedFile",file,NULL);
193     } else if (strcasecmp(ext,".html") == 0 || strcasecmp(ext,".htm") == 0) {
194       Html_invoke_browser("file",file);
195     } else {
196       char *title;
197 
198       title = strrchr(file,'/');
199       if (title)
200 	title++;
201       else
202 	title = file;
203 
204       DoTclL("viewFile",title,file,NULL);
205     }
206   }
207 
208 
209   if (label && *label) {
210     if (Hyperlink_inSimulator()) return;
211     if (*label == '/') {
212       DoTcl("gat_openBox %s",label+1);
213     } else {
214       ob_touch(TkGate.circuit);
215       if (editstate_checkPath(&TkGate.circuit->es,label))
216 	editstate_setPath(&TkGate.circuit->es,label);
217       else
218 	message(0,msgLookup("err.badfind"),label);
219     }
220   }
221 }
222 
223 /******************************************************************************
224  *
225  * Invoke a "action:" protocol url.
226  *
227  ******************************************************************************/
Html_invoke_action(char * file)228 void Html_invoke_action(char * file)
229 {
230   static char **proc_table = 0;
231   static int N = 0;
232   int i;
233 
234   if (Hyperlink_inSimulator()) return;
235 
236   if (!proc_table) {
237     char *temp_s,*t;
238     char **temp_a;
239 
240     DoTcl("info procs Action::*");
241     temp_s = strdup(Tcl_GetStringResult(TkGate.tcl));
242     temp_a = (char**)malloc(sizeof(char*)*strlen(temp_s));	// Probably way more than we need
243 
244     for (t = strtok(temp_s," \n\t");t;t = strtok(0," \n\t"))
245       temp_a[N++] = t;
246 
247     proc_table = (char**)malloc(sizeof(char*)*N);
248     for (i = 0;i < N;i++) {
249       t = strstr(temp_a[i],"Action::");
250       assert(t);						// Everything should be an Action:: proc.
251       proc_table[i] = t + 8;
252     }
253 
254     free(temp_a);
255   }
256 
257   for (i = 0;i < N;i++)
258     if (strcmp(file,proc_table[i]) == 0) {
259       DoTcl("Action::%s",proc_table[i]);
260       break;
261     }
262 }
263 
264 /*****************************************************************************
265  *
266  * Parse and invoke a hyperlink
267  *
268  *****************************************************************************/
dohyperlink(const char * url)269 void dohyperlink(const char *url)
270 {
271   char protocol[STRMAX];
272   char file[STRMAX];
273 
274   if (sscanf(url," %[^:]:%[^\n ]",protocol,file) == 2) {
275   } else if (sscanf(url," %[^\n ]",file) == 1) {
276     strcpy(protocol,"file");
277   } else {
278     return;			/* Bad url */
279   }
280 
281   if (strcasecmp(protocol,"file") == 0) {
282     Html_invoke_file(file);
283   } else if (strcasecmp(protocol,"action") == 0) {
284     Html_invoke_action(file);
285   } else if (strcasecmp(protocol,"http") == 0) {
286     Html_invoke_browser(protocol,file);
287   } else if (strcasecmp(protocol,"mailto") == 0) {
288     Html_invoke_email(protocol,file);
289   }
290 #if 0
291   ob_touch(TkGate.circuit);
292   TkGate.circuit->mode = MODE_MOVENULL;
293 #endif
294 }
295 
Hyperlink_activatePending(ClientData data)296 void Hyperlink_activatePending(ClientData data)
297 {
298   if (pending_jump.isConfirmed) {
299     ob_begin_framef("Hyperlink",0);
300     dohyperlink(pending_jump.link);
301     Hyperlink_cancel();
302     ob_end_frame();
303   }
304 }
305 
Hyperlink_selectAt(int x,int y)306 int Hyperlink_selectAt(int x,int y)
307 {
308   GCElement *g = gate_hit(TkGate.circuit->es->env,x,y);
309   const char *link;
310 
311   setPendingLink(0,0);
312 
313   if (!g || g->typeinfo->code != GC_COMMENT) {
314     return 0;
315   }
316 
317   link =  Comment_getHyperlink(g, x, y);
318   if (!link) {
319     return 0;
320   }
321 
322   setPendingLink(g,link);
323 
324   return 1;
325 }
326 
Hyperlink_getAt(int x,int y)327 const char *Hyperlink_getAt(int x,int y)
328 {
329   GCElement *g = gate_hit(TkGate.circuit->es->env,x,y);
330 
331   if (!g || g->typeinfo->code != GC_COMMENT) {
332     return 0;
333   }
334 
335   return Comment_getHyperlink(g,x,y);
336 }
337 
Hyperlink_confirmAt(int x,int y)338 int Hyperlink_confirmAt(int x,int y)
339 {
340   GCElement *g;
341   const char *link;
342   int failed = 0;
343 
344   if (!pending_jump.link) return 0;
345 
346   if (!pending_jump.link) {
347     failed = 1;
348   } else if (!(g = gate_hit(TkGate.circuit->es->env,x,y))) {
349     failed = 1;
350   } else if (g->typeinfo->code != GC_COMMENT) {
351     failed = 1;
352   } else if (g != pending_jump.g) {
353     failed = 1;
354   } else if (!(link =  Comment_getHyperlink(g,TkGate.ed->tx,TkGate.ed->ty))) {
355     failed = 1;
356   } else if (strcmp(link,pending_jump.link) != 0) {
357     failed = 1;
358   } else {
359     pending_jump.isConfirmed = 1;
360   }
361 
362   if (failed) {
363     setPendingLink(0,0);
364     return 0;
365   } else {
366     Tk_DoWhenIdle(Hyperlink_activatePending, 0);
367     return 1;
368   }
369 }
370 
371 
Hyperlink_cancel()372 void Hyperlink_cancel()
373 {
374   setPendingLink(0,0);
375 }
376 
Hyperlink_isPending()377 int Hyperlink_isPending()
378 {
379   return (pending_jump.link != 0);
380 }
381