How to manipulate components of pathnames in Tcl? - path

I'm writing a reporting script that's part of a portable testing package. The user can unzip the package anywhere on their system, which is fine, but it means I can't hardcode a path.
Let's say that on my system, this script lives at C:/projects/testpackage/foo/bar/script.tcl. I need to set a variable, packageLocation, the path to /testpackage. In this example, it would be C:/Projects/testpackage. But when the user gets the package, he or she could put it anywhere, like so:
C:/Users/whatever/testpackage.
So, how can I call two levels up from the location of my currently running script? In Batch, I could do
:: example.bat
cd %~dp0
cd ../..
set packageLocation=%cd%
In Tcl, I'm lost. I know that the current location of the running script can be called as $::argv0. I've tried using cd ../.., but to no avail. It tries to set packageLocation as "../..C:/Projects/testpackage/foo/bar/script.tcl."
Any help is appreciated. Thanks!

set where [file normalize [file dirname [info script]]]
set parts [file split $where]
set pkgloc [file join {*}[lrange $parts 0 end-2]]
Should do what you want.
It goes like this:
Obtains the directory name of the file from which the current script was read to be evaluated, then normalizes it (replaces ~, .. etc).
Splits obtained full pathname at path separators producing a list of path components.
Extracts a new list from the list of path components containing all them from the beginning except the last two, then joins them back to produce the final name.
If you have Tcl < 8.5, the last line will have to be rewritten:
set last [expr {[llength $parts] - 3}]
set pkgloc [eval [list file join] [lrange $parts 0 $last]]

Related

Is there a way to compose a batch of images with imagemagick?

I have a series of PNG images (ABC_a.png, ABC_b.png, XYZ_a.png, XYZ_b.png, BCA_a.png, BCA_b.png etc.) and would like to compose every image of the same code (i.e. the name of an image without _a or _b) within a folder.
Manually, the code would look like this:
magick composite ABC_b.png ABC_a.png ABC.png
magick composite XYZ_b.png XYZ_a.png XYZ.png
magick composite BCA_b.png BCA_a.BCA BCA.png
...
... where a partly transparent image _b would be placed "on top of" _a and the name of the output file losing its _a/_b suffix.
I looked around and tried several apporaches via mogrify or for loops (FOR %i IN (*.png) DO magick composite ...) but couldn't get it automated. Perhaps it would help to use two separate folders and working with the same image names (without the suffix), but I'm not sure...
I appreciate any tips. Please be aware that I'd need to work within the Windows CMD or PowerShell to make it happen.
There can be used the following batch file for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ImageMagick=magick.exe"
if exist "*!*_a.png" goto ExtendedVersion
setlocal EnableDelayedExpansion
for %%I in (*_a.png) do (
set "FileNameA=%%~nI"
set "FileName=!FileNameA:~0,-2!%%~xI"
if not exist "!FileName!" "!ImageMagick!" composite "!FileNameA:~0,-1!b%%~xI" "!FileNameA!%%~xI" "!FileName!"
)
endlocal
goto EndBatch
:ExtendedVersion
echo INFO: Extended version required because of a PNG file with exclamation marks.
for %%I in (*_a.png) do (
set "FileNameA=%%~nI"
setlocal EnableDelayedExpansion
set "FileName=!FileNameA:~0,-2!%%~xI"
if not exist "!FileName!" "!ImageMagick!" composite "!FileNameA:~0,-1!b%%~xI" "!FileNameA!%%~xI" "!FileName!"
endlocal
)
:EndBatch
endlocal
There is defined first the required execution environment with the first two command lines.
Next the environment variable ImageMagick is defined with the file name of this program. It would be best to add the full path because in this case cmd.exe would not need to search for magick.exe in current directory and next in all directories in string value of environment variable PATH using the file extensions in string value of environment variable PATHEXT before each execution of ImageMagick. The usage of the fully qualified file name of ImageMagick would avoid hundreds or even thousands of file system accesses on more than ten PNG files to process.
The IF condition in the fourth command line quickly checks if there is any PNG file with case-insensitive _a in the file name before the file extension .png containing one or more exclamation marks in the file name. The extended version of the processing loop is required if this condition is true.
The standard version enables first required delayed expansion. Then a FOR loop is used to process one PNG file after the other with case-insensitive _a in the file name before the file extension .png.
The current file name without file extension .png is assigned first to the environment variable FileNameA.
Next a string substitution is used to get from the string value of the environment variable FileNameA the file name without the last two characters _a concatenated with the file extension .png assigned to the environment variable FileName.
If there is not already a PNG file not ending with _a in the file name before the file extension, there is next executed ImageMagick with first argument being the corresponding _b.png file determined by using again a string substitution with using the file name string assigned to the environment variable FileName without last character a concatenated with b and the file extension .png and the_a.png file with the file extension as second argument and the file name without _a as third argument.
The command ENDLOCAL after the loop restores the previous environment before enabling delayed expansion and the command GOTO instructs the Windows Command Processor to continue processing the batch file with the command line below the label EndBatch which contains one more ENDLOCAL to restore the environment on starting the batch file processing.
The extended version is nearly the same as the standard version. The difference is that delayed variable expansion is not enabled on assigning the file name of the current _a.png file without the file extension to the environment variable FileNameA. That avoids interpreting the exclamation mark(s) in the file name as beginning/end of a delayed expanded variable reference resulting in a manipulation of the file name string before assigning it to the environment variable as it would happen with delayed expansion already enabled.
The extended version enables next delayed variable expansion inside the loop, does the same as the standard version and restores finally the previous environment before processing the next _a.png file.
The extended version is slower because of the environment variables list copy and the other operations made in background by every execution of SETLOCAL as explained in full details in this answer. The command ENDLOCAL in the loop is required to avoid a stack overflow on processing lots of PNG files.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?

