How to get bash like environment variable value? - tcl

There is a string like this: set mystring "$ENV_NAME/c/a/b/c"
How to get the full path?

To get the full path, you will need to use file join. To get to the environment variable, you will need to access the global env array:
set fullPath [file join $env(ENV_NAME) c a b c]
If ENV_NAME=/usr/bin, then the above will return fullPath as /usr/bin/c/a/b/c. You will get similar results in Windows platform.

Unfortunately your question is not very clear.
You say that you have the string set mystring "$ENV_NAME/c/a/b/c", which could either mean that this is some user supplied input, or it could be part of your program/Tcl script.
If this is indeed user supplied input, I suggest you use eval:
proc substEnv {input} {
set __ENV [array get ::env]
dict with __ENV {}
eval $input
}
puts [substEnv {set mystring "$ENV_NAME/c/a/b/c"}]
If you can trust the user, this is fine, otherwise I suggest using a safe interpreter. Note that a safe interpreter does not have access to the ::env array, so you have to pass the contents of it to the safe interpreter.
But if this is part of your program, I suggest you use file join instead
set path [file join $::env(ENV_NAME) c a b c]
file join deals with things like $::env(ENV_NAME) is the root directory (/ on *nix or C:\ on windows, which both end with the path separator as special case)

Related

Reading cmd arguments in TCL file

I am trying to run a tcl script through .bat file. I want to read some cmd arguments in the tcl script. Below is my code:
Command to run:
D:\Cadence\Sigrity2021.1\tools\bin\PowerSI.exe -tcl abcd.tcl %new_var%.spd %new_file_name%
Below is how I am trying to read the variable in the tcl file:
sigrity::open document [lindex $argv 0] {!}
It open up the Cadence Sigrity, but I see the below error:
How do I read cmd argument in tcl?
If you have no other way to do it that you can find (and it sounds like that might be the case) then you can fake it by writing a helper file with content like this, filling in the real arguments in the appropriate places:
# Name of script to call
set ::argv0 "abcd.tcl"
# Arguments to pass
set ::argv {}
lappend ::argv "%new_var%.spd"
lappend ::argv "%new_file_name%"
# Number of arguments (rarely used)
set ::argc [llength $::argv]
# Do the call
source $::argv0
Then you can pass that file to PowerSI and it will set things up and chain to the real file. It's messy, but practical.
If you're writing this from Tcl, use the list command to do the quoting of the strings (instead of putting them in double quotes) as it will do exactly the right thing for you. If you're writing the file from another language, you'll want to make sure you put backslashes in before \, ", $ and [ characters. The fiddlyness of doing that depends on your language.

Set a variable to a filename using glob

I have a directory that contains another directory named ABC_<version number>
I'd like to set my path to whatever ABC_<version number> happens to be (in a modulefile)
How do I use glob in TCL to get the name of the directory I want and put it into a TCL variable?
Thanks!
The glob command expands wildcards, but produces a Tcl list of everything that matches, so you need to be a bit careful. What's more, the order of the list is “random” — it depends on the raw order of entries in the OS's directory structure, which isn't easily predicted in general — so you really need to decide what you want. Also, if you only want a single item of the list, you must use lindex (or lassign in a degenerate operation mode) to pick it out: otherwise your code will blow up when it encounters a user who puts special characters (space, or one of a small list of other ones) in a pathname. It pays to be safe from the beginning.
For example, if you want to only match a single element and error out otherwise, you should do this:
set thePaths [glob -directory $theDir -type d ABC_*]
if {[llength $thePaths] != 1} {
error "ambiguous match for ABC_* in $theDir"
}
set theDir [lindex $thePaths 0]
If instead you want to sort by the version number and pick the (presumably) newes, you can use lsort -dictionary. That's pretty magical internally (seriously; read the docs if you want to see what it really does), but does the right thing with all sane version number schemes.
set thePaths [glob -directory $theDir -type d ABC_*]
set theSortedPaths [lsort -dictionary -decreasing $thePaths]
set theDir [lindex $theSortedPaths 0]
You could theoretically make a custom sort by the actual date on the directories, but that's more complex and can sometimes surprise when you're doing system maintenance.
Notice the use of -type d in glob. That's a type filter, which is great in this case where you're explicitly only wanting to get directory names back. The other main useful option there (in general) is -type f to get only real files.
Turns out the answer was:
set abc_path [glob -directory $env(RELDIR) ABC_*]
No need for quotes around the path. The -directory controls where you look.
Later in the modulefile
append-path PATH $abc_path

