/* File: scheduler.c * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit * simulation. * Copyright (C) 1998-2023 Stefan Frederik Schippers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xschem.h" /* n=1: messages in status bar * n=2: append str in ERC window messages * n=3: set ERC messages to str */ void statusmsg(char str[],int n) { if(!str) return; if(str[0]== '\0') { my_free(_ALLOC_ID_, &xctx->infowindow_text); return; } else { if(n == 3) { my_strdup(_ALLOC_ID_, &xctx->infowindow_text, str); } else if(n == 2) { if(xctx->infowindow_text && xctx->infowindow_text[0]) { my_strcat(_ALLOC_ID_, &xctx->infowindow_text, "\n"); } my_strcat(_ALLOC_ID_, &xctx->infowindow_text, str); } } if(!has_x) return; if(n == 2 || n == 3) { dbg(3, "statusmsg(): n = 2, str = %s\n", str); } else { tclvareval(xctx->top_path, ".statusbar.1 configure -text {", str, "}", NULL); dbg(3, "statusmsg(str, %d): -> str = %s\n", n, str); } } static int get_symbol(const char *s) { int i, found=0; if(isonlydigit(s)) { i=atoi(s); found = 1; } else for(i=0;isymbols; ++i) { if(!strcmp(xctx->sym[i].name, s)) { found=1; break; } } if(!found || i < 0 || i >= xctx->symbols) return -1; return i; } int get_instance(const char *s) { int i, found=0; Int_hashentry *entry; if(isonlydigit(s)) { i=atoi(s); found = 1; } else if(xctx->floater_inst_table.table) { entry = int_hash_lookup(&xctx->floater_inst_table, s, 0, XLOOKUP); i = entry ? entry->value : -1; found = 1; } else { for(i=0;iinstances; ++i) { if(!strcmp(xctx->inst[i].instname, s)) { found=1; break; } } } if(!found || i < 0 || i >= xctx->instances) return -1; return i; } static void xschem_cmd_help(int argc, const char **argv) { char prog[PATH_MAX]; if( get_file_path("x-www-browser")[0] == '/' ) goto done; if( get_file_path("firefox")[0] == '/' ) goto done; if( get_file_path("chromium")[0] == '/' ) goto done; if( get_file_path("chrome")[0] == '/' ) goto done; if( get_file_path("xdg-open")[0] == '/' ) goto done; #ifndef __unix__ const char *xschem_sharedir=tclgetvar("XSCHEM_SHAREDIR"); wchar_t app[MAX_PATH] = {0}; wchar_t w_url[PATH_MAX]; char url[PATH_MAX]="", url2[PATH_MAX]=""; int result = 0; if (xschem_sharedir) { my_snprintf(url, S(url), "%s/../doc/xschem_man/developer_info.html", xschem_sharedir); MultiByteToWideChar(CP_ACP, 0, url, -1, w_url, S(w_url)); /* The file:// url scheme doesn't have any allowance for HTTP parameters. So, use FindExecutable to get the browser app and then ShellExecute */ result = (int)FindExecutable(w_url, NULL, app); if (result > 32) goto done; } #endif if(has_x) tcleval("alert_ { No application to display html documentation} {}"); else dbg(0, "No application to display html documentation\n"); return; done: my_strncpy(prog, tclresult(), S(prog)); #ifdef __unix__ tclvareval("launcher {", "file://", XSCHEM_SHAREDIR, "/../doc/xschem/xschem_man/developer_info.html#cmdref", "} ", prog, NULL); #else my_snprintf(url2, S(url2), "file://%s#cmdref", url); MultiByteToWideChar(CP_ACP, 0, url2, -1, w_url, S(w_url)); ShellExecute(0, NULL, app, w_url, NULL, SW_SHOWNORMAL); #endif Tcl_ResetResult(interp); } /* can be used to reach C functions from the Tk shell. */ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * argv[]) { int i; char name[1024]; /* overflow safe 20161122 */ int cmd_found = 1; char *not_avail = "Not available in this context. If using --tcl consider using --command"; Tcl_ResetResult(interp); if(argc < 2) { Tcl_SetResult(interp, "Missing arguments.", TCL_STATIC); return TCL_ERROR; } if(debug_var>=2) { int i; fprintf(errfp, "xschem():"); for(i=0; imousex_snap-2.5, xctx->mousey_snap-2.5, xctx->mousex_snap+2.5, xctx->mousey_snap+2.5, xRECT, PINLAYER, SELECTED, "name=XXX\ndir=inout"); xctx->need_reb_sel_arr=1; rebuild_selected_array(); move_objects(START,0,0,0); xctx->ui_state |= START_SYMPIN; set_modify(1); Tcl_ResetResult(interp); } /* add_graph * Start a GUI placement of a graph object */ else if(!strcmp(argv[1], "add_graph")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} unselect_all(1); xctx->graph_lastsel = xctx->rects[GRIDLAYER]; storeobject(-1, xctx->mousex_snap-400, xctx->mousey_snap-200, xctx->mousex_snap+400, xctx->mousey_snap+200, xRECT, GRIDLAYER, SELECTED, "flags=graph\n" "y1=0\n" "y2=2\n" "ypos1=0\n" "ypos2=2\n" "divy=5\n" "subdivy=1\n" "unity=1\n" "x1=0\n" "x2=10e-6\n" "divx=5\n" "subdivx=1\n" "xlabmag=1.0\n" "ylabmag=1.0\n" "node=\"\"\n" "color=\"\"\n" "dataset=-1\n" "unitx=1\n" "logx=0\n" "logy=0\n" ); xctx->need_reb_sel_arr=1; rebuild_selected_array(); move_objects(START,0,0,0); xctx->ui_state |= START_SYMPIN; Tcl_ResetResult(interp); } /* add_png * Ask user to choose a png file and start a GUI placement of the image */ else if(!strcmp(argv[1], "add_png")) { char str[PATH_MAX+100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} unselect_all(1); tcleval("tk_getOpenFile -filetypes { {{Png} {.png}} {{All files} *} }"); if(tclresult()[0]) { my_snprintf(str, S(str), "flags=image,unscaled\nalpha=0.8\nimage=%s\n", tclresult()); storeobject(-1, xctx->mousex_snap-100, xctx->mousey_snap-100, xctx->mousex_snap+100, xctx->mousey_snap+100, xRECT, GRIDLAYER, SELECTED, str); xctx->need_reb_sel_arr=1; rebuild_selected_array(); move_objects(START,0,0,0); xctx->ui_state |= START_SYMPIN; } Tcl_ResetResult(interp); } /* align * Align currently selected objects to current snap setting */ else if(!strcmp(argv[1], "align")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->push_undo(); round_schematic_to_grid(tclgetdoublevar("cadsnap")); if(tclgetboolvar("autotrim_wires")) trim_wires(); set_modify(1); xctx->prep_hash_inst=0; xctx->prep_hash_wires=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; draw(); } /* annotate_op [raw_file] * Annotate operating point data into current schematic. * use .raw or use supplied argument as raw file to open * look for operating point data and annotate voltages/currents * into schematic */ else if(!strcmp(argv[1], "annotate_op")) { char f[PATH_MAX + 100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); } else { my_snprintf(f, S(f), "%s/%s.raw", tclgetvar("netlist_dir"), get_cell(xctx->sch[xctx->currsch], 0)); } tclsetboolvar("live_cursor2_backannotate", 1); tclsetvar("rawfile_loaded", "0"); extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 1); raw_read(f, &xctx->raw, "op"); update_op(); draw(); } /* arc * Start a GUI placement of an arc. * User should click 3 unaligned points to define the arc */ else if(!strcmp(argv[1], "arc")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTARC; } /* attach_labels * Attach net labels to selected component(s) instance(s) */ else if(!strcmp(argv[1], "attach_labels")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} attach_labels_to_inst(0); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'b': /*----------------------------------------------*/ /* bbox begin|end * Start/end bounding box calculation: parameter is either 'begin' or 'end' */ if(!strcmp(argv[1], "bbox")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { if(!strcmp(argv[2], "end")) { bbox(SET , 0.0 , 0.0 , 0.0 , 0.0); draw(); bbox(END , 0.0 , 0.0 , 0.0 , 0.0); } else if(!strcmp(argv[2], "begin")) { bbox(START,0.0, 0.0, 0.0, 0.0); } } Tcl_ResetResult(interp); } /* break_wires [remove] * Break wires at selected instance pins * if '1' is given as 'remove' parameter broken wires that are * all inside selected instances will be deleted */ else if(!strcmp(argv[1], "break_wires")) { int remove = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) remove = atoi(argv[2]); break_wires_at_pins(remove); Tcl_ResetResult(interp); } /* build_colors * Rebuild color palette using values of tcl vars dim_value and dim_bg */ else if(!strcmp(argv[1], "build_colors")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} build_colors(tclgetdoublevar("dim_value"), tclgetdoublevar("dim_bg")); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'c': /*----------------------------------------------*/ /* callback winpath event mx my key button aux state * Invoke the callback event dispatcher with a software event */ if(!strcmp(argv[1], "callback") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} callback( argv[2], atoi(argv[3]), atoi(argv[4]), atoi(argv[5]), (KeySym)atol(argv[6]), atoi(argv[7]), atoi(argv[8]), atoi(argv[9]) ); dbg(2, "callback %s %s %s %s %s %s %s %s\n", argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); Tcl_ResetResult(interp); } /* case_insensitive 1|0 * Set case insensitive symbol lookup. Use only on case insensitive filesystems */ else if(!strcmp(argv[1], "case_insensitive")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { int n = atoi(argv[2]); if(n) { xctx->case_insensitive = 1; xctx->x_strcmp = my_strcasecmp; } else { xctx->case_insensitive = 0; xctx->x_strcmp = strcmp; } } } /* change_elem_order n * set selected object (instance, wire, line, rect, ...) to * position 'n' in its respective array */ else if(!strcmp(argv[1], "change_elem_order")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { int n = atoi(argv[2]); if(n >= 0) { change_elem_order(n); xctx->prep_hash_wires=0; xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; } } } /* check_symbols * List all used symbols in current schematic and warn if some symbol is newer */ else if(!strcmp(argv[1], "check_symbols")) { char sympath[PATH_MAX]; const char *name; struct stat buf; char *res=NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i=0;isymbols; ++i) { name = xctx->sym[i].name; if(!strcmp(xctx->file_version, "1.0")) { my_strncpy(sympath, abs_sym_path(name, ".sym"), S(sympath)); } else { my_strncpy(sympath, abs_sym_path(name, ""), S(sympath)); } if(!stat(sympath, &buf)) { /* file exists */ if(xctx->time_last_modify < buf.st_mtime) { my_mstrcat(_ALLOC_ID_, &res, "Warning: symbol ", sympath, " is newer than schematic\n", NULL); } } else { /* not found */ my_mstrcat(_ALLOC_ID_, &res, "Warning: symbol ", sympath, " not found\n", NULL); } my_mstrcat(_ALLOC_ID_, &res, "symbol ", my_itoa(i), " : ", sympath, "\n", NULL); } Tcl_SetResult(interp, res, TCL_VOLATILE); my_free(_ALLOC_ID_, &res); } /* check_unique_names [1|0] * Check if all instances have a unique refdes (name attribute in xschem), * highlight such instances. If second parameter is '1' rename duplicates */ else if(!strcmp(argv[1], "check_unique_names")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 && !strcmp(argv[2], "1")) { check_unique_names(1); } else { check_unique_names(0); } Tcl_ResetResult(interp); } /* circle * Start a GUI placement of a circle. * User should click 3 unaligned points to define the circle */ else if(!strcmp(argv[1], "circle")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTCIRCLE; } /* clear [force] [symbol|schematic] * Clear current schematic window. Resets hierarchy level. Remove symbols * the 'force' parameter will not ask to save existing modified schematic. * the 'schematic' or 'symbol' parameter specifies to default to a schematic * or symbol window (default: schematic) */ else if(!strcmp(argv[1], "clear")) { int i, cancel = 1, symbol = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 2; i < argc; i++) { if(!strcmp(argv[i], "force") ) cancel = 0; if(!strcmp(argv[i], "symbol")) symbol = 1; } clear_schematic(cancel, symbol); Tcl_ResetResult(interp); } /* clear_drawing * Clears drawing but does not purge symbols */ else if(!strcmp(argv[1], "clear_drawing")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc==2) { unselect_all(1); clear_drawing(); } Tcl_ResetResult(interp); } /* color_dim value * Dim colors or brite colors depending on value parameter: -5 <= value <= 5 */ else if(!strcmp(argv[1], "color_dim")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { tclsetvar("dim_value", argv[2]); if(tclgetboolvar("enable_dim_bg") ) { tclsetvar("dim_bg", argv[2]); } } build_colors(tclgetdoublevar("dim_value"), tclgetdoublevar("dim_bg")); draw(); Tcl_ResetResult(interp); } /* compare_schematics [sch_file] * Compare currently loaded schematic with another 'sch_file' schematic. * if no file is given prompt user to choose one */ else if(!strcmp(argv[1], "compare_schematics")) { char f[PATH_MAX + 100]; int ret = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); ret = compare_schematics(f); } else { ret = compare_schematics(NULL); } Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE); } /* connected_nets [0|1|2|3] * Select nets/labels connected to currently selected instance * if '1' argument is given, stop at wire junctions * if '2' argument is given select only wires directly * attached to selected instance/net * if '3' argument is given combine '1' and '2' */ else if(!strcmp(argv[1], "connected_nets")) { int stop_at_junction = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 ) stop_at_junction = atoi(argv[2]); select_connected_nets(stop_at_junction); Tcl_ResetResult(interp); } /* copy * Copy selection to clipboard */ else if(!strcmp(argv[1], "copy")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} rebuild_selected_array(); save_selection(2); Tcl_ResetResult(interp); } /* copy_objects [deltax deltay [rot flip]] * if deltax and deltay (and optionally rot and flip) are given copy selection * to specified offset, otherwise start a GUI copy operation */ else if(!strcmp(argv[1], "copy_objects")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { copy_objects(START); xctx->deltax = atof(argv[2]); xctx->deltay = atof(argv[3]); if(argc > 4) { xctx->move_rot = (short int)atoi(argv[4]); } if(argc > 5) { xctx->move_flip = (short int)atoi(argv[5]); } copy_objects(END); } else { copy_objects(START); } Tcl_ResetResult(interp); } /* count_items string separator quoting_chars Debug command */ else if(!strcmp(argv[1], "count_items")) { if(argc > 4) { Tcl_SetResult(interp, my_itoa(count_items(argv[2], argv[3], argv[4])), TCL_VOLATILE); } } /* create_plot_cmd * Create an xplot file in netlist/simulation directory with * the list of highlighted nodes in a format the selected waveform * viewer understands (bespice, gaw, ngspice) */ else if(!strcmp(argv[1], "create_plot_cmd") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} create_plot_cmd(); Tcl_ResetResult(interp); } /* cut * Cut selection to clipboard */ else if(!strcmp(argv[1], "cut")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} rebuild_selected_array(); save_selection(2); delete(1/*to_push_undo*/); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'd': /*----------------------------------------------*/ /* debug n * Set xschem in debug mode.'n' is the debug level * (0=no debug). Higher levels yield more debug info.*/ if(!strcmp(argv[1], "debug")) { if(argc > 2) { debug_var=atoi(argv[2]); tclsetvar("debug_var",argv[2]); } Tcl_ResetResult(interp); } /* delete * Delete selection */ else if(!strcmp(argv[1], "delete")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc==2) delete(1/*to_push_undo*/); Tcl_ResetResult(interp); } /* delete_files * Bring up a file selector the user can use to delete files */ else if(!strcmp(argv[1], "delete_files")) { delete_files(); } /* descend [n] * Descend into selected component instance. Optional number 'n' specifies the * instance number to descend into for vector instances (default: 0). */ else if(!strcmp(argv[1], "descend")) { int ret=0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { int n = atoi(argv[2]); ret = descend_schematic(n); } else { ret = descend_schematic(0); } Tcl_SetResult(interp, dtoa(ret), TCL_VOLATILE); } /* descend_symbol * Descend into the symbol view of selected component instance */ else if(!strcmp(argv[1], "descend_symbol")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} descend_symbol(); Tcl_ResetResult(interp); } /* destroy_all [force] * Close all additional windows/.tabs. If 'force' is given do not ask for * confirmation for changed schematics * Returns the remaining # of windows/tabs in addition to main window/tab */ else if(!strcmp(argv[1], "destroy_all")) { int force = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 && !strcmp(argv[2], "force")) force = 1; if(force) new_schematic("destroy_all", "force", NULL, 1); else new_schematic("destroy_all", NULL, NULL, 1); Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); } /* display_hilights [nets|instances] * Print a list of highlighted objects (nets, net labels/pins, instances) * if 'instances' is specified list only instance highlights * if 'nets' is specified list only net highlights */ else if(!strcmp(argv[1], "display_hilights")) { char *str = NULL; int what = 3; /* nets and instances */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { if(!strcmp(argv[2], "instances")) what = 2; /* instances only */ else if(!strcmp(argv[2], "nets")) what = 1; /* nets only */ } display_hilights(what, &str); Tcl_SetResult(interp, str, TCL_VOLATILE); my_free(_ALLOC_ID_, &str); } /* draw_graph [n] [flags] * Redraw graph rectangle number 'n'. * If the optional 'flags' integer is given it will be used as the * flags bitmask to use while drawing (can be used to restrict what to redraw) */ else if(!strcmp(argv[1], "draw_graph")) { int flags; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { int i = atoi(argv[2]); if(argc > 3) { flags = atoi(argv[3]); } else { flags = 1 + 8 + (xctx->graph_flags & 6); } setup_graph_data(i, 0, &xctx->graph_struct); draw_graph(i, flags, &xctx->graph_struct, NULL); } Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'e': /*----------------------------------------------*/ /* edit_file * Edit xschem file of current schematic if nothing is selected. * Edit .sym file if a component is selected. */ if(!strcmp(argv[1], "edit_file") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} rebuild_selected_array(); if(xctx->lastsel==0 ) { save_schematic(xctx->sch[xctx->currsch]); /* sync data with disk file before editing file */ my_snprintf(name, S(name), "edit_file {%s}", abs_sym_path(xctx->sch[xctx->currsch], "")); tcleval(name); } else if(xctx->sel_array[0].type==ELEMENT) { my_snprintf(name, S(name), "edit_file {%s}", abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), "")); tcleval(name); } } /* edit_prop * Edit global schematic/symbol attributes or attributes * of currently selected instances */ else if(!strcmp(argv[1], "edit_prop")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} edit_property(0); Tcl_ResetResult(interp); } /* edit_vi_prop * Edit global schematic/symbol attributes or * attributes of currently selected instances * using a text editor (defined in tcl 'editor' variable) */ else if(!strcmp(argv[1], "edit_vi_prop")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} edit_property(1); Tcl_ResetResult(interp); } /* embed_rawfile raw_file * Embed base 64 encoded 'raw_file' into currently * selected element as a 'spice_data' * attribute. */ else if(!strcmp(argv[1], "embed_rawfile")) { char f[PATH_MAX + 100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); embed_rawfile(f); } Tcl_ResetResult(interp); } /* enable_layers * Enable/disable layers depending on tcl array variable enable_layer() */ else if(!strcmp(argv[1], "enable_layers")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} enable_layers(); Tcl_ResetResult(interp); } /* escape_chars source * escape tcl special characters with backslash */ else if(!strcmp(argv[1], "escape_chars")) { if(argc > 2) { Tcl_SetResult(interp, escape_chars(argv[2]), TCL_VOLATILE); } } /* exit [closewindow] * Exit the program, ask for confirm if current file modified. * if 'closewindow' is given close the window, otherwise leave with a blank schematic * if 'force' is given do not ask before closing modified schematic windows/tabs * when closing the last remaining window * This command returns the list of remaining open windows in addition to main window */ else if(!strcmp(argv[1], "exit")) { int closewindow = 0; int force = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 2; i < argc; ++i) { if(!strcmp(argv[i], "closewindow")) closewindow = 1; if(!strcmp(argv[i], "force")) force = 1; } if(!strcmp(xctx->current_win_path, ".drw")) { /* non tabbed interface */ if(!tclgetboolvar("tabbed_interface")) { int wc = get_window_count(); dbg(1, "wc=%d\n", wc); if(wc > 0 ) { if(!force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } if(force || !xctx->modified || !strcmp(tclresult(), "ok")) { swap_windows(); set_modify(0); /* set modified status to 0 to avoid another confirm in following line */ new_schematic("destroy", xctx->current_win_path, NULL, 1); } } else { if(!force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } if(force || !xctx->modified || !strcmp(tclresult(), "ok")) { if(closewindow) tcleval("exit"); else clear_schematic(0, 0); } } } /* tabbed interface */ else { int wc = get_window_count(); dbg(1, "wc=%d\n", wc); if(wc > 0 ) { if(has_x && !force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } if(!has_x || force || !xctx->modified || !strcmp(tclresult(), "ok")) { swap_tabs(); set_modify(0); new_schematic("destroy", xctx->current_win_path, NULL, 1); } } else { if(has_x && !force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } if(!has_x || force || !xctx->modified || !strcmp(tclresult(), "ok")) { if(closewindow) tcleval("exit"); else clear_schematic(0, 0); } } } } else { if(force) set_modify(0); /* avoid ask to save downstream */ new_schematic("destroy", xctx->current_win_path, NULL, 1); } Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); } /* expandlabel lab * Expand vectored labels/instance names: * xschem expandlabel {2*A[3:0]} --> A[3],A[2],A[1],A[0],A[3],A[2],A[1],A[0] 8 * last field is the number of bits * since [ and ] are TCL special characters argument must be quoted with { and } */ else if(!strcmp(argv[1], "expandlabel")) { int tmp; size_t llen; char *result=NULL; const char *l; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { l = expandlabel(argv[2], &tmp); llen = strlen(l); dbg(1, "l=%s\n", l ? l : ""); result = my_malloc(_ALLOC_ID_, llen + 30); my_snprintf(result, llen + 30, "%s %d", l, tmp); Tcl_SetResult(interp, result, TCL_VOLATILE); my_free(_ALLOC_ID_, &result); } } else { cmd_found = 0;} break; case 'f': /*----------------------------------------------*/ /* find_nth string sep quote keep_quote n * Find n-th field string separated by characters in sep. 1st field is in position 1 * do not split quoted fields (if quote characters are given) and return unquoted. * xschem find_nth {aaa,bbb,ccc,ddd} {,} 2 --> bbb * xschem find_nth {aaa, "bbb, ccc" , ddd} { ,} {"} 2 --> bbb, ccc */ if(!strcmp(argv[1], "find_nth")) { if(argc > 6) { Tcl_SetResult(interp, find_nth(argv[2], argv[3], argv[4], atoi(argv[5]), atoi(argv[6])), TCL_VOLATILE); } } /* flip [x0 y0] * Flip selection horizontally around point x0 y0. * if x0, y0 not given use mouse coordinates */ else if(!strcmp(argv[1], "flip")) { double x0 = xctx->mousex_snap; double y0 = xctx->mousey_snap; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { x0 = atof(argv[2]); y0 = atof(argv[3]); } if(xctx->ui_state & STARTMOVE) move_objects(FLIP,0,0,0); else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP); else { rebuild_selected_array(); xctx->mx_double_save = xctx->mousex_snap = x0; xctx->my_double_save = xctx->mousey_snap = y0; move_objects(START,0,0,0); move_objects(FLIP,0, 0, 0); move_objects(END,0,0,0); } Tcl_ResetResult(interp); } /* flip_in_place * Flip selection horizontally, each object around its center */ else if(!strcmp(argv[1], "flip_in_place")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx->ui_state & STARTMOVE) move_objects(ROTATE|ROTATELOCAL,0,0,0); else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE|ROTATELOCAL); else { rebuild_selected_array(); move_objects(START,0,0,0); move_objects(FLIP|ROTATELOCAL,0,0,0); move_objects(END,0,0,0); } Tcl_ResetResult(interp); } /* fullscreen * Toggle fullscreen modes: fullscreen with menu & status, fullscreen, normal */ else if(!strcmp(argv[1], "fullscreen")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) toggle_fullscreen(argv[2]); else toggle_fullscreen(".drw"); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'g': /*----------------------------------------------*/ /************ xschem get subcommands *************/ /* get var * Get C variable/constant 'var' */ if(!strcmp(argv[1], "get")) { if(argc > 2) { switch(argv[2][0]) { case 'b': if(!strcmp(argv[2], "backlayer")) { /* number of background layer */ Tcl_SetResult(interp, my_itoa(BACKLAYER), TCL_VOLATILE); } else if(!strcmp(argv[2], "bbox_hilighted")) { /* bounding box of highlinhted objects */ xRect boundbox; char res[2048]; calc_drawing_bbox(&boundbox, 2); my_snprintf(res, S(res), "%g %g %g %g", boundbox.x1, boundbox.y1, boundbox.x2, boundbox.y2); Tcl_SetResult(interp, res, TCL_VOLATILE); } else if(!strcmp(argv[2], "bbox_selected")) { /* bounding box of selected objects */ xRect boundbox; char res[2048]; calc_drawing_bbox(&boundbox, 1); my_snprintf(res, S(res), "%g %g %g %g", boundbox.x1, boundbox.y1, boundbox.x2, boundbox.y2); Tcl_SetResult(interp, res, TCL_VOLATILE); } break; case 'c': if(!strcmp(argv[2], "cadlayers")) { /* number of layers */ Tcl_SetResult(interp, my_itoa(cadlayers), TCL_VOLATILE); } else if(!strcmp(argv[2], "case_insensitive")) { /* case_insensitive symbol matching */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->case_insensitive), TCL_VOLATILE); } else if(!strcmp(argv[2], "color_ps")) { /* color postscript flag */ if(color_ps != 0 ) Tcl_SetResult(interp, "1",TCL_STATIC); else Tcl_SetResult(interp, "0",TCL_STATIC); } else if(!strcmp(argv[2], "current_dirname")) { /* directory name of current design */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, xctx->current_dirname, TCL_VOLATILE); } else if(!strcmp(argv[2], "current_name")) { /* name of current design (no library path) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, xctx->current_name, TCL_VOLATILE); } else if(!strcmp(argv[2], "current_win_path")) { /* path of current tab/window (.drw, .x1.drw, ...) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, xctx->current_win_path, TCL_VOLATILE); } else if(!strcmp(argv[2], "currsch")) { /* hierarchy level of current schematic (start at 0) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->currsch),TCL_VOLATILE); } break; case 'd': if(!strcmp(argv[2], "debug_var")) { /* debug level (0 = no debug, 1, 2, 3,...) */ Tcl_SetResult(interp, my_itoa(debug_var),TCL_VOLATILE); } else if(!strcmp(argv[2], "draw_window")) { /* direct draw into window */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->draw_window),TCL_VOLATILE); } break; case 'f': if(!strcmp(argv[2], "first_sel")) { /* get data about first selected object */ char res[40]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_snprintf(res, S(res), "%hu %d %u", xctx->first_sel.type, xctx->first_sel.n, xctx->first_sel.col); Tcl_SetResult(interp, res, TCL_VOLATILE); } else if(!strcmp(argv[2], "fix_broken_tiled_fill")) { /* get drawing method setting (for broken GPUs) */ Tcl_SetResult(interp, my_itoa(fix_broken_tiled_fill),TCL_VOLATILE); } else if(!strcmp(argv[2], "fix_mouse_coord")) { /* get fix_mouse_coord setting (fix for broken RDP)*/ Tcl_SetResult(interp, my_itoa(fix_mouse_coord),TCL_VOLATILE); } else if(!strcmp(argv[2], "format")) { /* alternate format attribute to use in netlist (or NULL) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(!xctx->format ) Tcl_SetResult(interp, "",TCL_STATIC); else Tcl_SetResult(interp, xctx->format,TCL_VOLATILE); } break; case 'g': if(!strcmp(argv[2], "graph_lastsel")) { /* number of last graph that was clicked */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->graph_lastsel),TCL_VOLATILE); } else if(!strcmp(argv[2], "gridlayer")) { /* layer number for grid */ Tcl_SetResult(interp, my_itoa(GRIDLAYER),TCL_VOLATILE); } break; case 'h': if(!strcmp(argv[2], "help")) { /* command help */ if(help != 0 ) Tcl_SetResult(interp, "1",TCL_STATIC); else Tcl_SetResult(interp, "0",TCL_STATIC); } else if(!strcmp(argv[2], "header_text")) { /* header metadata (license info etc) present in schematic */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx && xctx->header_text) { Tcl_SetResult(interp, xctx->header_text, TCL_VOLATILE); } else { Tcl_SetResult(interp, "", TCL_VOLATILE); } } break; case 'i': if(!strcmp(argv[2], "infowindow_text")) { /* ERC messages */ if(xctx && xctx->infowindow_text) Tcl_SetResult(interp, xctx->infowindow_text, TCL_VOLATILE); else Tcl_SetResult(interp, "", TCL_STATIC); } else if(!strcmp(argv[2], "instances")) { /* number of instances in schematic */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->instances), TCL_VOLATILE); } break; case 'l': if(!strcmp(argv[2], "lastsel")) { /* number of selected objects */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} rebuild_selected_array(); Tcl_SetResult(interp, my_itoa(xctx->lastsel),TCL_VOLATILE); } else if(!strcmp(argv[2], "line_width")) { /* get line width */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, dtoa(xctx->lw), TCL_VOLATILE); } else if(!strcmp(argv[2], "lines")) { /* (xschem get lines n) number of lines on layer 'n' */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { int c = atoi(argv[3]); if(c >=0 && c < cadlayers) { Tcl_SetResult(interp, my_itoa(xctx->lines[c]),TCL_VOLATILE); } else { Tcl_SetResult(interp, "xschem get rects n: layer number out of range", TCL_STATIC); return TCL_ERROR; } } else { Tcl_SetResult(interp, "xschem get rects n: give a layer number", TCL_STATIC); return TCL_ERROR; } } break; case 'n': if(!strcmp(argv[2], "netlist_name")) { /* netlist name if set. If 'fallback' given get default name */ if(argc > 3 && !strcmp(argv[3], "fallback")) { char f[PATH_MAX]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx->netlist_type == CAD_SPICE_NETLIST) { my_snprintf(f, S(f), "%s.spice", get_cell(xctx->current_name, 0)); } else if(xctx->netlist_type == CAD_VHDL_NETLIST) { my_snprintf(f, S(f), "%s.vhdl", get_cell(xctx->current_name, 0)); } else if(xctx->netlist_type == CAD_VERILOG_NETLIST) { my_snprintf(f, S(f), "%s.v", get_cell(xctx->current_name, 0)); } else if(xctx->netlist_type == CAD_TEDAX_NETLIST) { my_snprintf(f, S(f), "%s.tdx", get_cell(xctx->current_name, 0)); } else { my_snprintf(f, S(f), "%s.unknown", get_cell(xctx->current_name, 0)); } if(xctx->netlist_name[0] == '\0') { Tcl_SetResult(interp, f, TCL_VOLATILE); } else { Tcl_SetResult(interp, xctx->netlist_name, TCL_VOLATILE); } } else { Tcl_SetResult(interp, xctx->netlist_name, TCL_VOLATILE); } } else if(!strcmp(argv[2], "netlist_type")) { /* get current netlist type (spice/vhdl/verilog/tedax) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx->netlist_type == CAD_SPICE_NETLIST) { Tcl_SetResult(interp, "spice", TCL_STATIC); } else if(xctx->netlist_type == CAD_VHDL_NETLIST) { Tcl_SetResult(interp, "vhdl", TCL_STATIC); } else if(xctx->netlist_type == CAD_VERILOG_NETLIST) { Tcl_SetResult(interp, "verilog", TCL_STATIC); } else if(xctx->netlist_type == CAD_TEDAX_NETLIST) { Tcl_SetResult(interp, "tedax", TCL_STATIC); } else if(xctx->netlist_type == CAD_SYMBOL_ATTRS) { Tcl_SetResult(interp, "symbol", TCL_STATIC); } else { Tcl_SetResult(interp, "unknown", TCL_STATIC); } } else if(!strcmp(argv[2], "no_draw")) { /* disable drawing */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx->no_draw != 0 ) Tcl_SetResult(interp, "1",TCL_STATIC); else Tcl_SetResult(interp, "0",TCL_STATIC); } else if(!strcmp(argv[2], "ntabs")) { /* get number of additional tabs (0 = only one tab) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); } break; case 'p': if(!strcmp(argv[2], "pinlayer")) { /* layer number for pins */ Tcl_SetResult(interp, my_itoa(PINLAYER),TCL_VOLATILE); } break; case 'r': if(!strcmp(argv[2], "raw_level")) { /* hierarchy level where raw file was loaded */ int ret = -1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx->raw) ret = xctx->raw->level; Tcl_SetResult(interp, my_itoa(ret),TCL_VOLATILE); } else if(!strcmp(argv[2], "rectcolor")) { /* current layer number */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->rectcolor),TCL_VOLATILE); } else if(!strcmp(argv[2], "rects")) { /* (xschem get rects n) number of rectangles on layer 'n' */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { int c = atoi(argv[3]); if(c >=0 && c < cadlayers) { Tcl_SetResult(interp, my_itoa(xctx->rects[c]),TCL_VOLATILE); } else { Tcl_SetResult(interp, "xschem get rects n: layer number out of range", TCL_STATIC); return TCL_ERROR; } } else { Tcl_SetResult(interp, "xschem get rects n: give a layer number", TCL_STATIC); return TCL_ERROR; } } break; case 's': if(!strcmp(argv[2], "sellayer")) { /* layer number for selection */ Tcl_SetResult(interp, my_itoa(SELLAYER),TCL_VOLATILE); } else if(!strcmp(argv[2], "semaphore")) { /* used for debug */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->semaphore),TCL_VOLATILE); } else if(!strcmp(argv[2], "schname")) /* get full path of current sch. if 'n' given get sch of level 'n' */ { int x; /* allows to retrieve name of n-th parent schematic */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) x = atoi(argv[3]); else x = xctx->currsch; if(x < 0 ) x = xctx->currsch + x; if(x<=xctx->currsch && x >= 0) { Tcl_SetResult(interp, xctx->sch[x], TCL_VOLATILE); } } else if(!strcmp(argv[2], "schprop")) /* get schematic "spice" global attributes */ { Tcl_SetResult(interp, xctx->schprop ? xctx->schprop : "", TCL_VOLATILE); } else if(!strcmp(argv[2], "schvhdlprop")) /* get schematic "vhdl" global attributes */ { Tcl_SetResult(interp, xctx->schvhdlprop ? xctx->schvhdlprop : "", TCL_VOLATILE); } else if(!strcmp(argv[2], "schverilogprop")) /* get schematic "verilog" global attributes */ { Tcl_SetResult(interp, xctx->schverilogprop ? xctx->schverilogprop : "", TCL_VOLATILE); } else if(!strcmp(argv[2], "schsymbolprop")) /* get schematic "symbol" global attributes */ { Tcl_SetResult(interp, xctx->schsymbolprop ? xctx->schsymbolprop : "", TCL_VOLATILE); } else if(!strcmp(argv[2], "schtedaxprop")) /* get schematic "tedax" global attributes */ { Tcl_SetResult(interp, xctx->schtedaxprop ? xctx->schtedaxprop : "", TCL_VOLATILE); } else if(!strcmp(argv[2], "sch_path")) /* get hierarchy path. if 'n' given get hierpath of level 'n' */ { int x; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) x = atoi(argv[3]); else x = xctx->currsch; if(x < 0 ) x = xctx->currsch + x; if(x<=xctx->currsch && x >= 0) { Tcl_SetResult(interp, xctx->sch_path[x], TCL_VOLATILE); } } else if(!strcmp(argv[2], "sch_to_compare")) /* if set return schematic current design is compared with */ { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, xctx->sch_to_compare, TCL_VOLATILE); } break; case 't': #ifndef __unix__ if(!strcmp(argv[2], "temp_dir")) { /* get windows temporary dir */ if(win_temp_dir[0] != '\0') Tcl_SetResult(interp, win_temp_dir, TCL_VOLATILE); else { TCHAR tmp_buffer_path[MAX_PATH]; DWORD ret_val = GetTempPath(MAX_PATH, tmp_buffer_path); if(ret_val > MAX_PATH || (ret_val == 0)) { Tcl_SetResult(interp, "xschem get temp_dir failed\n", TCL_STATIC); fprintf(errfp, "xschem get temp_dir: path error\n"); tcleval("exit"); } else { char s[MAX_PATH]; size_t num_char_converted; int err = wcstombs_s(&num_char_converted, s, MAX_PATH, tmp_buffer_path, MAX_PATH); /*unicode TBD*/ if(err != 0) { Tcl_SetResult(interp, "xschem get temp_dir conversion failed\n", TCL_STATIC); fprintf(errfp, "xschem get temp_dir: conversion error\n"); tcleval("exit"); } else { change_to_unix_fn(s); size_t slen = strlen(s); if(s[slen - 1] == '/') s[slen - 1] = '\0'; my_strncpy(win_temp_dir, s, S(win_temp_dir)); dbg(2, "scheduler(): win_temp_dir is %s\n", win_temp_dir); Tcl_SetResult(interp, s, TCL_VOLATILE); } } } } else #endif if(!strcmp(argv[2], "text_svg")) { /* return 1 if using elements in svg export */ if(text_svg != 0 ) Tcl_SetResult(interp, "1",TCL_STATIC); else Tcl_SetResult(interp, "0",TCL_STATIC); } else if(!strcmp(argv[2], "textlayer")) { /* layer number for texts */ Tcl_SetResult(interp, my_itoa(TEXTLAYER), TCL_VOLATILE); } /* top_path="" for main window, ".x1", ".x2", ... for additional windows. * always "" in tabbed interface */ else if(!strcmp(argv[2], "top_path")) { /* get top hier path of current window (always "" for tabbed if) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, xctx->top_path, TCL_VOLATILE); } /* same as above but main window returned as "." */ else if(!strcmp(argv[2], "topwindow")) { /* same as top_path but main window returned as "." */ char *top_path; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} top_path = xctx->top_path[0] ? xctx->top_path : "."; Tcl_SetResult(interp, top_path,TCL_VOLATILE); } break; case 'v': if(!strcmp(argv[2], "version")) { /* return xschem version */ Tcl_SetResult(interp, XSCHEM_VERSION, TCL_VOLATILE); } break; case 'w': if(!strcmp(argv[2], "wirelayer")) { /* layer used for wires */ Tcl_SetResult(interp, my_itoa(WIRELAYER), TCL_VOLATILE); } else if(!strcmp(argv[2], "wires")) { /* number of wires */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa(xctx->wires), TCL_VOLATILE); } break; case 'x': if(!strcmp(argv[2], "xorigin")) { /* x coordinate of origin */ char s[128]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_snprintf(s, S(s), "%.16g", xctx->xorigin); Tcl_SetResult(interp, s,TCL_VOLATILE); } break; case 'y': if(!strcmp(argv[2], "yorigin")) { /* y coordinate of origin */ char s[128]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_snprintf(s, S(s), "%.16g", xctx->yorigin); Tcl_SetResult(interp, s,TCL_VOLATILE); } break; case 'z': if(!strcmp(argv[2], "zoom")) { /* zoom level */ char s[128]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_snprintf(s, S(s), "%.16g", xctx->zoom); Tcl_SetResult(interp, s,TCL_VOLATILE); } break; default: cmd_found = 0; break; } /* switch */ } } /* get_cell cell n_dirs * return result of get_cell function */ else if(!strcmp(argv[1], "get_cell") ) { if(argc > 3) { Tcl_SetResult(interp, (char *)get_cell(argv[2], atoi(argv[3])), TCL_VOLATILE); } } /* get_cell_w_ext cell n_dirs * return result of get_cell_w_ext function */ else if(!strcmp(argv[1], "get_cell_w_ext") ) { if(argc > 3) { Tcl_SetResult(interp, (char *)get_cell_w_ext(argv[2], atoi(argv[3])), TCL_VOLATILE); } } /************ end xschem get subcommands *************/ /* getprop instance|instance_pin|symbol|text ref * * getprop instance inst * Get the full attribute string of 'inst' * * getprop instance inst attr * Get the value of attribute 'attr' * If 'attr has the form 'cell::sym_attr' look up attribute 'sym_attr' * of the symbol referenced by the instance. * * getprop instance_notcl inst attr * Same as above but do not perform tcl substitution * * getprop instance_pin inst pin * Get the full attribute string of pin 'pin' of instance 'inst' * Example: xschem getprop instance_pin x3 MINUS --> name=MINUS dir=in * * getprop instance_pin inst pin pin_attr * Get attribute 'pin_attr' of pin 'pin' of instance 'inst' * Example: xschem getprop instance_pin x3 MINUS dir --> in * * getprop symbol sym_name * Get full attribute string of symbol 'sym_name' * example: * xschem getprop symbol comp_ngspice.sym --> * type=subcircuit * format="@name @pinlist @symname * OFFSET=@OFFSET AMPLITUDE=@AMPLITUDE GAIN=@GAIN ROUT=@ROUT COUT=@COUT" * template="name=x1 OFFSET=0 AMPLITUDE=5 GAIN=100 ROUT=1000 COUT=1p" * * getprop symbol sym_name sym_attr [with_quotes] * Get value of attribute 'sym_attr' of symbol 'sym_name' * 'with_quotes' (default:0) is an integer passed to get_tok_value() * * getprop rect layer num attr [with_quotes] * if '1' is given as 'keep' return backslashes and unescaped quotes if present in value * Get attribute 'attr' of rectangle number 'num' on layer 'layer' * * getprop text num attr * Get attribute 'attr' of text number 'num' * * getprop wire num attr * Get attribute 'attr' of wire number 'num' * * ('inst' can be an instance name or instance number) * ('pin' can be a pin name or pin number) */ else if(!strcmp(argv[1], "getprop")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 && (!strcmp(argv[2], "instance") || !strcmp(argv[2], "instance_notcl"))) { int i; int with_quotes = 0; const char *tmp; if(argc < 4) { Tcl_SetResult(interp, "'xschem getprop instance' needs 1 or 2 additional arguments", TCL_STATIC); return TCL_ERROR; } if((i = get_instance(argv[3])) < 0 ) { Tcl_SetResult(interp, "xschem getprop: instance not found", TCL_STATIC); return TCL_ERROR; } if(!strcmp(argv[2], "instance_notcl")) with_quotes = 2; if(argc < 5) { Tcl_SetResult(interp, xctx->inst[i].prop_ptr, TCL_VOLATILE); } else if(!strcmp(argv[4], "cell::name")) { tmp = xctx->inst[i].name; Tcl_SetResult(interp, (char *) tmp, TCL_VOLATILE); } else if(strstr(argv[4], "cell::") ) { tmp = get_tok_value( (xctx->inst[i].ptr+ xctx->sym)->prop_ptr, argv[4]+6, with_quotes); dbg(1, "scheduler(): xschem getprop: looking up instance %d prop cell::|%s| : |%s|\n", i, argv[4]+6, tmp); Tcl_SetResult(interp, (char *) tmp, TCL_VOLATILE); } else { Tcl_SetResult(interp, (char *)get_tok_value(xctx->inst[i].prop_ptr, argv[4], with_quotes), TCL_VOLATILE); } } else if(argc > 2 && !strcmp(argv[2], "instance_pin")) { /* 0 1 2 3 4 5 */ /* xschem getprop instance_pin X10 PLUS [pin_attr] */ /* xschem getprop instance_pin X10 1 [pin_attr] */ int inst, n; size_t tmp; char *subtok=NULL; const char *value=NULL; if(argc < 5) { Tcl_SetResult(interp, "xschem getprop instance_pin needs 2 or 3 additional arguments", TCL_STATIC); return TCL_ERROR; } if((inst = get_instance(argv[3])) < 0 ) { Tcl_SetResult(interp, "xschem getprop: instance not found", TCL_STATIC); return TCL_ERROR; } n = get_inst_pin_number(inst, argv[4]); if(n>=0 && n < (xctx->inst[inst].ptr+ xctx->sym)->rects[PINLAYER]) { if(argc < 6) { Tcl_SetResult(interp, (xctx->inst[inst].ptr+ xctx->sym)->rect[PINLAYER][n].prop_ptr, TCL_VOLATILE); } else { tmp = 100 + strlen(argv[4]) + strlen(argv[5]); subtok = my_malloc(_ALLOC_ID_,tmp); my_snprintf(subtok, tmp, "%s(%s)", argv[5], argv[4]); value = get_tok_value(xctx->inst[inst].prop_ptr,subtok,0); if(!value[0]) { my_snprintf(subtok, tmp, "%s(%d)", argv[5], n); value = get_tok_value(xctx->inst[inst].prop_ptr,subtok,0); } if(!value[0]) { value = get_tok_value((xctx->inst[inst].ptr+ xctx->sym)->rect[PINLAYER][n].prop_ptr,argv[5],0); } if(value[0] != 0) { char *ss; int slot; if((ss = strchr(xctx->inst[inst].instname, ':')) ) { sscanf(ss + 1, "%d", &slot); if(strstr(value, ":")) value = find_nth(value, ":", "", 0, slot); } Tcl_SetResult(interp, (char *)value, TCL_VOLATILE); } my_free(_ALLOC_ID_, &subtok); } } /* xschem getprop symbol lm358.sym [type] */ } else if(!strcmp(argv[2], "symbol")) { int i; if(argc < 4) { Tcl_SetResult(interp, "xschem getprop symbol needs 1 or 2 or 3 additional arguments", TCL_STATIC); return TCL_ERROR; } i = get_symbol(argv[3]); if( i == -1) { Tcl_SetResult(interp, "Symbol not found", TCL_STATIC); return TCL_ERROR; } if(argc < 5) Tcl_SetResult(interp, xctx->sym[i].prop_ptr, TCL_VOLATILE); else if(argc == 5) Tcl_SetResult(interp, (char *)get_tok_value(xctx->sym[i].prop_ptr, argv[4], 0), TCL_VOLATILE); else if(argc > 5) Tcl_SetResult(interp, (char *)get_tok_value(xctx->sym[i].prop_ptr, argv[4], atoi(argv[5])), TCL_VOLATILE); } else if(!strcmp(argv[2], "rect")) { /* xschem getprop rect c n token */ if(argc < 6) { Tcl_SetResult(interp, "xschem getprop rect needs ", TCL_STATIC); return TCL_ERROR; } else { int with_quotes = 0; int c = atoi(argv[3]); int n = atoi(argv[4]); if(argc > 6) with_quotes = atoi(argv[6]); Tcl_SetResult(interp, (char *)get_tok_value(xctx->rect[c][n].prop_ptr, argv[5], with_quotes), TCL_VOLATILE); } } else if(!strcmp(argv[2], "text")) { /* xschem getprop text n token */ if(argc < 5) { Tcl_SetResult(interp, "xschem getprop text needs ", TCL_STATIC); return TCL_ERROR; } else { int n = atoi(argv[3]); Tcl_SetResult(interp, (char *)get_tok_value(xctx->text[n].prop_ptr, argv[4], 2), TCL_VOLATILE); } } else if(!strcmp(argv[2], "wire")) { /* xschem getprop wire n token */ if(argc < 5) { Tcl_SetResult(interp, "xschem getprop wire needs ", TCL_STATIC); return TCL_ERROR; } else { int n = atoi(argv[3]); Tcl_SetResult(interp, (char *)get_tok_value(xctx->wire[n].prop_ptr, argv[4], 2), TCL_VOLATILE); } } } /* get_tok str tok [with_quotes] * get value of token 'tok' in string 'str' * 'with_quotes' (default:0) is an integer passed to get_tok_value() */ else if(!strcmp(argv[1], "get_tok") ) { char *s=NULL; int t; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 4) {Tcl_SetResult(interp, "Missing arguments", TCL_STATIC);return TCL_ERROR;} if(argc == 5) t = atoi(argv[4]); else t = 0; my_strdup(_ALLOC_ID_, &s, get_tok_value(argv[2], argv[3], t)); Tcl_SetResult(interp, s, TCL_VOLATILE); my_free(_ALLOC_ID_, &s); } /* get_tok_size * Get length of last looked up attribute name (not its value) * if returned value is 0 it means that last searched attribute did not exist */ else if(!strcmp(argv[1], "get_tok_size") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_SetResult(interp, my_itoa((int)xctx->tok_size), TCL_VOLATILE); } /* globals * Return various global variables used in the program */ else if(!strcmp(argv[1], "globals")) { static char res[8192]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_ResetResult(interp); my_snprintf(res, S(res), "*******global variables:*******\n"); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areax1=%d\n", xctx->areax1); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areay1=%d\n", xctx->areay1); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areax2=%d\n", xctx->areax2); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areay2=%d\n", xctx->areay2); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areaw=%d\n", xctx->areaw); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areah=%d\n", xctx->areah); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "xrect[0].width=%d\n", xctx->xrect[0].width); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "xrect[0].height=%d\n", xctx->xrect[0].height); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "INT_WIDTH(lw)=%d\n", INT_WIDTH(xctx->lw)); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "lw=%g\n", xctx->lw); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "wires=%d\n", xctx->wires); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "instances=%d\n", xctx->instances); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "symbols=%d\n", xctx->symbols); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "lastsel=%d\n", xctx->lastsel); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "texts=%d\n", xctx->texts); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "maxt=%d\n", xctx->maxt); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "maxw=%d\n", xctx->maxw); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "maxi=%d\n", xctx->maxi); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "maxsel=%d\n", xctx->maxsel); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "zoom=%.16g\n", xctx->zoom); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "xorigin=%.16g\n", xctx->xorigin); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "yorigin=%.16g\n", xctx->yorigin); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "maxs=%d\n", xctx->maxs); Tcl_AppendResult(interp, res, NULL); for(i=0;i<8; ++i) { my_snprintf(res, S(res), "rects[%d]=%d\n", i, xctx->rects[i]); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "lines[%d]=%d\n", i, xctx->lines[i]); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "maxr[%d]=%d\n", i, xctx->maxr[i]); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "maxl[%d]=%d\n", i, xctx->maxl[i]); Tcl_AppendResult(interp, res, NULL); } my_snprintf(res, S(res), "current_dirname=%s\n", xctx->current_dirname); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "current_name=%s\n", xctx->current_name); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "currsch=%d\n", xctx->currsch); Tcl_AppendResult(interp, res, NULL); for(i=0;i<=xctx->currsch; ++i) { my_snprintf(res, S(res), "previous_instance[%d]=%d\n", i,xctx->previous_instance[i]); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "sch_path[%d]=%s\n",i,xctx->sch_path[i]? xctx->sch_path[i]:""); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "sch[%d]=%s\n",i,xctx->sch[i]); Tcl_AppendResult(interp, res, NULL); } my_snprintf(res, S(res), "modified=%d\n", xctx->modified); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areaw=%d\n", xctx->areaw); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "areah=%d\n", xctx->areah); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "color_ps=%d\n", color_ps); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "hilight_nets=%d\n", xctx->hilight_nets); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "semaphore=%d\n", xctx->semaphore); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "ui_state=%d\n", xctx->ui_state); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "prep_net_structs=%d\n", xctx->prep_net_structs); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "prep_hi_structs=%d\n", xctx->prep_hi_structs); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "prep_hash_inst=%d\n", xctx->prep_hash_inst); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "prep_hash_wires=%d\n", xctx->prep_hash_wires); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "need_reb_sel_arr=%d\n", xctx->need_reb_sel_arr); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "undo_type=%d\n", xctx->undo_type); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "******* end global variables:*******\n"); Tcl_AppendResult(interp, res, NULL); my_snprintf(res, S(res), "******* Compile options:*******\n"); Tcl_AppendResult(interp, res, NULL); #ifdef HAS_DUP2 my_snprintf(res, S(res), "HAS_DUP2=%d\n", HAS_DUP2); Tcl_AppendResult(interp, res, NULL); #endif #ifdef HAS_PIPE my_snprintf(res, S(res), "HAS_PIPE=%d\n", HAS_PIPE); Tcl_AppendResult(interp, res, NULL); #endif #ifdef HAS_POPEN my_snprintf(res, S(res), "HAS_POPEN=%d\n", HAS_POPEN); Tcl_AppendResult(interp, res, NULL); #endif #if HAS_CAIRO==1 my_snprintf(res, S(res), "HAS_CAIRO=%d\n", HAS_CAIRO); Tcl_AppendResult(interp, res, NULL); #endif #ifdef XSCHEM_SHAREDIR my_snprintf(res, S(res), "XSCHEM_SHAREDIR=%s\n", XSCHEM_SHAREDIR); Tcl_AppendResult(interp, res, NULL); #endif #ifdef PREFIX my_snprintf(res, S(res), "PREFIX=%s\n", PREFIX); Tcl_AppendResult(interp, res, NULL); #endif #ifdef USER_CONF_DIR my_snprintf(res, S(res), "USER_CONF_DIR=%s\n", USER_CONF_DIR); Tcl_AppendResult(interp, res, NULL); #endif #ifdef XSCHEM_LIBRARY_PATH my_snprintf(res, S(res), "XSCHEM_LIBRARY_PATH=%s\n", XSCHEM_LIBRARY_PATH); Tcl_AppendResult(interp, res, NULL); #endif #ifdef HAS_SNPRINTF my_snprintf(res, S(res), "HAS_SNPRINTF=%s\n", HAS_SNPRINTF); Tcl_AppendResult(interp, res, NULL); #endif } /* go_back * Go up one level (pop) in hierarchy */ else if(!strcmp(argv[1], "go_back")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} go_back(1); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'h': /*----------------------------------------------*/ /* hash_file file [skip_path_lines] * Do a simple hash of 'file' * 'skip_path_lines' is an integer (default: 0) passed to hash_file() */ if(!strcmp(argv[1], "hash_file")) { unsigned int h; char s[40]; if(argc > 2) { if(argc > 3) { h = hash_file(argv[2], atoi(argv[3])); } else { h = hash_file(argv[2], 0); } my_snprintf(s, S(s), "%u", h); Tcl_SetResult(interp, s, TCL_VOLATILE); } } /* hash_string str * Do a simple hashing of string 'str' */ else if(!strcmp(argv[1], "hash_string")) { if(argc > 2) { unsigned int h; char s[50]; h = str_hash(argv[2]); my_snprintf(s, S(s), "%u", h); Tcl_SetResult(interp, s, TCL_VOLATILE); } } /* help * Print command help */ else if(!strcmp(argv[1], "help")) { xschem_cmd_help(argc, argv); } /* hier_psprint [file] * Hierarchical postscript / pdf print * if 'file' is not given show a fileselector dialog box */ else if(!strcmp(argv[1], "hier_psprint")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { my_strncpy(xctx->plotfile, argv[2], S(xctx->plotfile)); } hier_psprint(NULL, 1); Tcl_ResetResult(interp); } /* hilight [drill] * Highlight selected element/pins/labels/nets * if 'drill' is given propagate net highlights through conducting elements * (elements that have the 'propag' attribute on pins ) */ else if(!strcmp(argv[1], "hilight")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->enable_drill = 0; if(argc >=3 && !strcmp(argv[2], "drill")) xctx->enable_drill = 1; hilight_net(0); redraw_hilights(0); Tcl_ResetResult(interp); } /* hilight_instname inst * Highlight instance 'inst' * 'inst' can be an instance name or number */ else if(!strcmp(argv[1], "hilight_instname")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { int inst; char *type; int incr_hi; xctx->enable_drill=0; incr_hi = tclgetboolvar("incr_hilight"); prepare_netlist_structs(0); if((inst = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem hilight_instname: instance not found", TCL_STATIC); return TCL_ERROR; } else { type = (xctx->inst[inst].ptr+ xctx->sym)->type; if( type && xctx->inst[inst].node && IS_LABEL_SH_OR_PIN(type) ) { /* instance must have a pin! */ /* sets xctx->hilight_nets=1 */ if(!bus_hilight_hash_lookup(xctx->inst[inst].node[0], xctx->hilight_color, XINSERT_NOREPLACE)) { dbg(1, "xschem hilight_instname: node=%s\n", xctx->inst[inst].node[0]); if(incr_hi) incr_hilight_color(); } } else { dbg(1, "xschem hilight_instname: setting hilight flag on inst %d\n",inst); /* xctx->hilight_nets=1; */ /* done in hilight_hash_lookup() */ xctx->inst[inst].color = xctx->hilight_color; inst_hilight_hash_lookup(inst, xctx->hilight_color, XINSERT_NOREPLACE); if(incr_hi) incr_hilight_color(); } dbg(1, "hilight_nets=%d\n", xctx->hilight_nets); if(xctx->hilight_nets) propagate_hilights(1, 0, XINSERT_NOREPLACE); redraw_hilights(0); } } Tcl_ResetResult(interp); } /* hilight_netname net * Highlight net name 'net' */ else if(!strcmp(argv[1], "hilight_netname")) { int ret = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { ret = hilight_netname(argv[2]); } Tcl_SetResult(interp, ret ? "1" : "0" , TCL_STATIC); } else { cmd_found = 0;} break; case 'i': /*----------------------------------------------*/ /* instance sym_name x y rot flip [prop] [n] * Place a new instance of symbol 'sym_name' at position x,y, * rotation and flip set to 'rot', 'flip' * if 'prop' is given it is the new instance attribute * string (default: symbol template string) * if 'n' is given it must be 0 on first call * and non zero on following calls * It is used only for efficiency reasons if placing multiple instances */ if(!strcmp(argv[1], "instance")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc==7) { /* pos sym_name x y rot */ place_symbol(-1, argv[2], atof(argv[3]), atof(argv[4]), (short)atoi(argv[5]), /* flip prop draw first to_push_undo */ (short)atoi(argv[6]),NULL, 3, 1, 1); set_modify(1); } else if(argc==8) { place_symbol(-1, argv[2], atof(argv[3]), atof(argv[4]), (short)atoi(argv[5]), (short)atoi(argv[6]), argv[7], 3, 1, 1); set_modify(1); } else if(argc==9) { int x = !(atoi(argv[8])); place_symbol(-1, argv[2], atof(argv[3]), atof(argv[4]), (short)atoi(argv[5]), (short)atoi(argv[6]), argv[7], 0, x, 1); set_modify(1); } } /* instance_bbox inst * return instance and symbol bounding boxes * 'inst' can be an instance name or number */ else if(!strcmp(argv[1], "instance_bbox")) { int i; char s[200]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { if((i = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem instance_bbox: instance not found", TCL_STATIC); return TCL_ERROR; } my_snprintf(s, S(s), "Instance: %g %g %g %g", xctx->inst[i].x1, xctx->inst[i].y1, xctx->inst[i].x2, xctx->inst[i].y2); Tcl_AppendResult(interp, s, NULL); my_snprintf(s, S(s), "\nSymbol: %g %g %g %g", (xctx->inst[i].ptr+ xctx->sym)->minx, (xctx->inst[i].ptr+ xctx->sym)->miny, (xctx->inst[i].ptr+ xctx->sym)->maxx, (xctx->inst[i].ptr+ xctx->sym)->maxy); Tcl_AppendResult(interp, s, NULL); } } /* instance_coord [instance] * Return instance name, symbol name, x placement coord, y placement coord, rotation and flip * of selected instances * if 'instance' is given (instance name or number) return data about specified instance * Example: * xschem [~] xschem instance_coord * {R5} {res.sym} 260 260 0 0 * {C1} {capa.sym} 150 150 1 1 */ else if(!strcmp(argv[1], "instance_coord")) { /* xschem instances_to_net PLUS */ xSymbol *symbol; short flip, rot; double x0,y0; int n, i = 0; int user_inst = -1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { i = get_instance(argv[2]); if(i < 0) { Tcl_SetResult(interp, "xschem instance_net: instance not found", TCL_STATIC); return TCL_ERROR; } user_inst = i; } rebuild_selected_array(); for(n=0; user_inst >=0 || n < xctx->lastsel; ++n) { if(user_inst >=0 || xctx->sel_array[n].type == ELEMENT) { char srot[16], sflip[16], sx0[70], sy0[70]; if(user_inst == -1) i = xctx->sel_array[n].n; else i = user_inst; x0 = xctx->inst[i].x0; y0 = xctx->inst[i].y0; rot = xctx->inst[i].rot; flip = xctx->inst[i].flip; symbol = xctx->sym + xctx->inst[i].ptr; my_snprintf(srot, S(srot), "%d", rot); my_snprintf(sflip, S(sflip), "%d", flip); my_snprintf(sx0, S(sx0), "%g", x0); my_snprintf(sy0, S(sy0), "%g", y0); Tcl_AppendResult(interp, "{", xctx->inst[i].instname, "} ", "{", symbol->name, "} ", sx0, " ", sy0, " ", srot, " ", sflip, "\n", NULL); if(user_inst >= 0) break; } } } /* instance_list * Return a list of 3-items. Each 3-item is * an instance name followed by the symbol reference and symbol type. * Example: xschem instance_list --> * {x1} {sky130_tests/bandgap.sym} {subcircuit}} {...} {...} {...} ... */ else if(!strcmp(argv[1], "instance_list")) { int i; char *s = NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 0; i < xctx->instances; ++i) { const char *name = xctx->inst[i].name ? xctx->inst[i].name : ""; char *instname = xctx->inst[i].instname ? xctx->inst[i].instname : ""; char *type = (xctx->inst[i].ptr + xctx->sym)->type; type = type ? type : ""; if(i > 0) my_mstrcat(_ALLOC_ID_, &s, "\n", NULL); my_mstrcat(_ALLOC_ID_, &s, "{", instname, "} {", name, "} {", type, "}", NULL); } Tcl_SetResult(interp, (char *)s, TCL_VOLATILE); my_free(_ALLOC_ID_, &s); } /* instance_net inst pin * Return the name of the net attached to pin 'pin' of instance 'inst' * Example: xschem instance_net x3 MINUS --> REF */ else if(!strcmp(argv[1], "instance_net")) { /* xschem instance_net inst pin */ int no_of_pins, i, p, multip; const char *str_ptr=NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 4) { Tcl_SetResult(interp, "xschem instance_net needs 2 additional arguments", TCL_STATIC); return TCL_ERROR; } if((i = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem instance_net: instance not found", TCL_STATIC); return TCL_ERROR; } prepare_netlist_structs(0); no_of_pins= (xctx->inst[i].ptr+ xctx->sym)->rects[PINLAYER]; for(p=0;pinst[i].ptr+ xctx->sym)->rect[PINLAYER][p].prop_ptr, "name",0), argv[3])) { str_ptr = net_name(i,p, &multip, 0, 1); break; } } /* /20171029 */ if(p>=no_of_pins) { Tcl_SetResult(interp, "Pin not found", TCL_STATIC); return TCL_ERROR; } Tcl_SetResult(interp, (char *)str_ptr, TCL_VOLATILE); } /* instance_nodemap inst [pin] * Return the instance name followed by a list of 'pin net' associations * example: xschem instance_nodemap x3 * --> x3 PLUS LED OUT LEVEL MINUS REF * instance x3 pin PLUS is attached to net LED, pin OUT to net LEVEL and so on... * If 'pin' is given restrict map to only that pin */ else if(!strcmp(argv[1], "instance_nodemap")) { /* xschem instance_nodemap [instance_name] */ int p, no_of_pins; int inst = -1, first=1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} prepare_netlist_structs(0); if(argc > 2) { inst = get_instance(argv[2]); if(inst >=0) { Tcl_AppendResult(interp, xctx->inst[inst].instname, " ", NULL); no_of_pins= (xctx->inst[inst].ptr+ xctx->sym)->rects[PINLAYER]; for(p=0;pinst[inst].ptr+ xctx->sym)->rect[PINLAYER][p].prop_ptr, "name",0); if(!pin[0]) pin = "--ERROR--"; if(argc > 3 && strcmp(argv[3], pin)) continue; if(first == 0) Tcl_AppendResult(interp, " ", NULL); Tcl_AppendResult(interp, pin, " ", xctx->inst[inst].node && xctx->inst[inst].node[p] ? xctx->inst[inst].node[p] : "{}", NULL); first = 0; } } } } /* instance_pin_coord inst attr value * Return the name and coordinates of pin with * attribute 'attr' set to 'value' of instance 'inst' * 'inst can be an instance name or a number * Example: xschem instance_pin_coord x3 name MINUS --> {MINUS} 600 -840 */ else if(!strcmp(argv[1], "instance_pin_coord")) { /* 0 1 2 3 4 * xschem instance_pin_coord m12 pinnumber 2 * xschem instance_pin_coord U3:2 pinnumber 5 * xschem instance_pin_coord m12 name d * returns pin_name x y */ xSymbol *symbol; xRect *rct; double pinx0, piny0; char num[60]; int p, i, no_of_pins, slot; const char *pin; char *ss; char *tmpstr = NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 5) { Tcl_SetResult(interp, "xschem instance_pin_coord requires an instance, a pin attribute and a value", TCL_STATIC); return TCL_ERROR; } i = get_instance(argv[2]); if(i < 0) { Tcl_SetResult(interp, "", TCL_STATIC); return TCL_OK; } symbol = xctx->sym + xctx->inst[i].ptr; no_of_pins= symbol->rects[PINLAYER]; rct=symbol->rect[PINLAYER]; /* slotted devices: name= U1:2, pinnumber=2:5 */ slot = -1; tmpstr = my_malloc(_ALLOC_ID_, sizeof(xctx->inst[i].instname)); if((ss=strchr(xctx->inst[i].instname, ':')) ) { sscanf(ss+1, "%s", tmpstr); if(isonlydigit(tmpstr)) { slot = atoi(tmpstr); } } for(p = 0;p < no_of_pins; p++) { pin = get_tok_value(rct[p].prop_ptr,argv[3],0); if(slot > 0 && !strcmp(argv[3], "pinnumber") && strstr(pin, ":")) pin = find_nth(pin, ":", "", 0, slot); if(!strcmp(pin, argv[4])) break; } if(p >= no_of_pins) { Tcl_SetResult(interp, "", TCL_STATIC); return TCL_OK; } get_inst_pin_coord(i, p, &pinx0, &piny0); my_snprintf(num, S(num), "{%s} %g %g", get_tok_value(rct[p].prop_ptr, "name", 0), pinx0, piny0); Tcl_SetResult(interp, num, TCL_VOLATILE); my_free(_ALLOC_ID_, &tmpstr); } /* instance_pins inst * Return list of pins of instance 'inst' * 'inst can be an instance name or a number */ else if(!strcmp(argv[1], "instance_pins")) { char *pins = NULL; int p, i, no_of_pins; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { prepare_netlist_structs(0); if((i = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem instance_pins: instance not found", TCL_STATIC); return TCL_ERROR; } no_of_pins= (xctx->inst[i].ptr+ xctx->sym)->rects[PINLAYER]; for(p=0;pinst[i].ptr+ xctx->sym)->rect[PINLAYER][p].prop_ptr, "name",0); if(!pin[0]) pin = "--ERROR--"; my_mstrcat(_ALLOC_ID_, &pins, "{", pin, "}", NULL); if(p< no_of_pins-1) my_strcat(_ALLOC_ID_, &pins, " "); } Tcl_SetResult(interp, pins, TCL_VOLATILE); my_free(_ALLOC_ID_, &pins); } } /* instance_pos inst * Get number (position) of instance name 'inst' */ else if(!strcmp(argv[1], "instance_pos")) { int i; char s[30]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { i = get_instance(argv[2]); my_snprintf(s, S(s), "%d", i); Tcl_SetResult(interp, s, TCL_VOLATILE); } } /* instances_to_net net * Return list of instances names and pins attached to net 'net' * Example: xschem instances_to_net PANEL * --> { {Vsw} {plus} {580} {-560} } { {p2} {p} {660} {-440} } * { {Vpanel1} {minus} {600} {-440} } */ else if(!strcmp(argv[1], "instances_to_net")) { /* xschem instances_to_net PLUS */ xSymbol *symbol; xRect *rct; double pinx0, piny0; char *pins = NULL; int p, i, no_of_pins; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} prepare_netlist_structs(0); if(argc < 3) { Tcl_SetResult(interp, "xschem instances_to_net requires a net name argument", TCL_STATIC); return TCL_ERROR; } for(i = 0;i < xctx->instances; ++i) { symbol = xctx->sym + xctx->inst[i].ptr; no_of_pins= symbol->rects[PINLAYER]; rct=symbol->rect[PINLAYER]; for(p = 0;p < no_of_pins; p++) { const char *pin; char xx[70], yy[70]; pin = get_tok_value(rct[p].prop_ptr, "name",0); if(!pin[0]) pin = "--ERROR--"; if(xctx->inst[i].node[p] && !strcmp(xctx->inst[i].node[p], argv[2]) && !IS_LABEL_SH_OR_PIN( (xctx->inst[i].ptr+xctx->sym)->type )) { my_mstrcat(_ALLOC_ID_, &pins, "{ {", xctx->inst[i].instname, "} {", pin, NULL); get_inst_pin_coord(i, p, &pinx0, &piny0); my_strncpy(xx, dtoa(pinx0), S(xx)); my_strncpy(yy, dtoa(piny0), S(yy)); my_mstrcat(_ALLOC_ID_, &pins, "} {", xx, "} {", yy, "} } ", NULL); } } } Tcl_SetResult(interp, pins ? pins : "", TCL_VOLATILE); my_free(_ALLOC_ID_, &pins); } /* is_symgen symbol * tell if 'symbol' is agenerator (symbol(param1,param2,...) */ else if(!strcmp(argv[1], "is_symgen")) { char s[30]; if(argc > 2) { my_snprintf(s, S(s), "%d", is_generator(argv[2])); Tcl_SetResult(interp, s, TCL_VOLATILE); } } else { cmd_found = 0;} break; case 'l': /*----------------------------------------------*/ /* line [x1 y1 x2 y2] [pos] [propstring] [draw] * if 'x1 y1 x2 y2'is given place line on current * layer (rectcolor) at indicated coordinates. * if 'pos' is given insert at given position in rectangle array. * if 'pos' set to -1 append to last element in line array. * 'propstring' is the attribute string. Set to empty if not given. * if 'draw' is set to 1 (default) draw the new object, else don't * If no coordinates are given start a GUI operation of line placement */ if(!strcmp(argv[1], "line")) { double x1,y1,x2,y2; int pos, save; int draw = 1; const char *prop_str = NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 5) { x1=atof(argv[2]); y1=atof(argv[3]); x2=atof(argv[4]); y2=atof(argv[5]); ORDER(x1,y1,x2,y2); pos=-1; if(argc > 6) pos=atoi(argv[6]); if(argc > 7) prop_str = argv[7]; if(argc > 8) draw = atoi(argv[8]); storeobject(pos, x1,y1,x2,y2,LINE,xctx->rectcolor,0,prop_str); if(draw) { save = xctx->draw_window; xctx->draw_window = 1; drawline(xctx->rectcolor,NOW, x1,y1,x2,y2, 0, NULL); xctx->draw_window = save; } set_modify(1); } else { xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTLINE; } } /* line_width n * set line width to floating point number 'n' */ else if(!strcmp(argv[1], "line_width")) { if(argc > 2) { change_linewidth(atof(argv[2])); Tcl_ResetResult(interp); } } /* list_hierarchy * List all schematics at or below current hierarchy with modification times. * Example: xschem list_hiearchy * --> * 20230302_003134 {/home/.../ngspice/solar_panel.sch} * 20230211_010031 {/home/.../ngspice/pv_ngspice.sch} * 20221011_175308 {/home/.../ngspice/diode_ngspice.sch} * 20221014_091945 {/home/.../ngspice/comp_ngspice.sch} */ else if(!strcmp(argv[1], "list_hierarchy")) { char *res = NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_ResetResult(interp); hier_psprint(&res, 2); Tcl_SetResult(interp, res, TCL_VOLATILE); my_free(_ALLOC_ID_, &res); } /* list_hilights [sep] * Sorted list of highlight nets, separated by character 'sep' (default: space) */ else if(!strcmp(argv[1], "list_hilights")) { const char *sep; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { sep = argv[2]; } else { sep = "{ }"; } list_hilights(); tclvareval("join [lsort -decreasing -dictionary {", tclresult(), "}] ", sep, NULL); } /* list_tokens str with_quotes * List tokens in string 'str' * with_quotes: * 0: eat non escaped quotes (") * 1: return unescaped quotes as part of the token value if they are present * 2: eat backslashes */ else if(!strcmp(argv[1], "list_tokens")) { if(argc > 3) { Tcl_ResetResult(interp); Tcl_SetResult(interp, (char *)list_tokens(argv[2], atoi(argv[3])), TCL_VOLATILE); } } /* load f [symbol|gui|noundoreset|nofullzoom] * Load a new file 'f'. * 'gui': ask to save modified file or warn if opening an already * open file or opening a new(not existing) file. * 'noundoreset': do not reset the undo history * 'symbol': do not load symbols (used if loading a symbol instead of a schematic) * 'nofullzoom': do not do a full zoom on new schematic. */ else if(!strcmp(argv[1], "load") ) { int load_symbols = 1, force = 1, undo_reset = 1, nofullzoom = 0; size_t i; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { for(i = 3; i < argc; ++i) { if(!strcmp(argv[i], "symbol")) load_symbols = 0; if(!strcmp(argv[i], "gui")) force = 0; if(!strcmp(argv[i], "noundoreset")) undo_reset = 0; if(!strcmp(argv[i], "nofullzoom")) nofullzoom = 1; } } if(argc>2) { char f[PATH_MAX + 100]; my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); if(force || !has_x || !xctx->modified || save(1) != -1 ) { /* save(1)==-1 --> user cancel */ char win_path[WINDOW_PATH_SIZE]; int skip = 0; dbg(1, "scheduler(): load: filename=%s\n", argv[2]); my_strncpy(f, abs_sym_path(f, ""), S(f)); if(!force && f[0] && check_loaded(f, win_path) ) { char msg[PATH_MAX + 100]; my_snprintf(msg, S(msg), "tk_messageBox -type okcancel -icon warning -parent [xschem get topwindow] " "-message {Warning: %s already open.}", f); if(has_x) { tcleval(msg); if(strcmp(tclresult(), "ok")) skip = 1; } else dbg(0, "xschem load: %s already open: %s\n", f, win_path); } if(!skip) { clear_all_hilights(); unselect_all(1); if(!undo_reset) xctx->push_undo(); xctx->currsch = 0; remove_symbols(); if(!nofullzoom) { xctx->zoom=CADINITIALZOOM; xctx->mooz=1/CADINITIALZOOM; xctx->xorigin=CADINITIALX; xctx->yorigin=CADINITIALY; } dbg(1, "scheduler: undo_reset=%d\n", undo_reset); load_schematic(load_symbols, f, undo_reset, !force); tclvareval("update_recent_file {", f, "}", NULL); my_strdup(_ALLOC_ID_, &xctx->sch_path[xctx->currsch], "."); if(xctx->portmap[xctx->currsch].table) str_hash_free(&xctx->portmap[xctx->currsch]); str_hash_init(&xctx->portmap[xctx->currsch], HASHSIZE); xctx->sch_path_hash[xctx->currsch] = 0; xctx->sch_inst_number[xctx->currsch] = 1; if(nofullzoom) draw(); else zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); } } } else if(argc==2) { ask_new_file(); } Tcl_SetResult(interp, xctx->sch[xctx->currsch], TCL_STATIC); } /* load_new_window [f] * Load schematic in a new tab/window. If 'f' not given prompt user * if 'f' is given as empty '{}' then open untitled.sch */ else if(!strcmp(argv[1], "load_new_window") ) { char f[PATH_MAX + 100]; int cancel = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, abs_sym_path(tclresult(), ""), S(f)); } else { tcleval("load_file_dialog {Load file} *.\\{sch,sym\\} INITIALLOADDIR"); if(tclresult()[0]) { my_snprintf(f, S(f), "%s", tclresult()); } else { cancel = 1; } } if(!cancel) { if(f[0]) { new_schematic("create", "noconfirm", f, 1); tclvareval("update_recent_file {", f, "}", NULL); } else { new_schematic("create", NULL, NULL, 1); } } Tcl_ResetResult(interp); } /* log f * If 'f' is given output stderr messages to file 'f' * if 'f' is not given and a file log is open, close log * file and resume logging to stderr */ else if(!strcmp(argv[1], "log")) { /* added check to avoid multiple open */ if(argc > 2 && errfp == stderr ) { char f[PATH_MAX + 100]; FILE *fp; my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); fp = fopen(f, "w"); if(fp) errfp = fp; else dbg(0, "xschem log: problems opening file %s\n", f); } else if(argc==2 && errfp != stderr) { fclose(errfp); errfp=stderr; } } /* logic_get_net net_name * Get logic state of net named 'net_name' * Returns 0, 1, 2, 3 for logic levels 0, 1, X, Z or nothing if no net found. */ else if(!strcmp(argv[1], "logic_get_net")) { static char s[2]; my_strncpy(s, "2", S(s)); if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_ResetResult(interp); if(argc > 2) { Hilight_hashentry *entry; entry = bus_hilight_hash_lookup(argv[2], 0, XLOOKUP); if(entry) { switch(entry->value) { case -5: s[0] = '1'; break; case -12: s[0] = '0'; break; case -1: s[0] = '2'; /* Unknown (X) */ break; case -13: s[0] = '3'; /* Hi-Z (Z) */ break; default: s[0] = '2'; break; } } Tcl_SetResult(interp, s, TCL_VOLATILE); } } /* logic_set_net net_name n [num] * set 'net_name' to logic level 'n' 'num' times. * 'n': * 0 set to logic value 0 * 1 set to logic value 1 * 2 set to logic value X * 3 set to logic value Z * -1 toggle logic valie (1->0, 0->1) * the 'num' parameter is essentially useful only with 'toggle' (-1) value */ else if(!strcmp(argv[1], "logic_set_net")) { int num = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 4 ) num = atoi(argv[4]); if(argc > 3) { int n = atoi(argv[3]); if(n == 4) n = -1; logic_set(n, num, argv[2]); } Tcl_ResetResult(interp); } /* logic_set n [num] * set selected nets, net labels or pins to logic level 'n' 'num' times. * 'n': * 0 set to logic value 0 * 1 set to logic value 1 * 2 set to logic value X * 3 set to logic value Z * -1 toggle logic valie (1->0, 0->1) * the 'num' parameter is essentially useful only with 'toggle' (-1) value */ else if(!strcmp(argv[1], "logic_set")) { int num = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3 ) num = atoi(argv[3]); if(argc > 2) { int n = atoi(argv[2]); if(n == 4) n = -1; logic_set(n, num, NULL); } Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'm': /*----------------------------------------------*/ /* make_sch * Make a schematic from selected symbol */ if(!strcmp(argv[1], "make_sch")) /* make schematic from selected symbol 20171004 */ { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} create_sch_from_sym(); Tcl_ResetResult(interp); } /* make_sch_from_sel * Create an LCC instance from selection and place it instead of selection * also ask if a symbol (.sym) file needs to be created */ else if(!strcmp(argv[1], "make_sch_from_sel")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} make_schematic_symbol_from_sel(); Tcl_ResetResult(interp); } /* make_symbol * From current schematic (circuit.sch) create a symbol (circuit.sym) * using ipin.sym, opin.sym, iopin.sym in schematic * to deduce symbol interface pins. */ else if(!strcmp(argv[1], "make_symbol")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(has_x) tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] " "-message {do you want to make symbol view ?}"); if(!has_x || strcmp(tclresult(), "ok")==0) { save_schematic(xctx->sch[xctx->currsch]); make_symbol(); } Tcl_ResetResult(interp); } /* merge [f] * Merge another file. if 'f' not given prompt user. */ else if(!strcmp(argv[1], "merge")) { char f[PATH_MAX + 100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 3) { merge_file(0, ""); /* 2nd param not used for merge 25122002 */ } else { my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); merge_file(0, f); } Tcl_ResetResult(interp); } /* move_instance inst x y rot flip [nodraw] [noundo] * resets instance coordinates, and rotaton/flip. A dash will keep existing value * if 'nodraw' is given do not draw the moved instance * if 'noundo' is given operation is not undoable */ else if(!strcmp(argv[1], "move_instance")) { int undo = 1, dr = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 7) { int i; for(i = 7; i < argc; i++) { if(!strcmp(argv[i], "nodraw")) dr = 0; if(!strcmp(argv[i], "noundo")) undo = 0; } } if(argc > 6) { int i, s_pnetname, floaters; if((i = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem move_instance: instance not found", TCL_STATIC); return TCL_ERROR; } s_pnetname = tclgetboolvar("show_pin_net_names"); floaters = set_modify(1); floaters |= s_pnetname; if(undo) xctx->push_undo(); if(dr && !floaters) { bbox(START,0.0,0.0,0.0,0.0); bbox(ADD, xctx->inst[i].x1, xctx->inst[i].y1, xctx->inst[i].x2, xctx->inst[i].y2); } if(strcmp(argv[3], "-")) xctx->inst[i].x0 = atof(argv[3]); if(strcmp(argv[4], "-")) xctx->inst[i].y0 = atof(argv[4]); if(strcmp(argv[5], "-")) xctx->inst[i].rot = (unsigned short)atoi(argv[5]); if(strcmp(argv[6], "-")) xctx->inst[i].flip = (unsigned short)atoi(argv[6]); symbol_bbox(i, &xctx->inst[i].x1, &xctx->inst[i].y1, &xctx->inst[i].x2, &xctx->inst[i].y2); xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; if(dr) { if(!floaters) { bbox(ADD, xctx->inst[i].x1, xctx->inst[i].y1, xctx->inst[i].x2, xctx->inst[i].y2); bbox(SET,0.0,0.0,0.0,0.0); } draw(); if(!floaters) { bbox(END,0.0,0.0,0.0,0.0); } } } } /* move_objects [dx dy] [kissing] [stretch] * Start a move operation on selection and let user terminate the operation in the GUI * if kissing is given add nets to pins that touch other instances or nets * if stretch is given stretch connected nets to follow instace pins * if dx and dy are given move by that amount. */ else if(!strcmp(argv[1], "move_objects")) { int nparam = 0; int kissing= 0; int stretch = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { int i; for(i = 2; i < argc; i++) { if(!strcmp(argv[i], "kissing")) {kissing = 1; nparam++;} if(!strcmp(argv[i], "stretch")) {stretch = 1; nparam++;} } } if(stretch) select_attached_nets(); if(kissing) xctx->connect_by_kissing = 2; if(argc > 3 + nparam) { move_objects(START,0,0,0); move_objects( END,0,atof(argv[2]), atof(argv[3])); } else { xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTMOVE; } Tcl_ResetResult(interp); } /* my_strtok_r str delim quote keep_quote * test for my_strtok_r() function */ else if(!strcmp(argv[1], "my_strtok_r")) { if(argc > 5) { char *strcopy = NULL, *strptr = NULL, *saveptr = NULL, *tok; my_strdup(_ALLOC_ID_, &strcopy, argv[2]); strptr = strcopy; while( (tok = my_strtok_r(strptr, argv[3], argv[4], atoi(argv[5]), &saveptr)) ) { strptr = NULL; Tcl_AppendResult(interp, "{", tok, "}\n", NULL); } my_free(_ALLOC_ID_, &strptr); } } else { cmd_found = 0;} break; case 'n': /*----------------------------------------------*/ /* net_label [type] * Place a new net label * 'type': 1: place a 'lab_pin.sym' label * 0: place a 'lab_wire.sym' label * User should complete the placement in the GUI. */ if(!strcmp(argv[1], "net_label")) { if(argc > 2) { unselect_all(1); place_net_label(atoi(argv[2])); } } /* net_pin_mismatch * Highlight nets attached to selected symbols with * a different name than symbol pin */ else if(!strcmp(argv[1], "net_pin_mismatch")) { hilight_net_pin_mismatches(); } /* netlist [-messages] [filename] * do a netlist of current schematic in currently defined netlist format * if 'filename'is given use specified name for the netlist * if 'filename' contains path components place the file in specified path location. * if only a name is given and no path ('/') components are given use the * default netlisting directory. * This means that 'xschem netlist test.spice' and 'xschem netlist ./test.spice' * will create the netlist in different places. * netlisting directory is reset to previous setting after completing this command * If -messages is given return the ERC messages instead of just a fail (1) * or no fail (0) code. */ else if(!strcmp(argv[1], "netlist") ) { char *saveshow = NULL; int err = 0; int messages = 0; char save[PATH_MAX]; const char *fname = NULL; const char *path; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} yyparse_error = 0; my_strdup(_ALLOC_ID_, &saveshow, tclgetvar("show_infowindow_after_netlist")); my_strncpy(save, tclgetvar("netlist_dir"), S(save)); if(argc <= 2 || (argc > 2 && strcmp(argv[2], "-erc"))) { /* xschem netlist NOT invoked from GUI */ tclsetvar("show_infowindow_after_netlist", "never"); } if(argc > 2) { if(!strcmp(argv[2], "-messages")) { messages = 1; if(argc > 3) fname = argv[3]; } else if(strcmp(argv[2], "-erc")) { fname = argv[2]; } if(fname) { my_strncpy(xctx->netlist_name, get_cell_w_ext(fname, 0), S(xctx->netlist_name)); tclvareval("file dirname ", fname, NULL); path = tclresult(); if(strchr(fname, '/')) { set_netlist_dir(1, path); } } } if(set_netlist_dir(0, NULL) ) { if(xctx->netlist_type == CAD_SPICE_NETLIST) err = global_spice_netlist(1); /* 1 means global netlist */ else if(xctx->netlist_type == CAD_VHDL_NETLIST) err = global_vhdl_netlist(1); else if(xctx->netlist_type == CAD_VERILOG_NETLIST) err = global_verilog_netlist(1); else if(xctx->netlist_type == CAD_TEDAX_NETLIST) global_tedax_netlist(1); else if(has_x) tcleval("tk_messageBox -type ok -parent [xschem get topwindow] " "-message {Please Set netlisting mode (Options menu)}"); if(argc > 2) { my_strncpy(xctx->netlist_name, "", S(xctx->netlist_name)); set_netlist_dir(1, save); } if(messages) { Tcl_SetResult(interp, xctx->infowindow_text, TCL_VOLATILE); } else { Tcl_SetResult(interp, my_itoa(err), TCL_VOLATILE); } } tclsetvar("show_infowindow_after_netlist", saveshow); my_free(_ALLOC_ID_, &saveshow); } /* new_process [f] * Start a new xschem process for a schematic. * If 'f' is given load specified schematic. */ else if(!strcmp(argv[1], "new_process")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { char f[PATH_MAX + 100]; my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); new_xschem_process(f, 0); } else new_xschem_process("", 0); Tcl_ResetResult(interp); } /* new_schematic create|destroy|destroy_all|switch winpath file [draw] * Open/destroy a new tab or window * create: create new empty window or with 'file' loaded if 'file' given. * The winpath must be given (even {} is ok). * non empty winpath ({1}) will avoid warnings if opening the * same file multiple times. * destroy: destroy tab/window identified by winpath. Example: * xschem new_schematic destroy .x1.drw * destroy_all: close all tabs/additional windows * if the 'force'argument is given do not issue a warning if modified * tabs are about to be closed. * switch: switch context to specified 'winpath' window or specified schematic name * If 'draw' is given and set to 0 do not redraw after switching tab * (only tab i/f) * Main window/tab has winpath set to .drw, * Additional windows/tabs have winpath set to .x1.drw, .x2.drw and so on... */ else if(!strcmp(argv[1], "new_schematic")) { int r = -1; int dr = 1; char s[20]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { if(argc >= 6 && argv[5][0] == '0') dr = 0; if(argc == 3) r = new_schematic(argv[2], NULL, NULL, 1); else if(argc == 4) r = new_schematic(argv[2], argv[3], NULL, 1); else if(argc >= 5) { char f[PATH_MAX + 100]; my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[4], home_dir); tcleval(f); my_strncpy(f, abs_sym_path(tclresult(), ""), S(f)); r = new_schematic(argv[2], argv[3], f, dr); } my_snprintf(s, S(s), "%d", r); Tcl_SetResult(interp, s, TCL_VOLATILE); } } else { cmd_found = 0;} break; case 'o': /*----------------------------------------------*/ /* only_probes * dim schematic to better show highlights */ if(!strcmp(argv[1], "only_probes")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->only_probes = !xctx->only_probes; tclsetboolvar("only_probes", xctx->only_probes); toggle_only_probes(); Tcl_ResetResult(interp); } /* origin x y [zoom] * Move origin to 'x, y', optionally changing zoom level to 'zoom' * A dash ('-') given for x or y will keep existing value */ else if(!strcmp(argv[1], "origin")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { if(strcmp(argv[2], "-")) xctx->xorigin = atof(argv[2]); if(strcmp(argv[3], "-")) xctx->yorigin = atof(argv[3]); if(argc > 4) { xctx->zoom = atof(argv[4]); xctx->mooz=1/xctx->zoom; } draw(); } Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'p': /*----------------------------------------------*/ /* parse_cmd * debug command to test parse_cmd_string() * splits a command string into argv-like arguments * return # of args in *argc * argv[*argc] is always set to NULL */ if(!strcmp(argv[1], "parse_cmd")) { if(argc > 2) { int c, i; char **av; av = parse_cmd_string(argv[2], &c); for(i = 0; i < c; ++i) { dbg(0, "--> %s\n", av[i]); } } } /* parselabel str * Debug command to test vector net syntax parser */ else if(!strcmp(argv[1], "parselabel")) { if(argc > 2) { parse(argv[2]); } } /* paste [x y] * Paste clipboard. If 'x y' not given user should complete placement in the GUI */ else if(!strcmp(argv[1], "paste")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { merge_file(10, ".sch"); /* set bit 3 to avoid doing move_objects(RUBBER,...) */ xctx->deltax = atof(argv[2]); xctx->deltay = atof(argv[3]); move_objects(END, 0, 0.0, 0.0); } else { merge_file(2, ".sch"); } Tcl_ResetResult(interp); } /* pinlist inst [attr] * List all pins of instance 'inst' * if no 'attr' is given return full attribute string, * else return value for attribute 'attr'. * Example: xschem pinlist x3 name * --> { {0} {PLUS} } { {1} {OUT} } { {2} {MINUS} } * Example: xschem pinlist x3 dir * --> { {0} {in} } { {1} {out} } { {2} {in} } * Example: xschem pinlist x3 * --> { {0} {name=PLUS dir=in } } { {1} {name=OUT dir=out } } * { {2} {name=MINUS dir=in } } */ else if(!strcmp(argv[1], "pinlist")) { int i, p, no_of_pins, first = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { if((i = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem pinlist: instance not found", TCL_STATIC); return TCL_ERROR; } no_of_pins= (xctx->inst[i].ptr+ xctx->sym)->rects[PINLAYER]; for(p=0;p 3 && argv[3][0]) { Tcl_AppendResult(interp, "{ {", my_itoa(p), "} {", get_tok_value((xctx->inst[i].ptr+ xctx->sym)->rect[PINLAYER][p].prop_ptr, argv[3], 0), "} }", NULL); } else { Tcl_AppendResult(interp, "{ {", my_itoa(p), "} {", (xctx->inst[i].ptr+ xctx->sym)->rect[PINLAYER][p].prop_ptr, "} }", NULL); } first = 0; } } } /* place_symbol [sym_name] [prop] * Start a GUI placement operation of specified 'sym_name' symbol. * If 'sym_name' not given prompt user * 'prop' is the attribute string of the symbol. * If not given take from symbol template attribute. */ else if(!strcmp(argv[1], "place_symbol")) { int ret; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->semaphore++; rebuild_selected_array(); if(xctx->lastsel && xctx->sel_array[0].type==ELEMENT) { tclvareval("set INITIALINSTDIR [file dirname {", abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""), "}]", NULL); } unselect_all(1); xctx->mx_double_save = xctx->mousex_snap; xctx->my_double_save = xctx->mousey_snap; if(argc > 3) { /* pos name x y rot flip prop draw first to_push_undo */ ret = place_symbol(-1,argv[2],xctx->mousex_snap, xctx->mousey_snap, 0, 0, argv[3], 4, 1, 1); } else if(argc > 2) { ret = place_symbol(-1,argv[2],xctx->mousex_snap, xctx->mousey_snap, 0, 0, NULL, 4, 1, 1); } else { xctx->last_command = 0; ret = place_symbol(-1,NULL, xctx->mousex_snap, xctx->mousey_snap, 0, 0, NULL, 4, 1, 1); } if(ret) { xctx->mousey_snap = xctx->my_double_save; xctx->mousex_snap = xctx->mx_double_save; move_objects(START,0,0,0); xctx->ui_state |= PLACE_SYMBOL; } xctx->semaphore--; Tcl_ResetResult(interp); } /* place_text * Start a GUI placement of a text object */ else if(!strcmp(argv[1], "place_text")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->semaphore++; xctx->last_command = 0; unselect_all(1); xctx->mx_double_save = xctx->mousex_snap; xctx->my_double_save = xctx->mousey_snap; if(place_text(0, xctx->mousex_snap, xctx->mousey_snap)) { /* 1 = draw text 24122002 */ xctx->mousey_snap = xctx->my_double_save; xctx->mousex_snap = xctx->mx_double_save; move_objects(START,0,0,0); xctx->ui_state |= PLACE_TEXT; } xctx->semaphore--; Tcl_ResetResult(interp); } /* polygon * Start a GUI placement of a polygon */ else if(!strcmp(argv[1], "polygon")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTPOLYGON; } /* preview_window create|draw|destroy [winpath] [file] * Used in fileselector to show a schematic preview. */ else if(!strcmp(argv[1], "preview_window")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc == 3) preview_window(argv[2], "{}", "{}"); else if(argc == 4) preview_window(argv[2], argv[3], "{}"); else if(argc == 5) { char f[PATH_MAX + 100]; my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[4], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); preview_window(argv[2], argv[3], f); } Tcl_ResetResult(interp); } /* print png|svg|ps|pdf|ps_full|pdf_full img_file [img_x img_y] [x1 y1 x2 y2] * Export current schematic to image. * img x y size xschem area to export * 0 1 2 3 4 5 6 7 8 9 * xschem print png file.png [400 300] [ -300 -200 300 200 ] * xschem print svg file.svg [400 300] [ -300 -200 300 200 ] * xschem print ps file.ps * xschem print pdf file.pdf * xschem print ps_full file.ps * xschem print pdf_full file.pdf */ else if(!strcmp(argv[1], "print") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 3) { Tcl_SetResult(interp, "xschem print needs at least 1 more arguments: plot_type", TCL_STATIC); return TCL_ERROR; } if(argc > 3) { tclvareval("file normalize {", argv[3], "}", NULL); my_strncpy(xctx->plotfile, Tcl_GetStringResult(interp), S(xctx->plotfile)); } if(!strcmp(argv[2], "pdf") || !strcmp(argv[2],"ps")) { int fullzoom = 0; ps_draw(7, fullzoom); } else if(!strcmp(argv[2], "pdf_full") || !strcmp(argv[2],"ps_full")) { int fullzoom = 1; ps_draw(7, fullzoom); } else if(!strcmp(argv[2], "png")) { int w = 0, h = 0; double x1, y1, x2, y2; if(argc == 6) { w = atoi(argv[4]); h = atoi(argv[5]); if(w == 0) w = xctx->xrect[0].width; if(h == 0) h = xctx->xrect[0].height; save_restore_zoom(1); set_viewport_size(w, h, 1.0); zoom_full(0, 0, 2 * tclgetboolvar("zoom_full_center"), 0.97); resetwin(1, 1, 1, w, h); print_image(); save_restore_zoom(0); resetwin(1, 1, 1, 0, 0); change_linewidth(-1.); } else if(argc == 10) { w = atoi(argv[4]); h = atoi(argv[5]); x1 = atof(argv[6]); y1 = atof(argv[7]); x2 = atof(argv[8]); y2 = atof(argv[9]); if(w == 0) w = (int) fabs(x2 - x1); if(h == 0) h = (int) fabs(y2 - y1); save_restore_zoom(1); set_viewport_size(w, h, 1.0); zoom_box(x1, y1, x2, y2, 1.0); resetwin(1, 1, 1, w, h); print_image(); save_restore_zoom(0); resetwin(1, 1, 1, 0, 0); change_linewidth(-1.); } else { print_image(); } } else if(!strcmp(argv[2], "svg")) { int w = 0, h = 0; double x1, y1, x2, y2; if(argc == 6) { w = atoi(argv[4]); h = atoi(argv[5]); save_restore_zoom(1); set_viewport_size(w, h, xctx->lw); zoom_full(0, 0, 2 * tclgetboolvar("zoom_full_center"), 0.97); svg_draw(); save_restore_zoom(0); } else if(argc == 10) { w = atoi(argv[4]); h = atoi(argv[5]); x1 = atof(argv[6]); y1 = atof(argv[7]); x2 = atof(argv[8]); y2 = atof(argv[9]); save_restore_zoom(1); set_viewport_size(w, h, xctx->lw); zoom_box(x1, y1, x2, y2, 1.0); svg_draw(); save_restore_zoom(0); } else { svg_draw(); } } draw(); Tcl_ResetResult(interp); } /* print_hilight_net show * from highlighted nets/pins/labels: * show == 0 ==> create pins from highlight nets * show == 1 ==> show list of highlight net in a dialog box * show == 2 ==> create labels with i prefix from hilight nets * show == 3 ==> show list of highlight net with path and label * expansion in a dialog box * show == 4 ==> create labels without i prefix from hilight nets * for show = 0, 2, 4 user should complete GUI placement * of created objects */ else if(!strcmp(argv[1], "print_hilight_net")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { print_hilight_net(atoi(argv[2])); } } /* print_spice_element inst * Print spice raw netlist line for instance (number or name) 'inst' */ else if(!strcmp(argv[1], "print_spice_element") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { int inst; if((inst = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem replace_symbol: instance not found", TCL_STATIC); return TCL_ERROR; } print_spice_element(stderr, inst); } } /* propagate_hilights [set clear] * Debug: wrapper to propagate_hilights() function */ else if(!strcmp(argv[1], "propagate_hilights")) { int set = 1, clear = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { set = atoi(argv[2]); clear = atoi(argv[3]); } propagate_hilights(set, clear, XINSERT_NOREPLACE); } /* push_undo * Push current state on undo stack */ else if(!strcmp(argv[1], "push_undo")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->push_undo(); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'r': /*----------------------------------------------*/ /* raw what [rawfile type] * what = read | clear | info | switch | switch_back * Load /clear / switch additional raw files */ if(!strcmp(argv[1], "raw")) { int err = 0; int ret = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3 && !strcmp(argv[2], "read")) { if(argc > 4) ret = extra_rawfile(1, argv[3], argv[4]); else ret = extra_rawfile(1, argv[3], NULL); Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE); } else if(argc > 2 && !strcmp(argv[2], "switch")) { if(argc > 4) { ret = extra_rawfile(2, argv[3], argv[4]); } else { ret = extra_rawfile(2, NULL, NULL); } update_op(); Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE); } else if(argc > 2 && !strcmp(argv[2], "info")) { ret = extra_rawfile(4, NULL, NULL); } else if(argc > 2 && !strcmp(argv[2], "switch_back")) { ret = extra_rawfile(5, NULL, NULL); update_op(); Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE); } else if(argc > 2 && !strcmp(argv[2], "clear")) { if(argc > 3) { ret = extra_rawfile(3, argv[3], NULL); } else { ret = extra_rawfile(3, NULL, NULL); } Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE); } else { err = 1; } if(err) {Tcl_SetResult(interp, "Wrong command", TCL_STATIC); return TCL_ERROR;} } /* raw_clear * Delete loaded simulation raw file */ else if(!strcmp(argv[1], "raw_clear")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} tclsetvar("rawfile_loaded", "0"); extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 1); Tcl_ResetResult(interp); } /* raw_query loaded|value|index|values|datasets|vars|list * xschem raw_query list: get list of saved simulation variables * xschem raw_query vars: get number of simulation variables * xschem raw_query datasets: get number of datasets (simulation runs) * xschem raw_query value node n: return n-th value of 'node' in raw file * xschem raw_query loaded: return hierarchy level * xschem raw_query rawfile: return raw filename * where raw file was loaded or -1 if no raw loaded * xschem raw_query index node: get index of simulation variable 'node'. * Example: raw_query index v(led) --> 46 * xschem raw_query values node [dset] : print all simulation * values of 'node' for dataset 'dset' (default dset=0) * xschem raw_query points [dset] : print simulation points for * dataset 'dset' (default: all dataset points combined) */ else if(!strcmp(argv[1], "raw_query")) { int i; Raw *raw = xctx->raw; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_ResetResult(interp); if(argc > 2 && !strcmp(argv[2], "loaded")) { Tcl_SetResult(interp, my_itoa(sch_waves_loaded()), TCL_VOLATILE); } else if(raw && raw->values) { /* xschem rawfile_query value v(ldcp) 123 */ if(argc > 4 && !strcmp(argv[2], "value")) { int dataset = -1; int point = atoi(argv[4]); const char *node = argv[3]; int idx = -1; if(argc > 5) dataset = atoi(argv[5]); if((dataset >= 0 && point >= 0 && point < raw->npoints[dataset]) || (point >= 0 && point < raw->allpoints)) { if(isonlydigit(node)) { int i = atoi(node); if(i >= 0 && i < raw->nvars) { idx = i; } } else { idx = get_raw_index(node); } if(idx >= 0) { double val = get_raw_value(dataset, idx, point); Tcl_SetResult(interp, dtoa(val), TCL_VOLATILE); } } } else if(argc > 3 && !strcmp(argv[2], "index")) { /* xschem rawfile_query index v(ldcp) */ int idx; idx = get_raw_index(argv[3]); Tcl_SetResult(interp, my_itoa(idx), TCL_VOLATILE); } else if(argc > 3 && !strcmp(argv[2], "values")) { /* xschem raw_query values ldcp [dataset] */ int idx; char n[70]; int p, dataset = 0; idx = get_raw_index(argv[3]); if(argc > 4) dataset = atoi(argv[4]); if(idx >= 0) { int np; if(dataset < 0 ) np = raw->allpoints; else np = raw->npoints[dataset]; Tcl_ResetResult(interp); for(p = 0; p < np; p++) { sprintf(n, "%.10e", get_raw_value(dataset, idx, p)); Tcl_AppendResult(interp, n, " ", NULL); } } } else if(argc > 2 && !strcmp(argv[2], "datasets")) { Tcl_SetResult(interp, my_itoa(raw->datasets), TCL_VOLATILE); } else if(argc > 2 && !strcmp(argv[2], "points")) { int dset = -1; if(argc > 3) dset = atoi(argv[3]); if(dset == -1) Tcl_SetResult(interp, my_itoa(raw->allpoints), TCL_VOLATILE); else { if(dset >= 0 && dset < raw->datasets) Tcl_SetResult(interp, my_itoa(raw->npoints[dset]), TCL_VOLATILE); } } else if(argc > 2 && !strcmp(argv[2], "rawfile")) { Tcl_SetResult(interp, raw->rawfile, TCL_VOLATILE); } else if(argc > 2 && !strcmp(argv[2], "vars")) { Tcl_SetResult(interp, my_itoa(raw->nvars), TCL_VOLATILE); } else if(argc > 2 && !strcmp(argv[2], "list")) { for(i = 0 ; i < raw->nvars; ++i) { if(i > 0) Tcl_AppendResult(interp, "\n", NULL); Tcl_AppendResult(interp, raw->names[i], NULL); } } } } /* raw_read [file] [sim] * If a raw file is already loaded delete from memory * else load specified file and analysis 'sim' (dc, ac, tran, op, ...) * If 'sim' not specified load first section found in raw file. */ else if(!strcmp(argv[1], "raw_read")) { char f[PATH_MAX + 100]; int res = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(sch_waves_loaded() >= 0) { tcleval("array unset ngspice::ngspice_data"); extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 1); tclsetvar("rawfile_loaded", "0"); } else if(argc > 2) { extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 0); my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); if(argc > 3) res = raw_read(f, &xctx->raw, argv[3]); else res = raw_read(f, &xctx->raw, NULL); if(sch_waves_loaded() >= 0) { tclsetvar("rawfile_loaded", "1"); draw(); } else tclsetvar("rawfile_loaded", "0"); } Tcl_SetResult(interp, my_itoa(res), TCL_VOLATILE); } /* raw_read_from_attr [sim] * If a simulation raw file is already loaded delete from memory * else read section 'sim' (tran, dc, ac, op, ...) * of base64 encoded data from a 'spice_data' * attribute of selected instance * If sim not given read first section found */ else if(!strcmp(argv[1], "raw_read_from_attr")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(sch_waves_loaded() >= 0) { extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 1); } else { extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 0); if(argc > 2) raw_read_from_attr(&xctx->raw, argv[2]); else raw_read_from_attr(&xctx->raw, NULL); if(sch_waves_loaded() >= 0) { tclsetvar("rawfile_loaded", "1"); draw(); } else tclsetvar("rawfile_loaded", "0"); } Tcl_ResetResult(interp); } /* rebuild_connectivity Rebuild logical connectivity abstraction of schematic */ else if(!strcmp(argv[1], "rebuild_connectivity")) { int err = 0; int n = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) n = atoi(argv[2]); xctx->prep_hash_inst=0; xctx->prep_hash_wires=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; err |= prepare_netlist_structs(n); Tcl_SetResult(interp, my_itoa(err), TCL_VOLATILE); } /* rebuild_selection Rebuild selection list*/ else if(!strcmp(argv[1], "rebuild_selection")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} rebuild_selected_array(); } /* rect [x1 y1 x2 y2] [pos] [propstring] [draw] * if 'x1 y1 x2 y2'is given place recangle on current * layer (rectcolor) at indicated coordinates. * if 'pos' is given insert at given position in rectangle array. * if 'pos' set to -1 append rectangle to last element in rectangle array. * 'propstring' is the attribute string. Set to empty if not given. * if 'draw' is set to 1 (default) draw the new object, else don't * If no coordinates are given start a GUI operation of rectangle placement */ else if(!strcmp(argv[1], "rect")) { double x1,y1,x2,y2; int pos, save; int draw = 1; const char *prop_str = NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 5) { x1=atof(argv[2]); y1=atof(argv[3]); x2=atof(argv[4]); y2=atof(argv[5]); RECTORDER(x1,y1,x2,y2); pos=-1; if(argc > 6) pos=atoi(argv[6]); if(argc > 7) prop_str = argv[7]; if(argc > 8) draw = atoi(argv[8]); storeobject(pos, x1,y1,x2,y2,xRECT,xctx->rectcolor,0,prop_str); if(draw) { save = xctx->draw_window; xctx->draw_window = 1; drawrect(xctx->rectcolor,NOW, x1,y1,x2,y2, 0); xctx->draw_window = save; } set_modify(1); } else { xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTRECT; } } /* redo * Redo last undone action */ else if(!strcmp(argv[1], "redo")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->pop_undo(1, 1); /* 2nd param: set_modify_status */ Tcl_ResetResult(interp); } /* redraw * redraw window */ else if(!strcmp(argv[1], "redraw")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} draw(); Tcl_ResetResult(interp); } /* reload * Forced (be careful!) Reload current schematic from disk */ else if(!strcmp(argv[1], "reload")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} unselect_all(1); remove_symbols(); load_schematic(1, xctx->sch[xctx->currsch], 1, 1); if(argc > 2 && !strcmp(argv[2], "zoom_full") ) { zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); } else { draw(); } Tcl_ResetResult(interp); } /* reload_symbols * Reload all used symbols from disk */ else if(!strcmp(argv[1], "reload_symbols")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} remove_symbols(); link_symbols_to_instances(-1); xctx->prep_hi_structs=0; xctx->prep_net_structs=0; Tcl_ResetResult(interp); } /* remove_symbols * Internal command: remove all symbol definitions */ else if(!strcmp(argv[1], "remove_symbols")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} remove_symbols(); Tcl_ResetResult(interp); } /* replace_symbol inst new_symbol [fast] * Replace 'inst' symbol with 'new_symbol' * If doing multiple substitutions set 'fast' to {} * on first call and 'fast' on next calls * for faster operation. * do a 'xschem redraw' at end to update screen * Example: xschem replace_symbol R3 capa.sym */ else if(!strcmp(argv[1], "replace_symbol")) { int inst, fast = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 4) { argc = 4; if(!strcmp(argv[4], "fast")) { fast = 1; } } if(argc!=4) { Tcl_SetResult(interp, "xschem replace_symbol needs 2 additional arguments", TCL_STATIC); return TCL_ERROR; } if((inst = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem replace_symbol: instance not found", TCL_STATIC); return TCL_ERROR; } else { char symbol[PATH_MAX]; int sym_number, prefix, cond; char *type; char *name=NULL; char *ptr=NULL; char *sym = NULL; my_strncpy(symbol, argv[3], S(symbol)); if(!fast) { xctx->push_undo(); xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; } my_strdup(_ALLOC_ID_, &sym, tcl_hook2(symbol)); sym_number=match_symbol(sym); my_free(_ALLOC_ID_, &sym); if(sym_number>=0) { prefix=(get_tok_value( (xctx->sym+sym_number)->templ , "name",0))[0]; /* get new symbol prefix */ } else prefix = 'x'; delete_inst_node(inst); /* 20180208 fix crashing bug: delete node info if changing symbol */ /* if number of pins is different we must delete these data *before* */ /* changing ysmbol, otherwise i might end up deleting non allocated data. */ my_strdup2(_ALLOC_ID_, &xctx->inst[inst].name, rel_sym_path(symbol)); xctx->inst[inst].ptr=sym_number; my_strdup(_ALLOC_ID_, &name, xctx->inst[inst].instname); if(name && name[0] ) { /* 20110325 only modify prefix if prefix not NUL */ if(prefix) name[0]=(char)prefix; /* change prefix if changing symbol type; */ my_strdup(_ALLOC_ID_, &ptr,subst_token(xctx->inst[inst].prop_ptr, "name", name) ); if(!fast) hash_names(-1, XINSERT); hash_names(inst, XDELETE); new_prop_string(inst, ptr, fast, /* sets also inst[].instname */ tclgetboolvar("disable_unique_names")); /* set new prop_ptr */ hash_names(inst, XINSERT); type=xctx->sym[xctx->inst[inst].ptr].type; cond= type && IS_LABEL_SH_OR_PIN(type); if(cond) { xctx->inst[inst].flags |= PIN_OR_LABEL; my_strdup2(_ALLOC_ID_, &xctx->inst[inst].lab, get_tok_value(xctx->inst[inst].prop_ptr, "lab", 0)); } else xctx->inst[inst].flags &= ~PIN_OR_LABEL; xctx->inst[inst].embed = !strboolcmp(get_tok_value(xctx->inst[inst].prop_ptr, "embed", 2), "true"); my_free(_ALLOC_ID_, &ptr); } my_free(_ALLOC_ID_, &name); set_modify(1); /* draw(); */ Tcl_SetResult(interp, xctx->inst[inst].instname , TCL_VOLATILE); } } /* reset_symbol inst symref * This is a low level command, it merely changes the xctx->inst[...].name field. * It is caller responsibility to delete all symbols before and do a reload_symbols * afterward */ else if(!strcmp(argv[1], "reset_symbol")) { int inst; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc!=4) { Tcl_SetResult(interp, "xschem reset_symbol needs 2 additional arguments", TCL_STATIC); return TCL_ERROR; } if((inst = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem reset_symbol: instance not found", TCL_STATIC); return TCL_ERROR; } else { my_strdup(_ALLOC_ID_, &xctx->inst[inst].name, argv[3]); } Tcl_ResetResult(interp); } /* reset_flags * Reset cached instance and symbol cached flags (inst->flags, sym->flags) */ else if(!strcmp(argv[1], "reset_flags")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} reset_flags(); Tcl_ResetResult(interp); } /* resetwin create_pixmap clear_pixmap force w h * internal command: calls resetwin() */ else if(!strcmp(argv[1], "resetwin")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 6) { resetwin(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]), atoi(argv[6])); } Tcl_ResetResult(interp); } /* resolved_net [net] * if 'net' is given return its topmost full hierarchy name * else returns the topmost full hierarchy name of selected net/pin/label. * Nets connected to I/O ports are mapped to upper level recursively */ else if(!strcmp(argv[1], "resolved_net")) { const char *net = NULL; char *rn = NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_ResetResult(interp); prepare_netlist_structs(0); if(argc > 2) { net = argv[2]; } else if(xctx->lastsel == 1) { if(xctx->sel_array[0].type == ELEMENT) { int n=xctx->sel_array[0].n; if(xctx->inst[n].ptr >= 0) { const char *type = xctx->sym[xctx->inst[n].ptr].type; if(IS_LABEL_SH_OR_PIN(type) && xctx->inst[n].node && xctx->inst[n].node[0]) { net = xctx->inst[n].node[0]; } } } else if(xctx->sel_array[0].type == WIRE) { int n=xctx->sel_array[0].n; if(xctx->wire[n].node) { net = xctx->wire[n].node; } } } rn = resolved_net(net); Tcl_AppendResult(interp, rn, NULL); my_free(_ALLOC_ID_, &rn); } /* rotate [x0 y0] * Rotate selection around point x0 y0. * if x0, y0 not given use mouse coordinates */ else if(!strcmp(argv[1], "rotate")) { double x0 = xctx->mousex_snap; double y0 = xctx->mousey_snap; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 3) { x0 = atof(argv[2]); y0 = atof(argv[3]); } if(xctx->ui_state & STARTMOVE) move_objects(ROTATE,0,0,0); else if(xctx->ui_state & STARTCOPY) copy_objects(ROTATE); else { rebuild_selected_array(); xctx->mx_double_save = xctx->mousex_snap = x0; xctx->my_double_save = xctx->mousey_snap = y0; move_objects(START,0,0,0); move_objects(ROTATE,0,0,0); move_objects(END,0,0,0); } Tcl_ResetResult(interp); } /* rotate_in_place * Rotate selected objects around their 0,0 coordinate point */ else if(!strcmp(argv[1], "rotate_in_place")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx->ui_state & STARTMOVE) move_objects(FLIP|ROTATELOCAL,0,0,0); else if(xctx->ui_state & STARTCOPY) copy_objects(FLIP|ROTATELOCAL); else { rebuild_selected_array(); move_objects(START,0,0,0); move_objects(ROTATE|ROTATELOCAL,0,0,0); move_objects(END,0,0,0); } Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 's': /*----------------------------------------------*/ /* save * Save schematic if modified. Does not ask confirmation! */ if(!strcmp(argv[1], "save")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} dbg(1, "scheduler(): saving: current schematic\n"); if(!strcmp(xctx->sch[xctx->currsch], "")) { /* check if unnamed schematic, use saveas in this case... */ saveas(NULL, SCHEMATIC); } else { save(0); } } /* saveas [file] [type] * save current schematic as 'file' * if file is empty ({}) use current schematic name * as defalt and prompt user with file selector * 'type' is used used to set/change file extension: * schematic: save as schematic (*.sch) * symbol: save as symbol (*.sym) * If not specified default to schematic (*.sch) * Does not ask confirmation if file name given */ else if(!strcmp(argv[1], "saveas")) { const char *fptr; char f[PATH_MAX + 100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); } if(argc > 3) { fptr = !strcmp(f, "") ? NULL : f; if(!strcmp(argv[3], "schematic")) saveas(fptr, SCHEMATIC); else if(!strcmp(argv[3], "symbol")) saveas(fptr, SYMBOL); else saveas(fptr, SCHEMATIC); } else if(argc > 2) { fptr = !strcmp(f, "") ? NULL : f; saveas(fptr, SCHEMATIC); } else saveas(NULL, SCHEMATIC); } /* sch_pinlist * List a 2-item list of all pins and directions of current schematic * Example: xschem sch_pinlist * --> {PLUS} {in} {OUT} {out} {MINUS} {in} {VCC} {inout} {VSS} {inout} */ else if(!strcmp(argv[1], "sch_pinlist")) { int i, first = 1; char *dir = NULL; const char *lab; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 0; i < xctx->instances; ++i) { if( !strcmp((xctx->inst[i].ptr + xctx->sym)->type, "ipin") ) dir="in"; else if( !strcmp((xctx->inst[i].ptr + xctx->sym)->type, "opin") ) dir="out"; else if( !strcmp((xctx->inst[i].ptr + xctx->sym)->type, "iopin") ) dir="inout"; else dir = NULL; if(dir) { lab = xctx->inst[i].lab; if(first == 0) Tcl_AppendResult(interp, " ", NULL); Tcl_AppendResult(interp, "{", lab, "} {", dir, "}", NULL); first = 0; } } } /* schematic_in_new_window [new_process] * When a symbol is selected edit corresponding schematic * in a new tab/window if not already open. * If nothing selected open another window of the second * schematic (issues a warning). * if 'new_process' is given start a new xschem process */ else if(!strcmp(argv[1], "schematic_in_new_window")) { int new_process = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 && !strcmp(argv[2], "new_process")) new_process = 1; schematic_in_new_window(new_process); Tcl_ResetResult(interp); } /* search regex|exact select tok val [match_case] * Search instances with attribute string containing 'tok' * attribute and value 'val' * search can be exact ('exact') or as a regular expression ('regex') * select: * 0 : highlight matching instances * 1 : select matching instances * -1 : unselect matching instances * 'tok' set as: * propstring : will search for 'val' in the entire * *instance* attribute string. * cell::propstring : will search for 'val' in the entire * *symbol* attribute string. * cell::name : will search for 'val' in the symbol name * cell:: will search for 'val' in symbol attribute 'attr' * example: xschem search regex 0 cell::template GAIN=100 * match_case: * 1 : Match case * 0 : Do not match case * If not given assume 1 (Match case) */ else if(!strcmp(argv[1], "search") || !strcmp(argv[1], "searchmenu")) { /* 0 1 2 3 4 5 6 */ /* select */ /* xschem search regex|exact 0|1|-1 tok val [match_case] */ int select, r; int match_case = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 6 && argv[6][0] == '0') match_case = 0; if(argc < 6) { Tcl_SetResult(interp, "xschem search requires 4 or 5 additional fields.", TCL_STATIC); return TCL_ERROR; } if(argc > 5) { select = atoi(argv[3]); if(!strcmp(argv[2], "regex") ) r = search(argv[4],argv[5],0,select, match_case); else r = search(argv[4],argv[5],1,select, match_case); if(r == 0) { if(has_x && !strcmp(argv[1], "searchmenu")) tcleval("tk_messageBox -type ok -parent [xschem get topwindow] -message {Not found.}"); Tcl_SetResult(interp, "0", TCL_STATIC); } else { Tcl_SetResult(interp, "1", TCL_STATIC); } return TCL_OK; } } /* select instance|wire|text id [clear] [fast] * select rect|line|poly|arc layer id [clear] [fast] * Select indicated instance or wire or text, or * Select indicated (layer, number) rectangle, line, polygon, arc. * For 'instance' 'id' can be the instance name or number * for all other objects 'id' is the position in the respective arrays * if 'clear' is specified does an unselect operation * if 'fast' is specified avoid sending information to infowindow and status bar * returns 1 if something selected, 0 otherwise */ else if(!strcmp(argv[1], "select")) { short unsigned int sel = SELECTED; int fast = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 3) { Tcl_SetResult(interp, "xschem select: missing arguments.", TCL_STATIC); return TCL_ERROR; } else if(argc < 5 && (!strcmp(argv[2], "rect") || !strcmp(argv[2], "line") || !strcmp(argv[2], "poly") || !strcmp(argv[2], "arc"))) { Tcl_SetResult(interp, "xschem select: missing arguments.", TCL_STATIC); return TCL_ERROR; } else if(argc < 4) { Tcl_SetResult(interp, "xschem select: missing arguments.", TCL_STATIC); return TCL_ERROR; } if(argc > 4) { int i; for(i = 4; i < argc; i++) { if(!strcmp(argv[i], "clear")) sel = 0; if(!strcmp(argv[i], "fast")) fast = 1; } } if(!strcmp(argv[2], "instance") && argc > 3) { int n; /* find by instance name or number*/ n = get_instance(argv[3]); if(n >= 0) { select_element(n, sel, fast, 1); xctx->ui_state |= SELECTION; } Tcl_SetResult(interp, (n >= 0) ? "1" : "0" , TCL_STATIC); } else if(!strcmp(argv[2], "wire") && argc > 3) { int n=atoi(argv[3]); int valid = n < xctx->wires && n >= 0; if(valid) { select_wire(n, sel, fast); xctx->ui_state |= SELECTION; } Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC); } else if(!strcmp(argv[2], "line") && argc > 4) { int c=atoi(argv[3]); int n=atoi(argv[4]); int valid = n < xctx->lines[c] && n >= 0 && c < cadlayers && c >= 0; if(valid) { select_line(c, n, sel, fast); xctx->ui_state |= SELECTION; } Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC); } else if(!strcmp(argv[2], "rect") && argc > 4) { int c=atoi(argv[3]); int n=atoi(argv[4]); int valid = n < xctx->rects[c] && n >= 0 && c < cadlayers && c >= 0; if(valid) { select_box(c, n, sel, fast, 0); xctx->ui_state |= SELECTION; } Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC); } else if(!strcmp(argv[2], "arc") && argc > 4) { int c=atoi(argv[3]); int n=atoi(argv[4]); int valid = n < xctx->arcs[c] && n >= 0 && c < cadlayers && c >= 0; if(valid) { select_arc(c, n, sel, fast); xctx->ui_state |= SELECTION; } Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC); } else if(!strcmp(argv[2], "poly") && argc > 4) { int c=atoi(argv[3]); int n=atoi(argv[4]); int valid = n < xctx->polygons[c] && n >= 0 && c < cadlayers && c >= 0; if(valid) { select_polygon(c, n, sel, fast); xctx->ui_state |= SELECTION; } Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC); } else if(!strcmp(argv[2], "text") && argc > 3) { int n=atoi(argv[3]); int valid = n < xctx->texts && n >= 0; if(valid) { select_text(n, sel, fast); xctx->ui_state |= SELECTION; } Tcl_SetResult(interp, valid ? "1" : "0" , TCL_STATIC); } drawtemparc(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0, 0.0); drawtemprect(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0); drawtempline(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0); } /* select_all * Selects all objects in schematic */ else if(!strcmp(argv[1], "select_all")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} select_all(); Tcl_ResetResult(interp); } /* select_dangling_nets * Select all nets/labels that are dangling, ie not attached to any non pin/port/probe components * Returns number of selected items (wires,labels) if danglings found, 0 otherwise */ else if(!strcmp(argv[1], "select_dangling_nets")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} select_dangling_nets(); Tcl_SetResult(interp, my_itoa(xctx->lastsel), TCL_VOLATILE); } /* select_hilight_net * Select all highlight objects (wires, labels, pins, instances) */ else if(!strcmp(argv[1], "select_hilight_net")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} select_hilight_net(); Tcl_ResetResult(interp); } /* select_inside x1 y1 x2 y2 [sel] * Select all objects inside the indicated area if [sel] is set to '0' do an unselect operation */ else if(!strcmp(argv[1], "select_inside")) { int sel = SELECTED; double x1, y1, x2, y2; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 6 && argv[6][0] == '0') sel = 0; x1 = atof(argv[2]); y1 = atof(argv[3]); x2 = atof(argv[4]); y2 = atof(argv[5]); select_inside(x1, y1, x2, y2, sel); Tcl_ResetResult(interp); } /* selected_set [what] * Return a list of selected instance names * If what is not given or set to 'inst' return list of selected instance names * If what set to 'rect' return list of selected rectangles with their coordinates */ else if(!strcmp(argv[1], "selected_set")) { int n, i, first = 1; int what = ELEMENT; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { if(!strcmp(argv[2], "rect")) what = xRECT; } rebuild_selected_array(); for(n=0; n < xctx->lastsel; ++n) { if(what == xRECT && xctx->sel_array[n].type == xRECT) { char col[30], num[30], coord[200]; int c = xctx->sel_array[n].col; i = xctx->sel_array[n].n; my_strncpy(col, my_itoa(c), S(col)); my_strncpy(num, my_itoa(i), S(num)); my_snprintf(coord, S(coord), "%g %g %g %g", xctx->rect[c][i].x1, xctx->rect[c][i].y1, xctx->rect[c][i].x2, xctx->rect[c][i].y2); if(first == 0) Tcl_AppendResult(interp, "\n", NULL); first = 0; Tcl_AppendResult(interp,col, " ", num, " ", coord , NULL); } else if(what == ELEMENT && xctx->sel_array[n].type == ELEMENT) { i = xctx->sel_array[n].n; if(first == 0) Tcl_AppendResult(interp, " ", NULL); Tcl_AppendResult(interp, "{", xctx->inst[i].instname, "}", NULL); first = 0; } } } /* selected_wire * Return list of selected nets */ else if(!strcmp(argv[1], "selected_wire")) { int n, i, first = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} rebuild_selected_array(); for(n=0; n < xctx->lastsel; ++n) { if(xctx->sel_array[n].type == WIRE) { i = xctx->sel_array[n].n; if(first == 0) Tcl_AppendResult(interp, " ", NULL); Tcl_AppendResult(interp, "{", get_tok_value(xctx->wire[i].prop_ptr, "lab",0), "}", NULL); first = 0; } } } /* send_to_viewer * Send selected wires/net labels/pins/voltage source or ammeter currents to current * open viewer (gaw or bespice) */ else if(!strcmp(argv[1], "send_to_viewer")) { int viewer = 0; int exists = 0; char *viewer_name = NULL; char tcl_str[200]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} tcleval("info exists sim"); if(tclresult()[0] == '1') exists = 1; xctx->enable_drill = 0; if(exists) { viewer = atoi(tclgetvar("sim(spicewave,default)")); my_snprintf(tcl_str, S(tcl_str), "sim(spicewave,%d,name)", viewer); my_strdup(_ALLOC_ID_, &viewer_name, tclgetvar(tcl_str)); dbg(1, "send_to_viewer: viewer_name=%s\n", viewer_name); if(strstr(viewer_name, "Gaw")) viewer=GAW; else if(strstr(viewer_name, "Bespice")) viewer=BESPICE; if(viewer) { hilight_net(viewer); redraw_hilights(0); } my_free(_ALLOC_ID_, &viewer_name); } Tcl_ResetResult(interp); } /* set var value * Set C variable 'var' to 'value' */ else if(!strcmp(argv[1], "set")) { if(argc > 3) { if(argv[2][0] < 'n') { if(!strcmp(argv[2], "cadgrid")) { /* set cad grid (default: 20) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} set_grid( atof(argv[3]) ); } else if(!strcmp(argv[2], "cadsnap")) { /* set mouse snap (default: 10) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} set_snap( atof(argv[3]) ); } else if(!strcmp(argv[2], "color_ps")) { /* set color psoscript (1 or 0) */ color_ps=atoi(argv[3]); } else if(!strcmp(argv[2], "constrained_move")) { /* set constrained move (1=horiz, 2=vert, 0=none) */ constrained_move = atoi(argv[3]); } else if(!strcmp(argv[2], "draw_window")) { /* set drawing to window (1 or 0) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->draw_window=atoi(argv[3]); } else if(!strcmp(argv[2], "fix_broken_tiled_fill")) { /* alternate drawing method for broken GPUs */ fix_broken_tiled_fill = atoi(argv[3]); } else if(!strcmp(argv[2], "fix_mouse_coord")) { /* fix for wrong mouse coords in RDP software */ fix_mouse_coord = atoi(argv[3]); } else if(!strcmp(argv[2], "format")) { /* set name of custom format attribute used for netlisting */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->format, argv[3]); } else if(!strcmp(argv[2], "header_text")) { /* set header metadata (used for license info) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(!xctx->header_text || strcmp(xctx->header_text, argv[3])) { set_modify(1); xctx->push_undo(); my_strdup2(_ALLOC_ID_, &xctx->header_text, argv[3]); } } else if(!strcmp(argv[2], "hide_symbols")) { /* set to 0,1,2 for various hiding level of symbols */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->hide_symbols=atoi(argv[3]); } else if(!strcmp(argv[2], "infowindow_text")) { /* ERC messages */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->infowindow_text, argv[3]); } } else { /* argv[2][0] >= 'n' */ if(!strcmp(argv[2], "netlist_name")) { /* set custom netlist name */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strncpy(xctx->netlist_name, argv[3], S(xctx->netlist_name)); } else if(!strcmp(argv[2], "netlist_type")) /* set netlisting mode (spice, verilog, vhdl, tedax, symbol) */ { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(!strcmp(argv[3], "spice")){ xctx->netlist_type=CAD_SPICE_NETLIST; } else if(!strcmp(argv[3], "vhdl")) { xctx->netlist_type=CAD_VHDL_NETLIST; } else if(!strcmp(argv[3], "verilog")) { xctx->netlist_type=CAD_VERILOG_NETLIST; } else if(!strcmp(argv[3], "tedax")) { xctx->netlist_type=CAD_TEDAX_NETLIST; } else if(!strcmp(argv[3], "symbol")) { xctx->netlist_type=CAD_SYMBOL_ATTRS; } else { dbg(0, "Warning: undefined netlist format: %s\n", argv[3]); } set_tcl_netlist_type(); } else if(!strcmp(argv[2], "no_draw")) { /* set no drawing flag (0 or 1) */ int s = atoi(argv[3]); if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->no_draw=s; } else if(!strcmp(argv[2], "no_undo")) { /* set to 1 to disable undo */ int s = atoi(argv[3]); if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->no_undo=s; } else if(!strcmp(argv[2], "rectcolor")) { /* set current layer (0, 1, .... , cadlayers-1) */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->rectcolor=atoi(argv[3]); if(xctx->rectcolor < 0 ) xctx->rectcolor = 0; if(xctx->rectcolor >= cadlayers ) xctx->rectcolor = cadlayers - 1; rebuild_selected_array(); if(xctx->lastsel) { change_layer(); } } else if(!strcmp(argv[2], "sch_to_compare")) { /* set name of schematic to compare current window with */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strncpy(xctx->sch_to_compare, abs_sym_path(argv[3], ""), S(xctx->sch_to_compare)); } else if(!strcmp(argv[2], "schsymbolprop")) { /* set global symbol attribute string */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->schsymbolprop, argv[3]); } else if(!strcmp(argv[2], "schprop")) { /* set schematic global spice attribute string */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->schprop, argv[3]); } else if(!strcmp(argv[2], "schverilogprop")) { /* set schematic global verilog attribute string */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->schverilogprop, argv[3]); } else if(!strcmp(argv[2], "schvhdlprop")) { /* set schematic global vhdl attribute string */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->schvhdlprop, argv[3]); } else if(!strcmp(argv[2], "schtedaxprop")) { /* set schematic global tedax attribute string */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} my_strdup(_ALLOC_ID_, &xctx->schtedaxprop, argv[3]); } else if(!strcmp(argv[2], "text_svg")) { /* set to 1 to use svg elements */ text_svg=atoi(argv[3]); } else if(!strcmp(argv[2], "semaphore")) { /* debug */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} dbg(1, "scheduler(): set semaphore to %s\n", argv[3]); xctx->semaphore=atoi(argv[3]); } else if(!strcmp(argv[2], "show_hidden_texts")) { /* set to 1 to enable showing texts with attr hide=true */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} dbg(1, "scheduler(): set show_hidden_texts to %s\n", argv[3]); xctx->show_hidden_texts=atoi(argv[3]); } else if(!strcmp(argv[2], "sym_txt")) { /* set to 0 to hide symbol texts */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->sym_txt=atoi(argv[3]); } else { cmd_found = 0; } } /* argv[2][0] >= 'n' */ } /* if(argc > 3 */ } /************ end xschem set subcommands *************/ /* set_different_tok str new_str old_str * Return string 'str' replacing/adding/removing tokens that are * different between 'new_str' and 'old_str' */ else if(!strcmp(argv[1], "set_different_tok") ) { char *s = NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 5) {Tcl_SetResult(interp, "Missing arguments", TCL_STATIC);return TCL_ERROR;} my_strdup(_ALLOC_ID_, &s, argv[2]); set_different_token(&s, argv[3], argv[4]); Tcl_SetResult(interp, s, TCL_VOLATILE); my_free(_ALLOC_ID_, &s); } /* set_modify * Force modify status on current schematic */ else if(!strcmp(argv[1], "set_modify")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { set_modify(atoi(argv[2])); } Tcl_ResetResult(interp); } /* setprop instance|symbol|text|rect ref tok [val] [fast] * * setprop instance inst tok [val] [fast] * set attribute 'tok' of instance (name or number) 'inst' to value 'val' * If 'val' not given (no attribute value) delete attribute from instance * If 'fast' argument if given does not redraw and is not undoable * * setprop symbol name tok [val] * Set attribute 'tok' of symbol name 'name' to 'val' * If 'val' not given (no attribute value) delete attribute from symbol * * setprop rect lay n tok [val] [fast|fastundo] * Set attribute 'tok' of rectangle number'n' on layer 'lay' * If 'val' not given (no attribute value) delete attribute from rect * If 'fast' argument is given does not redraw and is not undoable * If 'fastundo' s given same as above but action is undoable. * * setprop rect 2 n fullxzoom * setprop rect 2 n fullyzoom * These commands do full x/y zoom of graph 'n' (on layer 2, this is hardcoded). * * setprop text n tok [val] [fast|fastundo] * Set attribute 'tok' of text number 'n' * If 'val' not given (no attribute value) delete attribute from text * If 'fast' argument is given does not redraw and is not undoable * If 'fastundo' s given same as above but action is undoable. */ else if(!strcmp(argv[1], "setprop")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} /* 0 1 2 3 4 5 6 * xschem setprop instance R4 value [30k] [fast] */ if(argc > 2 && !strcmp(argv[2], "instance")) { int inst, fast=0; if(argc > 6) { if(!strcmp(argv[6], "fast")) { fast = 1; argc = 6; } } else if(argc > 5) { if(!strcmp(argv[5], "fast")) { fast = 1; argc = 5; } } if(argc < 5) { Tcl_SetResult(interp, "xschem setprop instance needs 2 or 3 additional arguments", TCL_STATIC); return TCL_ERROR; } if((inst = get_instance(argv[3])) < 0 ) { Tcl_SetResult(interp, "xschem setprop: instance not found", TCL_STATIC); return TCL_ERROR; } else { char *type; int floaters = 0, cond; char *subst = NULL; int s_pnetname = tclgetboolvar("show_pin_net_names"); floaters = set_modify(1); floaters |= s_pnetname; if(!fast) { if(!floaters) bbox(START,0.0,0.0,0.0,0.0); symbol_bbox(inst, &xctx->inst[inst].x1, &xctx->inst[inst].y1, &xctx->inst[inst].x2, &xctx->inst[inst].y2); if(!floaters) bbox(ADD, xctx->inst[inst].x1, xctx->inst[inst].y1, xctx->inst[inst].x2, xctx->inst[inst].y2); xctx->push_undo(); } xctx->prep_hash_inst=0; xctx->prep_net_structs=0; xctx->prep_hi_structs=0; if(!strcmp(argv[4], "name") && fast == 0) hash_names(-1, XINSERT); if(argc > 5) { my_strdup2(_ALLOC_ID_, &subst, subst_token(xctx->inst[inst].prop_ptr, argv[4], argv[5])); } else {/* assume argc == 5 , delete attribute */ my_strdup2(_ALLOC_ID_, &subst, subst_token(xctx->inst[inst].prop_ptr, argv[4], NULL)); } hash_names(inst, XDELETE); new_prop_string(inst, subst, fast, tclgetboolvar("disable_unique_names")); my_free(_ALLOC_ID_, &subst); set_inst_flags(&xctx->inst[inst]); hash_names(inst, XINSERT); type=xctx->sym[xctx->inst[inst].ptr].type; cond= type && IS_LABEL_SH_OR_PIN(type); if(cond) { xctx->inst[inst].flags |= PIN_OR_LABEL; my_strdup2(_ALLOC_ID_, &xctx->inst[inst].lab, get_tok_value(xctx->inst[inst].prop_ptr, "lab", 0)); } else xctx->inst[inst].flags &= ~PIN_OR_LABEL; if(!fast) { /* new symbol bbox after prop changes (may change due to text length) */ symbol_bbox(inst, &xctx->inst[inst].x1, &xctx->inst[inst].y1, &xctx->inst[inst].x2, &xctx->inst[inst].y2); if(!floaters) { bbox(ADD, xctx->inst[inst].x1, xctx->inst[inst].y1, xctx->inst[inst].x2, xctx->inst[inst].y2); /* redraw symbol with new props */ bbox(SET,0.0,0.0,0.0,0.0); } draw(); if(!floaters) { bbox(END,0.0,0.0,0.0,0.0); } } Tcl_SetResult(interp, xctx->inst[inst].instname , TCL_VOLATILE); } } else if(argc > 2 && !strcmp(argv[2], "symbol")) { /* 0 1 2 3 4 5 * xschem setprop symbol name token [value] */ int i; xSymbol *sym; if(argc < 4) { Tcl_SetResult(interp, "xschem setprop symbol needs 1 or 2 or 3 additional arguments", TCL_STATIC); return TCL_ERROR; } i = get_symbol(argv[3]); if(i == -1) { Tcl_SetResult(interp, "Symbol not found", TCL_STATIC); return TCL_ERROR; } sym = &xctx->sym[i]; if(argc > 5) my_strdup2(_ALLOC_ID_, &sym->prop_ptr, subst_token(sym->prop_ptr, argv[4], argv[5])); else my_strdup2(_ALLOC_ID_, &sym->prop_ptr, subst_token(sym->prop_ptr, argv[4], NULL)); /* delete attr */ } else if(argc > 5 && !strcmp(argv[2], "rect")) { /* 0 1 2 3 4 5 6 7 * xschem setprop rect c n token [value] [fast|fastundo] */ int change_done = 0; int fast = 0; xRect *r; int c = atoi(argv[3]); int n = atoi(argv[4]); if(!(c>=0 && c < cadlayers && n >=0 && n < xctx->rects[c]) ) { Tcl_SetResult(interp, "xschem setprop rect: wrong layer or rect number", TCL_STATIC); return TCL_ERROR; } r = &xctx->rect[c][n]; if(argc > 7) { if(!strcmp(argv[7], "fast")) { fast = 1; argc = 7; } if(!strcmp(argv[7], "fastundo")) { fast = 3; argc = 7; } } else if(argc > 6) { if(!strcmp(argv[6], "fast")) { fast = 1; argc = 6; } if(!strcmp(argv[6], "fastundo")) { fast = 3; argc = 6; } } if(!fast) { bbox(START,0.0,0.0,0.0,0.0); } if(argc > 5 && c == 2 && !strcmp(argv[5], "fullxzoom")) { Graph_ctx *gr = &xctx->graph_struct; int dataset; setup_graph_data(n, 0, gr); if(xctx->raw && gr->dataset >= 0 && gr->dataset < xctx->raw->datasets) dataset = gr->dataset; else dataset = -1; graph_fullxzoom(n, gr, dataset); } if(argc > 5 && c == 2 && !strcmp(argv[5], "fullyzoom")) { xRect *r = &xctx->rect[c][n]; Graph_ctx *gr = &xctx->graph_struct; int dataset; setup_graph_data(n, 0, gr); if(xctx->raw && gr->dataset >= 0 && gr->dataset < xctx->raw->datasets) dataset = gr->dataset; else dataset = -1; graph_fullyzoom(r, gr, dataset); } else if(argc > 6) { /* verify if there is some difference */ if(strcmp(argv[6], get_tok_value(r->prop_ptr, argv[5], 0))) { change_done = 1; if(fast == 3 || fast == 0) xctx->push_undo(); my_strdup2(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, argv[5], argv[6])); } } else { get_tok_value(r->prop_ptr, argv[5], 0); if(xctx->tok_size) { change_done = 1; if(fast == 3 || fast == 0) xctx->push_undo(); my_strdup2(_ALLOC_ID_, &r->prop_ptr, subst_token(r->prop_ptr, argv[5], NULL)); /* delete attr */ } } if(change_done) set_modify(1); set_rect_flags(r); /* set cached .flags bitmask from attributes */ if(!fast) { bbox(ADD, r->x1, r->y1, r->x2, r->y2); /* redraw rect with new props */ bbox(SET,0.0,0.0,0.0,0.0); draw(); bbox(END,0.0,0.0,0.0,0.0); } Tcl_ResetResult(interp); } else if(argc > 4 && !strcmp(argv[2], "text")) { /* 0 1 2 3 4 5 6 * xschem setprop text n token value [fast] */ int change_done = 0; int tmp, fast = 0; double xx1, xx2, yy1, yy2, dtmp; xText *t; int n = atoi(argv[3]); if(!(n >=0 && n < xctx->texts) ) { Tcl_SetResult(interp, "xschem setprop text: wrong text number", TCL_STATIC); return TCL_ERROR; } t = &xctx->text[n]; if(argc > 6) { if(!strcmp(argv[6], "fast")) { fast = 1; argc = 6; } if(!strcmp(argv[6], "fastundo")) { fast = 3; argc = 6; } } else if(argc > 5) { if(!strcmp(argv[5], "fast")) { fast = 1; argc = 5; } if(!strcmp(argv[5], "fastundo")) { fast = 3; argc = 5; } } if(!fast) { bbox(START,0.0,0.0,0.0,0.0); } if(argc > 5) { /* verify if there is some difference */ if(strcmp(argv[5], get_tok_value(t->prop_ptr, argv[4], 0))) { change_done = 1; if(fast == 3 || fast == 0) xctx->push_undo(); my_strdup2(_ALLOC_ID_, &t->prop_ptr, subst_token(t->prop_ptr, argv[4], argv[5])); } } else { get_tok_value(t->prop_ptr, argv[4], 0); if(xctx->tok_size) { change_done = 1; if(fast == 3 || fast == 0) xctx->push_undo(); my_strdup2(_ALLOC_ID_, &t->prop_ptr, subst_token(t->prop_ptr, argv[4], NULL)); /* delete attr */ } } if(change_done) set_modify(1); set_text_flags(t); text_bbox(get_text_floater(n), t->xscale, t->yscale, t->rot, t->flip, t->hcenter, t->vcenter, t->x0, t->y0, &xx1,&yy1,&xx2,&yy2, &tmp, &dtmp); if(!fast) { bbox(ADD, xx1, yy1, xx2, yy2); /* redraw rect with new props */ bbox(SET,0.0,0.0,0.0,0.0); draw(); bbox(END,0.0,0.0,0.0,0.0); } Tcl_ResetResult(interp); } } /* simulate [callback] * Run a simulation (start simulator configured as default in * Tools -> Configure simulators and tools) * If 'callback' procedure name is given execute the procedure when simulation * is finished. all execute(..., id) data is available (id = execute(id) ) * A callback prodedure is useful if simulation is launched in background mode * ( set sim(spice,1,fg) 0 ) */ else if(!strcmp(argv[1], "simulate") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(set_netlist_dir(0, NULL) ) { if(argc > 2) tclvareval("simulate ", argv[2], NULL); else tcleval("simulate"); } Tcl_ResetResult(interp); } /* snap_wire * Start a GUI start snapped wire placement (click to start a * wire to closest pin/net endpoint) */ else if(!strcmp(argv[1], "snap_wire")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTSNAPWIRE; } /* str_replace str rep with [escape] * replace 'rep' with 'with' in string 'str' * if rep not preceeded by an 'escape' character */ else if(!strcmp(argv[1], "str_replace")) { int escape = 0; if(argc > 5) escape = argv[5][0]; if(argc > 4) { Tcl_AppendResult(interp, str_replace(argv[2], argv[3], argv[4], escape), NULL); } else { Tcl_SetResult(interp, "Missing arguments", TCL_STATIC); return TCL_ERROR; } } /* subst_tok str tok newval * Return string 'str' with 'tok' attribute value replaced with 'newval' */ else if(!strcmp(argv[1], "subst_tok")) { char *s=NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 5) {Tcl_SetResult(interp, "Missing arguments", TCL_STATIC);return TCL_ERROR;} my_strdup(_ALLOC_ID_, &s, subst_token(argv[2], argv[3], strcmp(argv[4], "NULL") ? argv[4] : NULL)); Tcl_SetResult(interp, s, TCL_VOLATILE); my_free(_ALLOC_ID_, &s); } /* symbol_in_new_window [new_process] * When a symbol is selected edit it in a new tab/window if not already open. * If nothing selected open another window of the second schematic (issues a warning). * if 'new_process' is given start a new xschem process */ else if(!strcmp(argv[1], "symbol_in_new_window")) { int new_process = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 && !strcmp(argv[2], "new_process")) new_process = 1; symbol_in_new_window(new_process); Tcl_ResetResult(interp); } /* switch [window_path |schematic_name] * Switch context to indicated window path or schematic name * returns 0 if switch was successfull or 1 in case of errors * (no tabs/windows present or no matching winpath / schematic name * found). */ else if(!strcmp(argv[1], "switch")) { int r = 1; /* error: no switch was done */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) r = new_schematic("switch", argv[2], NULL, 1); Tcl_SetResult(interp, my_itoa(r), TCL_VOLATILE); } /* symbols [n] * if 'n' given list symbol with name or number 'n', else * list all used symbols */ else if(!strcmp(argv[1], "symbols")) { int i; char n[100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { i = get_symbol(argv[2]); Tcl_AppendResult(interp, my_itoa(i), " {", xctx->sym[i].name, "}", NULL); } else for(i=0; isymbols; ++i) { my_snprintf(n , S(n), "%d", i); Tcl_AppendResult(interp, " {", n, " ", "{", xctx->sym[i].name, "}", "}\n", NULL); } } else { cmd_found = 0;} break; case 't': /*----------------------------------------------*/ /* tab_list * list all windows / tabs with window pathname and associated filename */ if(!strcmp(argv[1], "tab_list")) { int i; Xschem_ctx *ctx, **save_xctx = get_save_xctx(); int found = 0; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 0; i < MAX_NEW_WINDOWS; ++i) { ctx = save_xctx[i]; /* if only one schematic it is not yet saved in save_xctx */ if(get_window_count() == 0 && i == 0) { ctx = xctx; } if(i == 0 ) { if(ctx) Tcl_AppendResult(interp, ".drw", " {", ctx->sch[ctx->currsch], "}\n", NULL); } else if(ctx) { Tcl_AppendResult(interp, get_window_path(i), " {", ctx->sch[ctx->currsch], "}\n", NULL); } } dbg(1, "check_loaded: return %d\n", found); return found; } /* table_read [table_file] * If a simulation raw file is lodaded unload from memory. * else read a tabular file 'table_file' * First line is the header line containing variable names. * data is presented in column format after the header line * First column is sweep (x-axis) variable * Double empty lines start a new dataset * Single empty lines are ignored * Datasets can have different # of lines. * new dataset do not start with a header row. * Lines beginning with '#' are comments and ignored * * time var_a var_b var_c * # this is a comment, ignored * 0.0 0.0 1.8 0.3 * * 0.1 0.0 1.5 0.6 * ... ... ... ... * * * 0.0 0.0 1.8 0.3 * 0.1 0.0 1.5 0.6 * ... ... ... ... */ else if(!strcmp(argv[1], "table_read")) { char f[PATH_MAX + 100]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(sch_waves_loaded() >= 0) { extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 1); tclsetvar("rawfile_loaded", "0"); } else if(argc > 2) { my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[2], home_dir); tcleval(f); my_strncpy(f, tclresult(), S(f)); extra_rawfile(3, NULL, NULL); free_rawfile(&xctx->raw, 0); table_read(f); if(sch_waves_loaded() >= 0) { tclsetvar("rawfile_loaded", "1"); draw(); } else tclsetvar("rawfile_loaded", "0"); } Tcl_ResetResult(interp); } /* test * Testmode ... */ else if(!strcmp(argv[1], "test") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} Tcl_ResetResult(interp); } /* text x y rot flip text props size draw * Create a text object * x, y, rot, flip specify the position and orientation * text is the text string * props is the attribute string * size sets the size * draw is a flag. If set to 1 will draw the created text */ else if(!strcmp(argv[1], "text") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 10) {Tcl_SetResult(interp, "xschem text requires 8 additional arguments", TCL_STATIC); return TCL_ERROR;} create_text(atoi(argv[9]), atof(argv[2]), atof(argv[3]), atoi(argv[4]), atoi(argv[5]), argv[6], argv[7], atof(argv[8]), atof(argv[9])); Tcl_ResetResult(interp); } /* text_string n * get text string of text object 'n' */ else if(!strcmp(argv[1], "text_string") ) { int n; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc < 3) {Tcl_SetResult(interp, "xschem text_string requires 1 additional argument", TCL_STATIC); return TCL_ERROR;} n = atoi(argv[2]); if(n >= 0 && n < xctx->texts) { Tcl_SetResult(interp, xctx->text[n].txt_ptr, TCL_VOLATILE); } else { Tcl_ResetResult(interp); } } /* toggle_colorscheme * Toggle dark/light colorscheme */ else if(!strcmp(argv[1], "toggle_colorscheme")) { int d_c; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} d_c = tclgetboolvar("dark_colorscheme"); d_c = !d_c; tclsetboolvar("dark_colorscheme", d_c); tclsetdoublevar("dim_value", 0.0); tclsetdoublevar("dim_bg", 0.0); build_colors(0.0, 0.0); draw(); Tcl_ResetResult(interp); } /* toggle_ignore * toggle *_ignore=true attribute on selected instances * * = {spice,verilog,vhdl,tedax} depending on current netlist mode */ else if(!strcmp(argv[1], "toggle_ignore")) { int i, n, first = 1, remove = 0; char *attr; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(xctx->netlist_type == CAD_VERILOG_NETLIST) attr="verilog_ignore"; else if(xctx->netlist_type == CAD_VHDL_NETLIST) attr="vhdl_ignore"; else if(xctx->netlist_type == CAD_TEDAX_NETLIST) attr="tedax_ignore"; else if(xctx->netlist_type == CAD_SPICE_NETLIST) attr="spice_ignore"; else attr = NULL; if(attr) { rebuild_selected_array(); for(n=0; n < xctx->lastsel; ++n) { if(xctx->sel_array[n].type == ELEMENT) { i = xctx->sel_array[n].n; if(first) { xctx->push_undo(); first = 0; } remove = 0; if(!strboolcmp(get_tok_value(xctx->inst[i].prop_ptr, attr, 0), "true")) remove = 1; if(remove) { my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, subst_token(xctx->inst[i].prop_ptr, attr, NULL)); } else { my_strdup(_ALLOC_ID_, &xctx->inst[i].prop_ptr, subst_token(xctx->inst[i].prop_ptr, attr, "true")); } set_inst_flags(&xctx->inst[i]); set_modify(1); } } draw(); } Tcl_ResetResult(interp); } /* touch x1 y1 x2 y2 x0 y0 * returns 1 if line {x1 y1 x2 y2} touches point {x0 y0}, 0 otherwise */ else if(!strcmp(argv[1], "touch") ) { if(argc>7) { double x1, y1, x2, y2, x0, y0; int r; x0 = atof(argv[6]); y0 = atof(argv[7]); x1 = atof(argv[2]); y1 = atof(argv[3]); x2 = atof(argv[4]); y2 = atof(argv[5]); r = touch(x1, y1, x2, y2, x0, y0); Tcl_SetResult(interp, my_itoa(r), TCL_VOLATILE); } } /* translate n str * Translate string 'str' replacing @xxx tokens with values in instance 'n' attributes * Example: xschem translate vref {the voltage is @value} * the voltage is 1.8 */ else if(!strcmp(argv[1], "translate") ) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc>3) { int i; char *s = NULL; if((i = get_instance(argv[2])) < 0 ) { Tcl_SetResult(interp, "xschem translate: instance not found", TCL_STATIC); return TCL_ERROR; } my_strdup2(_ALLOC_ID_, &s, translate(i, argv[3])); Tcl_ResetResult(interp); Tcl_SetResult(interp, s, TCL_VOLATILE); my_free(_ALLOC_ID_, &s); } } /* trim_chars str sep * Remove leading and trailing chars matching any character in 'sep' from str */ else if(!strcmp(argv[1], "trim_chars")) { if(argc > 3) { char *s = trim_chars(argv[2], argv[3]); Tcl_SetResult(interp, s, TCL_VOLATILE); } } /* trim_wires * Remove operlapping wires, join lines, trim wires at intersections */ else if(!strcmp(argv[1], "trim_wires")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->push_undo(); trim_wires(); draw(); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'u': /*----------------------------------------------*/ /* undo Undo last action */ if(!strcmp(argv[1], "undo")) { int redo = 0, set_modify = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { redo = atoi(argv[2]); } if(argc > 3) { set_modify = atoi(argv[3]); } xctx->pop_undo(redo, set_modify); /* 2nd param: set_modify_status */ Tcl_ResetResult(interp); } /* undo_type disk|memory * Use disk file ('disk') or RAM ('memory') for undo bufer */ else if(!strcmp(argv[1], "undo_type")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { dbg(1, "xschem undo_type %s\n", argv[2]); if(!strcmp(argv[2], "disk")) { if(xctx->undo_type == 1) { mem_delete_undo(); /*reset memory undo */ } /* redefine undo function pointers */ xctx->push_undo = push_undo; xctx->pop_undo = pop_undo; xctx->delete_undo = delete_undo; xctx->clear_undo = clear_undo; xctx->undo_type = 0; /* disk */ } else { /* "memory" */ if(xctx->undo_type == 0) { delete_undo(); /*reset disk undo */ } /* redefine undo function pointers */ xctx->push_undo = mem_push_undo; xctx->pop_undo = mem_pop_undo; xctx->delete_undo = mem_delete_undo; xctx->clear_undo = mem_clear_undo; xctx->undo_type = 1; /* memory */ } } } /* unhilight_all * Clear all highlights */ else if(!strcmp(argv[1], "unhilight_all")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} xctx->enable_drill=0; clear_all_hilights(); /* undraw_hilight_net(1); */ draw(); Tcl_ResetResult(interp); } /* unhilight * Unhighlight selected nets/pins */ else if(!strcmp(argv[1], "unhilight")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} unhilight_net(); } /* unselect_all [draw] * Unselect everything. If draw is given and set to '0' no drawing is done */ else if(!strcmp(argv[1], "unselect_all")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) unselect_all(atoi(argv[2])); else unselect_all(1); Tcl_ResetResult(interp); } /* update_all_sym_bboxes * Update all symbol bounding boxes (useful if show_pin_net_names is set) */ else if(!strcmp(argv[1], "update_all_sym_bboxes")) { int i; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 0; i < xctx->texts; i++) if(xctx->text[i].flags & TEXT_FLOATER) { my_free(_ALLOC_ID_, &xctx->text[i].floater_ptr); /* clear floater cached value */ } for(i = 0; i < xctx->instances; ++i) { symbol_bbox(i, &xctx->inst[i].x1, &xctx->inst[i].y1, &xctx->inst[i].x2, &xctx->inst[i].y2); } Tcl_ResetResult(interp); } /* update_op * update tcl ngspice::ngspice array data from raw file point 0 */ else if(!strcmp(argv[1], "update_op")) { int ret; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} ret = update_op(); Tcl_SetResult(interp, my_itoa(ret), TCL_VOLATILE); } else { cmd_found = 0;} break; case 'v': /*----------------------------------------------*/ /* view_prop * View attributes of selected element (read only) * if multiple selection show the first element (in xschem array order) */ if(!strcmp(argv[1], "view_prop")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} edit_property(2); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'w': /*----------------------------------------------*/ /* warning_overlapped_symbols [sel] Highlight or select (if 'sel' set to 1) perfectly overlapped instances this is usually an error and difficult to grasp visually */ if(!strcmp(argv[1], "warning_overlapped_symbols")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { warning_overlapped_symbols(atoi(argv[2])); } else { warning_overlapped_symbols(0); } } /* windowid topwin_path * Used by xschem.tcl for configure events (set icon) */ else if(!strcmp(argv[1], "windowid")) { if(argc > 2) { windowid(argv[2]); } } /* wire_coord n * return 4 coordinates of wire[n] */ else if(!strcmp(argv[1], "wire_coord")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { char *r = NULL; int n = atoi(argv[2]); if(n > 0 && n < xctx->wires) { xWire * const wire = xctx->wire; my_mstrcat(_ALLOC_ID_, &r, dtoa(wire[n].x1), " ", NULL); my_mstrcat(_ALLOC_ID_, &r, dtoa(wire[n].y1), " ", NULL); my_mstrcat(_ALLOC_ID_, &r, dtoa(wire[n].x2), " ", NULL); my_mstrcat(_ALLOC_ID_, &r, dtoa(wire[n].y2), NULL); Tcl_SetResult(interp, r, TCL_VOLATILE); } } } /* wire [x1 y1 x2 y2] [pos] [prop] [sel] * Place a new wire * if no coordinates are given start a GUI wire placement */ else if(!strcmp(argv[1], "wire")) { double x1,y1,x2,y2; int pos = -1, save, sel = 0; const char *prop=NULL; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 5) { x1=atof(argv[2]); y1=atof(argv[3]); x2=atof(argv[4]); y2=atof(argv[5]); ORDER(x1,y1,x2,y2); if(argc > 6) pos=atoi(argv[6]); if(argc > 7) prop = argv[7]; if(argc > 8) sel = atoi(argv[8]); xctx->push_undo(); storeobject(pos, x1,y1,x2,y2,WIRE,0,(short)sel,prop); if(sel) xctx->need_reb_sel_arr=1; xctx->prep_hi_structs=0; xctx->prep_net_structs=0; xctx->prep_hash_wires=0; save = xctx->draw_window; xctx->draw_window = 1; drawline(WIRELAYER,NOW, x1,y1,x2,y2, 0, NULL); xctx->draw_window = save; if(tclgetboolvar("autotrim_wires")) trim_wires(); set_modify(1); } else { xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTWIRE; } } /* wire_cut [x y] [noalign] * start a wire cut operation. Point the mouse in the middle of a wire and * Alt-click right button. * if x and y are given cut wire at given point * if noalign is given and is set to 'noalign' do not align the cut point to closest snap point */ else if(!strcmp(argv[1], "wire_cut")) { int i, align = 1; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 2; i < argc; i++) { if(!strcmp(argv[i], "noalign")) align = 0; } if(argc > 3) { break_wires_at_point(atof(argv[2]), atof(argv[3]), align); } else { if(align) { xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTWIRECUT; } else { xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTWIRECUT2; } } Tcl_ResetResult(interp); } else { cmd_found = 0;} break; case 'x': /*----------------------------------------------*/ #ifdef HAS_XCB /* xcb_info * For debug */ if(!strcmp(argv[1], "xcb_info")) { dbg(0, "maximum xcb req length=%u\n", xcb_get_maximum_request_length(xcb_conn)); } else { cmd_found = 0;} #endif break; case 'z': /*----------------------------------------------*/ /* zoom_box [x1 y1 x2 y2] [factor] * Zoom to specified coordinates, if 'factor' is given reduce view (factor < 1.0) * or add border (factor > 1.0) * If no coordinates are given start GUI zoom box operation */ if(!strcmp(argv[1], "zoom_box")) { double x1, y1, x2, y2, factor; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} dbg(1, "scheduler(): xschem zoom_box: argc=%d, argv[2]=%s\n", argc, argv[2]); if(argc==6 || argc == 7) { x1 = atof(argv[2]); y1 = atof(argv[3]); x2 = atof(argv[4]); y2 = atof(argv[5]); if(argc == 7) factor = atof(argv[6]); else factor = 1.; if(factor == 0.) factor = 1.; zoom_box(x1, y1, x2, y2, factor); change_linewidth(-1.); draw(); } else { xctx->ui_state |= MENUSTART; xctx->ui_state2 = MENUSTARTZOOM; } Tcl_ResetResult(interp); } /* zoom_full [center|nodraw|nolinewidth] * Set full view. * If 'center' is given center vire instead of lower-left align * If 'nodraw' is given don't redraw * If 'nolinewidth]' is given don't reset line widths. */ else if(!strcmp(argv[1], "zoom_full")) { int i, flags = 1; int draw = 1; double shrink = 0.97; char * endptr; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} for(i = 2; i < argc; ++i) { if(!strcmp(argv[i], "center")) flags |= 2; else if(!strcmp(argv[i], "nodraw")) draw = 0; else if(!strcmp(argv[i], "nolinewidth")) flags &= ~1; else { shrink = strtod(argv[i], &endptr); if(endptr == argv[i]) shrink = 1.0; } } if(tclgetboolvar("zoom_full_center")) flags |= 2; zoom_full(draw, 0, flags, shrink); Tcl_ResetResult(interp); } /* zoom_hilighted * Zoom to highlighted objects */ else if(!strcmp(argv[1], "zoom_hilighted")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} zoom_full(1, 2, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); Tcl_ResetResult(interp); } /* zoom_in * Zoom in drawing */ else if(!strcmp(argv[1], "zoom_in")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} view_zoom(0.0); Tcl_ResetResult(interp); } /* zoom_out * Zoom out drawing */ else if(!strcmp(argv[1], "zoom_out")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} view_unzoom(0.0); Tcl_ResetResult(interp); } /* zoom_selected * Zoom to selection */ else if(!strcmp(argv[1], "zoom_selected")) { if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} zoom_full(1, 1, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97); Tcl_ResetResult(interp); } else { cmd_found = 0;} break; default: cmd_found = 0; break; } /* switch */ if(!cmd_found) { Tcl_AppendResult(interp, "xschem ", argv[1], ": invalid command.", NULL); return TCL_ERROR; } return TCL_OK; } double tclgetdoublevar(const char *s) { const char *p; p = Tcl_GetVar(interp, s, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG); if(!p) { dbg(0, "%s\n", tclresult()); return 0.0; } return atof_spice(p); } int tclgetintvar(const char *s) { const char *p; p = Tcl_GetVar(interp, s, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG); if(!p) { dbg(0, "%s\n", tclresult()); return 0; } return atoi(p); } int tclgetboolvar(const char *s) { int res; const char *p; p = Tcl_GetVar(interp, s, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG); if(!p) { dbg(0, "%s\n", tclresult()); return 0; } if(Tcl_GetBoolean(interp, p, &res) == TCL_ERROR) { dbg(0, "%s\n", tclresult()); return 0; } return res; } const char *tclgetvar(const char *s) { const char *p; p = Tcl_GetVar(interp, s, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG); if(!p) { dbg(1, "%s\n", tclresult()); return NULL; } return p; } const char *tcleval(const char str[]) { if(Tcl_GlobalEval(interp, str) != TCL_OK) { fprintf(errfp, "tcleval(): evaluation of script: %s failed\n", str); fprintf(errfp, " : %s\n", Tcl_GetStringResult(interp)); Tcl_ResetResult(interp); } return Tcl_GetStringResult(interp); } const char *tclresult(void) { return Tcl_GetStringResult(interp); } void tclsetvar(const char *s, const char *value) { if(!Tcl_SetVar(interp, s, value, TCL_GLOBAL_ONLY)) { fprintf(errfp, "tclsetvar(): error setting variable %s to %s\n", s, value); } } void tclsetdoublevar(const char *s, const double value) { char str[80]; sprintf(str, "%.16g", value); if(!Tcl_SetVar(interp, s, str, TCL_GLOBAL_ONLY)) { fprintf(errfp, "tclsetdoublevar(): error setting variable %s to %g\n", s, value); } } void tclsetintvar(const char *s, const int value) { char str[80]; sprintf(str, "%d", value); if(!Tcl_SetVar(interp, s, str, TCL_GLOBAL_ONLY)) { fprintf(errfp, "tclsetintvar(): error setting variable %s to %d\n", s, value); } } void tclsetboolvar(const char *s, const int value) { if(!Tcl_SetVar(interp, s, (value ? "1" : "0"), TCL_GLOBAL_ONLY)) { fprintf(errfp, "tclsetboolvar(): error setting variable %s to %d\n", s, value); } } /* Replacement for Tcl_VarEval, which despite being very useful is deprecated */ int tclvareval(const char *script, ...) { char *str = NULL; int return_code; size_t size; const char *p; va_list args; va_start(args, script); size = my_strcat(_ALLOC_ID_, &str, script); while( (p = va_arg(args, const char *)) ) { size = my_strcat(_ALLOC_ID_, &str, p); dbg(2, "tclvareval(): p=%s, str=%s, size=%d\n", p, str, size); } dbg(2, "tclvareval(): script=%s, str=%s, size=%d\n", script, str ? str : "", size); return_code = Tcl_EvalEx(interp, str, (int)size, TCL_EVAL_GLOBAL); va_end(args); if(return_code != TCL_OK) { dbg(0, "tclvareval(): error executing %s: %s\n", str, tclresult()); Tcl_ResetResult(interp); } my_free(_ALLOC_ID_, &str); return return_code; }