Call $readmemh() on global path of file

I am currently calling $readmemh() from /< WORKSPACE>/< RUN_DIR> on file.txt.
Currently, the function only works for $readmemh(file.txt, memory), i.e., relative path of the file. How can I get it to work for $readmemh(/< WORKSPACE>/< RUN_DIR>/file.txt, memory), i.e., global path?
I have tried to prefix the global path with a '~' but that doesn't work, and just the raw global path also doesn't work.
You should be able to specify an absolute path to a file when you call $readmemh. This code works for me on different simulators:
module tb;
reg [1:0] memory [0:3];
initial begin
$readmemh("/tmp/verilog/file.txt", memory);
for (int i=0; i<4; i++) $display(memory[i]);
end
endmodule
The file /tmp/verilog/file.txt exists for me on my linux OS. I am running my simulation from a different directory from /tmp/verilog.
file.text contains:
0
1
2
3
I would not expect ~ to work since that is a character which has special meaning within a shell.
If you run your simulation as part of a script (which is a common practice), you could avoid the issue you are facing by either copying file.txt into the directory where you run the simulation or by linking to the file. This would be done prior to running the simulation command. In this case, you could then use the simple syntax:
$readmemh("file.txt", memory);

Iexpress - extraction path

I am going to create a self extracting archive but I have got a problem connecting with the default path of the extraction. I would like to extract my files in the same path as the self-extraction archive program. Unfortunately, the files are extracting in another path (C:\Users\computer\AppData\Temp\IXP000.TMP). Is it possible to set the path?
I can't find any direct way to do this with IExpress, but there is a trick we can apply.
But first I'll point out that this is really easy with something like 7-Zip's 7zCon.sfx module (if all you need to do is have the archive extract to the current directory, no questions asked). So you might just want to try something other than IExpress.
Anyhow, the problem with IExpress is that, at the time our install program runs, we're no longer in the directory of the original archive; the current directory is now something like %temp%\IXP000.TMP. So we need to find the directory of our parent process – kind of a pain. Once that's known, we can just xcopy the contents of the archive over to the destination folder.
In VBScript, it would look something like this:
Option Explicit
Dim objShell, objWMI
Dim objCmd, intMyPid, intMyParentPid, objMyParent
Set objShell = CreateObject("WScript.Shell")
Set objWMI = GetObject("winmgmts:root\cimv2")
Set objCmd = objShell.Exec("cmd.exe")
intMyPid = objWMI.Get("Win32_Process.Handle='" & objCmd.ProcessID & "'").ParentProcessId
objCmd.Terminate
intMyParentPid = objWMI.Get("Win32_Process.Handle='" & intMyPid & "'").ParentProcessId
Set objMyParent = objWMI.Get("Win32_Process.Handle='" & intMyParentPid & "'")
objShell.Run "xcopy /y * " & """" & Left(objMyParent.ExecutablePath, _
InStrRev(objMyParent.ExecutablePath, ".exe", -1, vbTextCompare) -1) &_
"\""", 0, True
Your install program would then be, eg: wscript extractToOriginalLocation.vbs //B.
(Inspired somewhat by the answer to this question.)
You could always use a cmd script and echo lines of code into files in specific directories

Lua: Pattern match after a string?

For example, I have arbitrary lines in this format:
directory C:\Program Files\abc\def\
or something like.
log-enabled On
I want to be able to extract the "C:\Program Files\ab\def\" part out of that first line. Likewise, I want to extract the "On" out in the second line. The spaces between the variable and its value are arbitrary. I will know the name of the variable, but I need extract the value based on that.
So basically, I want to remove the first word and a number of arbitrary spaces that follow the first word, and return what remains until the end of the line.
Assuming that, by "word" you mean "a string of characters without spaces", you can do this:
for line in ioFile:lines() do
local variable, value = line:match("(%S+)%s+(.+)")
... --Do stuff with variable and value
end
One alternative with string.match was shown by Nicol Bolas, here is another alternative:
function splitOnFirstSpace(input)
local space = input:find(' ') or (#input + 1)
return input:sub(1, space-1), input:sub(space+1)
end
Usage:
local command, param = splitOnFirstSpace(line)
If no argument is given (splitOnFirstSpace('no-param-here')), then param is the empty string.
I do not believe Lua is packaged with a split() function like Ruby or Perl.
I found that this guy built a lua version of Perl's split function:
http://lua-users.org/lists/lua-l/2011-02/msg01145.html
If you can guarantee that the argument will only have 1 word before it, with that word not containing any spaces, you can read in that line, run the split function on it, and use the return array's 1 index value as what you want.
You could error check that too and make sure you get a 'C:\' within your expected directory, or check to make sure the string is == to 'On' or 'Off'. Because of using the hardcoded index value I really advocate you error check your expected value. Nothing is worse than having an assumed value be wrong.
If an error is detected make sure to log or print it to the screen so you know about it.
This could catch bugs where maybe the string that was input is improper.
Some simple code that models what I suggest you do:
line = "directory C:\Program Files\abc\def/";
contents = line.split(" "); --Split using a space
directory = contents[2]; --Here is your directory
if(errorCheckDir(directory))
--Use directory
end
EDIT:
In response to comments below Lua indeed begins indexing at 1, not 0.
Also, in the case that the directory contains spaces (which is probable) instead of simply using contents[2], I would loop through all of contents except index 1, and piece back together the directory making sure to add the required space between each index that you attach.
So in the case above, contents[2] and contents[3] would have to be stitched back together with a space in between to recover the proper directory.
directory = contents[2].." "..contents[3]
This can be easily automated using a function which has a loop in it and returns back the proper directory:
function recoverDir(contents)
directory = "";
--Recover the directory
for i=2, table.getn(contents) do
directory = directory..contents[i].." ";
end
--strip extra space on the end
dirEnd = string.len(directory);
directory = string.sub(directory,1,dirEnd-1);
return directory; --proper directory
end

How do I make sure that a directory name is quoted in OMake?

I have a relatively complicated suite of OMake files designed for cross-compiling on a specific platform. My source is in C++.
I'm building from Windows and I need to pass to the compiler include directories which have spaces in their names. The way that the includes string which is inserted in the command line to compile files is created is by the line:
public.PREFIXED_INCLUDES = $`(addprefix $(INCLUDES_OPT), $(set $(absname $(INCLUDES))))
At some other point in the OMake files I have a line like:
INCLUDES += $(dir "$(LIBRARY_LOCATION)/Path with spaces/include")
In the middle of the command line this expands to:
-IC:\Library location with spaces\Path with spaces\include
I want it to expand to:
-I"C:\Library location with spaces\Path with spaces\include"
I don't want to change anything but the "INCLUDES += ..." line if possible, although modifying something else in that file is also fine. I don't want to have to do something like change the definition of PREFIXED_INCLUDES, as that's in a suite of OMake files which are part of an SDK which may change beneath me. Is this possible? If so, how can I do it? If not, in what ways can I make sure that includes with spaces in them are quoted by modifying little makefile code (hopefully one line)?
The standard library function quote adds escaped quotes around its argument, so it should do the job:
INCLUDES += $(quote $(dir "$(LIBRARY_LOCATION)/Path with spaces/include"))
If needed, see quote in Omake manual.
In case someone else is having the same problem, I thought I'd share the solution I eventually went with, having never figured out how to surround with quotes. Instead of putting quotes around a name with spaces in it I ended up converting the path to the short (8.3) version. I did this via a a simple JScript file called shorten.js and a one line OMake function.
The script:
// Get Access to the file system.
var FileSystemObject = WScript.CreateObject("Scripting.FileSystemObject");
// Get the short path.
var shortPath = FileSystemObject.GetFolder(WScript.Arguments(0)).ShortPath;
// Output short path.
WScript.StdOut.Write(shortPath);
The function:
ShortDirectoryPath(longPath) =
return $(dir $(shell cscript /Nologo $(dir ./tools/shorten.js) "$(absname $(longPath))"))
So now I just use a line like the following for includes:
INCLUDES += $(ShortDirectoryPath $(dir "$(LIBRARY_LOCATION)/Path with spaces/include"))

Resources