Converting Tcl to C++

I am trying to convert some tcl script into a C++ program. I don't have much experience with tcl and am hoping someone could explain what some of the following things are actually doing in the tcl script:
1) set rtn [true_test_sfm $run_dir]
2) cd [glob $run_dir]
3) set pwd [pwd]
Is the first one just checking if true_test_sfm directory exists in run_dir?
Also, I am programming on a windows machine. Would the system function be the equivalent to exec statements in tcl? And if so how would I print the result of the system function call to stdout?
In Tcl, square brackets indicate "evaluate the code between the square brackets". The result of that evaluation is substituted for the entire square-bracketed expression. So, the first line invokes the function true_test_sfm with a single argument $run_dir; the result of that function call is then assigned to the variable rtn. Unfortunately, true_test_sfm is not a built-in Tcl function, which means it's user-defined, which means there's no way we can tell you what the effect of that function call will be based on the information you've provided here.
glob is a built-in Tcl function which takes a file pattern as an argument and then lists files that match that pattern. For example, if a directory contains files "foo", "bar" and "baz", glob b* would return a list of two files, "bar" and "baz". Therefore the second line is looking for any files that match the pattern given by $run_dir, then using the cd command (another Tcl built-in) to change to the directory found by glob. Probably $run_dir is not actually a file pattern, but an explicit file name (ie, no globbing characters like * or ? in the string), otherwise this code may break unexpectedly. On Windows, some combination of FindFirstFile/FindNextFile in C++ could be used as a substitute for glob in Tcl, and SetCurrentDirectory could substitute for cd.
pwd is another built-in Tcl function which returns the process current working directory as an absolute path. So the last line is querying the current working directory and saving the result in a variable named pwd. Here you could use GetCurrentDirectory as a substitute for pwd.

Command to return library (not work) name of a path in modelsim

I want to find a way to return the name of a library of a certain path in a VHDL Design in Modelsim.
Given a VHDL Design with a path like "/mega_tb/D0". This is compiled in a library that is NOT 'work', say "libnwork".
I can of course take a look in my 'do' file to get the correct lib name. Or I can search in ModelSim's Library tab. But I want to have or create a modelsim command which I can later use in a Tcl script, to get the correct library name.
One of the easiest ways to find something in a Tcl script file – which is all a Modelsim “do” file is — is to evaluate it. Tcl's very good at that. Of course, you don't want to have the commands do all the conventional things. Instead, we'll evaluate in a context where we can make everything do nothing except for the command that produces the information we want:
# Set up our evaluation context, 'worker'
interp create worker -safe
interp eval worker {proc unknown args {}}; # Our do-nothing handler
interp alias worker theInterestingCommand {} ourHandler
proc ourHandler args {
puts "We were called with: $args"
}
# Parse the file!
set f [open /the/file.tcl]
interp eval worker [read $f]
# Clean up
close $f
interp delete worker
Now you just have to make theInterestingCommand have the right name and extract the interesting information from the arguments. Which should be relatively easy…
Te only way I've found is to use the command
write report -tcl
This prints a long list where I have search for the lib names with regexps.
Something like
set data [ write report -tcl]
foreach_regexp { _ type lib entity} $data{
if {$type == "Entity" && $entity == [entity_of_path /mega_tb/D0] } {
....
}
}
Where I of course had to define my "foreach_regexp" procedure and my "entity_of_path" procedure. I then can use something like regsub to extract the library name.
I am still looking for a better and easier way.

how to split path names.

the input is C:\test\deva\tcl\newfiles\aug.txt
the output should be "test" "deva" "tcl" "newfiles"
"aug.txt" files or anyother ".txt" files at the end of the string should not be printed.
Reverting back to my original solution and adding some bits...
Assuming this is a filepath not a random string that happens to need to be split \
File split does almost what you want, it returns the path split up as a list . you also want to use lrange to select everything but the volume i.e something like (untested)
lrange [file split $path] 1 end-1
so you don't have c:\ which should be the first element in the list returned by file split
Additionally you may want to use file dirname first if there is any chance you will get directory path instead of a filename e.g. same caveats re testing
lrange [file split [file dirname $name]] 1 end
[split] combined with [lrange] can do what you want but in a non-portable way.
One way to make this more portable would be to use the result of calling [file separator] for splitting instead of bare "\". But since "/" are also okay both in Tcl and Windows the real way to go portably would be to repeatedly call [file dirname] on the string and extract the last component of the returned pathname using [file tail].
For more info read this, this and this.