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