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 Thu Feb 12 14:50:38 2009
19 ****************************************************************************/
20 
21 #include <stdlib.h>
22 
23 #include "tkgate.h"
24 
25 static GModuleDef *hdl_active_module = 0;
26 
27 int hdl_isactive;
28 
29 /*****************************************************************************
30  *
31  * Data structure for holding information about a module definition from the
32  * HDL text buffer.  This data structure is used only in this file and only
33  * as a return value from hdl_findPartitions().
34  *
35  *****************************************************************************/
36 typedef struct {
37   char *comment_start;		// Start of comments for module.
38   char *module_line;		// Pointer to beginning of line with "module" in it.
39   char *name;			// Pointer to beginning of module name
40   int name_len;			// Number of characters in module name.
41   int module_length;		// Total length of module
42   char *endmodule_line;		// Pointer to beginning of line with "endmodule" in it.
43   char *module_end;		// End of module definition
44 } HdlModuleData;
45 
46 
47 /*****************************************************************************
48  *
49  * Check to make sure that s1 matches the first n characters of s2.
50  *
51  *****************************************************************************/
ismatch(const char * s1,const char * s2,int n)52 static int ismatch(const char *s1,const char *s2,int n)
53 {
54 
55   if (strlen(s1) != n) return 0;
56   return strncmp(s1,s2,n) == 0;
57 }
58 
59 /*****************************************************************************
60  *
61  * Load HDL text from module definition to editor.
62  *
63  * Parameters:
64  *     M		Module to be loaded into hdl editor.
65  *
66  *****************************************************************************/
hdl_load(GModuleDef * M)67 void hdl_load(GModuleDef *M)
68 {
69   hdl_active_module = M;
70 
71   if (M->m_text) {
72     DoTcl("HdlEditor::enable");
73     DoTcl("HdlEditor::clear");
74 
75     DoTclL("HdlEditor::loadText",M->m_text,NULL);
76 
77     if (GModuleDef_isDataProtected(M))
78       DoTcl("HdlEditor::disable");
79   } else {
80     printf("hdl_load ignored for non-HDL module [%s]\n",M->m_name);
81   }
82 
83 }
84 
85 /*****************************************************************************
86  *
87  * Save HDL text in editor to module definition.
88  *
89  * Parameters:
90  *     name		Expected name of module to save to or null for current module.
91  *****************************************************************************/
hdl_save(const char * name)92 int hdl_save(const char *name)
93 {
94   GModuleDef *M;
95   int rvalue = 0;
96   const char *text;
97 
98   if (name)
99     M = env_findModule(name);
100   else
101     M = hdl_active_module;
102 
103   if (!M) {
104     /* We must have tried to close the HDL editor while not editing an HDL module */
105     return rvalue;
106   }
107   if (M->m_type != MT_TEXTHDL) return 0;
108 
109   DoTcl("HdlEditor::dumpText");
110   text = Tcl_GetStringResult(TkGate.tcl);
111 
112 #if 0
113   printf("hdl_save(%s)\n",M->m_name);
114 #endif
115 
116   if (strcmp(text, M->m_text) == 0) return 0;
117 
118   /*
119    * Move text from the text widget into tcl variables
120    */
121   ob_touch(M);
122   GModuleDef_saveText(M,text);
123   M->m_needScan = 1;
124 
125   return rvalue;
126 }
127 
128 /*****************************************************************************
129  *
130  * Find the points in a text block at which modules are defined.
131  *
132  * Parameters:
133  *     text			Text block to partition
134  *     module_data[]		Array of lines containing 'module' or 'primitive'
135  *     n			Number of element in module data array.
136  *
137  *****************************************************************************/
hdl_findPartitions(char * text,HdlModuleData * module_data,int n)138 static int hdl_findPartitions(char *text,HdlModuleData *module_data,int n)
139 {
140   char *token_begin,*token_end;
141   char *p = text;
142   int r;
143   int state = 0; /* 0 = outside module, 1 = inside module */
144   int count = 0;
145 
146 
147   module_data[0].comment_start = text;
148 
149   while ((r = getNextToken(&p, &token_begin, &token_end)) != ST_END) {
150     int len = token_end-token_begin;
151 
152     if (r != ST_LITERAL) continue;
153 
154     if (ismatch("module",token_begin,len) || ismatch("primitive",token_begin,len)) {
155       if (state == 1) {
156 	/* got a module begin while still in a module */
157       } else {
158 	module_data[count].module_line = getLineStart(token_begin,text);
159 
160 	if (getNextToken(&p, &token_begin, &token_end) == ST_END) break;
161 
162 	module_data[count].name = token_begin;
163 	module_data[count].name_len = token_end - token_begin;
164 	state = 1;
165       }
166     } else if (ismatch("endmodule",token_begin,len) || ismatch("endprimitive",token_begin,len)) {
167       if (state == 0) {
168 	/* got an endmodule while not in a module definition. */
169       } else {
170 	state = 0;
171 	module_data[count].endmodule_line = getLineStart(token_begin,text);
172 	module_data[count].module_end = getLineEnd(token_end);
173 	if (*module_data[count].module_end == '\n') module_data[count].module_end++;
174 	module_data[count].module_length = module_data[count].module_end - module_data[count].comment_start;
175 	if (++count < n) {
176 	  module_data[count].comment_start = module_data[count-1].module_end;
177 	} else
178 	  return count;
179       }
180     }
181   }
182 
183   /*
184    * No endmodule was found.
185    */
186   if (state) {
187     module_data[count].endmodule_line = p;
188     module_data[count].module_end = p;
189     module_data[count].module_length = module_data[count].module_end - module_data[count].comment_start;
190     count++;
191   }
192 
193   /*
194    * Last module should snarf text to the end of the buffer.
195    */
196   if (count > 0) {
197     module_data[count-1].module_end = p;
198     module_data[count-1].module_length = module_data[count-1].module_end - module_data[count-1].comment_start;
199   }
200 
201   return count;
202 }
203 
204 /*****************************************************************************
205  *
206  * Replace the name of a module.
207  *
208  * Parameters:
209  *      M		Module to be renamed.
210  *      new_name	New module name.
211  *
212  * Returns:		Non-zero on error.
213  *
214  *****************************************************************************/
hdl_replaceName(GModuleDef * M,const char * new_name)215 int hdl_replaceName(GModuleDef *M,const char *new_name)
216 {
217   int new_len = strlen(new_name);
218   char *src_text = M->m_text;
219   int src_len = strlen(src_text);
220   int name_len;
221   char *name_ptr;
222   char *name_end;
223   int name_offset;
224   HdlModuleData module_data[2];
225   int npart;
226   char *text;
227 
228   npart = hdl_findPartitions(M->m_text,module_data,2);
229   if (npart != 1) return -1;
230 
231   name_ptr = module_data[0].name;
232   name_len = module_data[0].name_len;
233   name_end = name_ptr + name_len;
234   name_offset = (name_ptr-src_text);
235 
236   text = (char*) malloc(src_len+new_len-name_len+1);
237 
238   memmove(text, src_text, name_offset);
239   memmove(text+name_offset, new_name, new_len);
240   strcpy(text+name_offset+new_len, name_end);
241 
242   GModuleDef_saveText(M,text);
243   free(text);
244 
245   return 0;
246 }
247 
248 
249 
250 
251 /*****************************************************************************
252  *
253  * Split HDL text in module M into multiple modules.
254  *
255  * Parameters:
256  *      M			Module that this text is a part of.
257  *      module_data[]		Array of module data descriptors
258  *      module_count		Number of modules.
259  *
260  *****************************************************************************/
hdl_splitModules(GModuleDef * M,HdlModuleData * module_data,int module_count)261 int hdl_splitModules(GModuleDef *M,HdlModuleData *module_data,int module_count)
262 {
263   int i,keep_idx = 0;
264 
265   /*
266    * Find the module that we are going to keep as part of M's definiton, or use
267    * the first module if we can't find a matching name.
268    */
269   for (i = 0;i < module_count;i++) {
270     if (module_data[i].name_len > (STRMAX-10)) {
271       /* Found a very long module name. */
272       return -1;
273     }
274     if (ismatch(M->m_name, module_data[i].name, module_data[i].name_len)) {
275       keep_idx = i;
276     }
277   }
278 
279   for (i = 0;i < module_count;i++) {
280     char modName[STRMAX],*p;
281     int count = 0;
282     int length = module_data[i].module_length;
283     GModuleDef *new_M;
284 
285     if (i == keep_idx) continue;
286 
287     /*
288      * Make sure the target name does not exist.  If it does append a number
289      * to the end until we find an available module name.
290      */
291     strncpy(modName, module_data[i].name, module_data[i].name_len);
292     modName[module_data[i].name_len] = 0;
293     p = modName + strlen(modName);
294     while (env_findModule(modName)) {
295       sprintf(p,"_%d",count++);
296     }
297     new_M = env_defineModule(modName,0);
298     new_M->m_type = MT_TEXTHDL;
299     GModuleDef_allocText(new_M,length+1);
300     memmove(new_M->m_text, module_data[i].comment_start, length);
301     new_M->m_text[length] = 0;
302     if (count != 0)
303       hdl_replaceName(new_M,modName);
304   }
305 
306   memmove(M->m_text, module_data[keep_idx].comment_start, module_data[keep_idx].module_length);
307   M->m_text[module_data[keep_idx].module_length] = 0;
308   if (!ismatch(M->m_name,module_data[keep_idx].name,module_data[keep_idx].name_len))
309       hdl_replaceName(M,M->m_name);
310 
311 #if 0
312   printf("BEGIN KEEP\n%s\nEND KEEP\n",M->m_text);
313 #endif
314 
315   return 0;
316 }
317 
318 /*****************************************************************************
319  *
320  * Save HDL text in editor to module definition with consitency checks.
321  *
322  * Parameters:
323  *     name		Expected name of module to save to or null for current module.
324  *
325  * Save back hdl text to a module.  The HDL text is scanned to make sure it
326  * is being saved with the correct module.  The following actions are also
327  * taken as appropriate:
328  *
329  *   1) If the name of the module in the text definition does not match the
330  *   current name of the module, rename the module.
331  *
332  *   2) If there is more than one module definition, split the text into
333  *   multiple modules.
334  *
335  *   3) If there are is no module definition, add a simple empty definition.
336  *
337  *   4) If any module names in 1) or 2) are in use, manipulate the module names.
338  *
339  *****************************************************************************/
hdl_checkSave(const char * name)340 int hdl_checkSave(const char *name)
341 {
342   int module_count = 0;
343   HdlModuleData module_data[MAXMODS];
344   GModuleDef *M;
345   const char *text;
346   int rvalue = 0;
347 
348   if (name)
349     M = env_findModule(name);
350   else
351     M = hdl_active_module;
352 
353   if (!M) {
354     /* We must have tried to close the HDL editor while not editing an HDL module */
355     return rvalue;
356   }
357 
358   if (M->m_type != MT_TEXTHDL) return 0;
359 
360   ob_touch(M);
361   M->m_needScan = 1;
362 
363   DoTcl("HdlEditor::dumpText");
364   text = Tcl_GetStringResult(TkGate.tcl);
365 
366   /*
367    * Turn text into a copy.  Use non-ob functions since we only use it here
368    */
369   text = strdup(text);
370 
371   /*
372    * Find the partition points for modules
373    */
374   module_count = hdl_findPartitions((char *)text,module_data,MAXMODS);
375 
376   ob_touch(M);
377 
378   if (module_count == 0) {
379     /*
380      * If no modules where found, copy any available text and add a module/endmodule
381      * block with the appropriate name.
382      */
383     char buf[STRMAX];
384 
385     sprintf(buf,"module %s;\n\nendmodule\n",M->m_name);
386     GModuleDef_saveText(M,buf);
387 
388     module_data[0].comment_start = M->m_text;
389     module_data[0].module_line = M->m_text;
390     module_data[0].name = M->m_text + 7;	/* Position of module name */
391     module_data[0].name_len = strlen(M->m_name);
392     module_data[0].endmodule_line = M->m_text + strlen(M->m_text);
393     module_data[0].module_end = module_data[0].endmodule_line;
394     module_count = 1;
395     DoTclL("HdlEditor::loadText",M->m_text,NULL);
396   } else if (module_count == 1) {
397     /*
398      * If a single module was found, store the HDL text back in the module.  Rename the
399      * module if it was changed in the HDL text.
400      */
401 
402     GModuleDef_saveText(M,text);
403 
404     if (!ismatch(M->m_name,module_data[0].name,module_data[0].name_len)) {
405       DoTcl("HdlEditor::askRename");
406       if (strcmp(Tcl_GetStringResult(TkGate.tcl),"autoedit") == 0) {
407 	hdl_replaceName(M,M->m_name);
408 	DoTclL("HdlEditor::loadText",M->m_text,NULL);
409       } else if (strcmp(Tcl_GetStringResult(TkGate.tcl),"ignore") == 0) {
410 	/* Do nothing */
411       } else {	/* cancel */
412 	rvalue = -1;
413       }
414     }
415   } else {
416     /*
417      * If there is more than one module in the text block, we can either split them into
418      * separate modules, or comment out everything after the first module.  Ask the user
419      * which action they want to take.
420      */
421     DoTcl("HdlEditor::askSaveOption");
422     if (strcmp(Tcl_GetStringResult(TkGate.tcl),"split") == 0) {
423       GModuleDef_saveText(M, text);
424       hdl_splitModules(M,module_data,module_count);
425       DoTclL("HdlEditor::loadText",M->m_text,NULL);
426     } else if (strcmp(Tcl_GetStringResult(TkGate.tcl),"ignore") == 0) {
427       GModuleDef_saveText(M, text);
428     } else {
429       /* "cancel" or unknown value */
430       GModuleDef_saveText(M, text);
431       rvalue = -1;
432     }
433   }
434 
435   if (text) free((void*)text);
436 
437   return rvalue;
438 }
439 
440 /*****************************************************************************
441  *
442  * Save the cursor position in the specified module.
443  *
444  * Parameters:
445  *     name		Name of module
446  *     line		Line number in buffer
447  *     pos		Character position on line.
448  *
449  *****************************************************************************/
hdl_saveCursor(const char * name,int line,int pos)450 void hdl_saveCursor(const char *name,int line,int pos)
451 {
452   GModuleDef *M = hdl_active_module;
453 
454   if (name) M = env_findModule(name);
455   if (M && M->m_type == MT_TEXTHDL) {
456     ob_touch(M);
457     M->m_curLine = line;
458     M->m_curChar = pos;
459   }
460 }
461 
462 /*****************************************************************************
463  *
464  * Get the saved cursor position from the current module
465  *
466  * Parameters:
467  *     *line		Line number in buffer
468  *     *pos		Character position on line.
469  *
470  *****************************************************************************/
hdl_getCursor(int * line,int * pos)471 int hdl_getCursor(int *line,int *pos)
472 {
473   GModuleDef *M = hdl_active_module;
474 
475   if (!M) return -1;
476   if (M->m_type != MT_TEXTHDL) return -1;
477 
478   *line = M->m_curLine;
479   *pos = M->m_curChar;
480 
481   return 0;
482 }
483 
484 /*****************************************************************************
485  *
486  * Save the current HDL text and end HDL editing.
487  *
488  *****************************************************************************/
hdl_close(void)489 int hdl_close(void)
490 {
491   int rvalue;
492 
493 #if 0
494   printf("hdl_close()\n");
495 #endif
496 
497   rvalue = hdl_save(0);
498   hdl_active_module = 0;
499   return rvalue;
500 }
501 
502