How to do 'svn checkout URL' using exec? - tcl

I'm trying to checkout Joe English's tile-extras from Github using svn using a Tcl script.
The required command is
svn checkout https://github.com/jenglish/tile-extras.git path
I have some code that boils down to
exec C:/cygwin64/bin/svn.exe checkout \
https://github.com/jenglish/tile-extras.git C:/cygwin64/tmp/TCL61416]
which fails with the message
couldn't execute "C:\cygwin64\bin\svn.exe checkout
https:\github.com\jenglish\tile-extras.git
C:\cygwin64\tmp\TCL61416": No error
Pasting the command quoted in the error message into a Windows Command Prompt window, I see
svn: E125002: 'https:\github.com\jenglish\tile-extras.git' does not appear to be a URL
So, the problem seems to be that exec converts Tcl-style paths to Unix-style a little over-enthusiastically. Is there any way I can prevent it from converting https://github.com/jenglish... to https:\github.com\jenglish...?
For information, I'm running on Windows 10, with cygwin (setup version 2.889 (64 bit)), svn 1.9.7 and tcl version 8.6.7 (via ActiveTcl 8.6.7.0).
UPDATE
Here is my actual code, which I'm only slightly embarrassed by:
# svn wrapper proposed by Donal Fellows at
# http://stackoverflow/questions/49224268
proc svn {args} {
exec {*}[auto_execok svn] {*}$args <#stdin >#stdout }
# Checkout from github to a temporary repository
set repository https://github.com/jenglish/tile-extras.git set
svnImage [auto_execok svn]
set fil [file tempfile tempfnm] close $fil file delete $tempfnm
set tempRepo [file rootname $tempfnm] puts stdout tempRepo:\ $tempRepo
file mkdir $tempRepo
set svnCmd [list svn checkout $repository [file nativename $tempRepo]]
puts stdout svnCmd:\ $svnCmd eval $svnCmd
# Determine the tile-extras sources
set sourceFiles {keynav.tcl icons.tcl}
set targets [file nativename [file join $tempRepo trunk *.tcl]]
foreach filnam [split [svn ls $targets] \n] {
if {[string match *.tcl $filnam] && [lsearch $sourceFiles $filnam] < 0} {
lappend sourceFiles $filnam
}
}
And here is the result
$ tclsh foo.tcl
tempRepo: C:/cygwin64/tmp/TCL61838
svnCmd: svn checkout
https://github.com/jenglish/tile-extras.git {C:\cygwin64\tmp\TCL61838}
A C:\cygwin64\tmp\TCL61838/branches
A C:\cygwin64\tmp\TCL61838/trunk
A C:\cygwin64\tmp\TCL61838/trunk/README.md
A C:\cygwin64\tmp\TCL61838/trunk/dialog.tcl
A C:\cygwin64\tmp\TCL61838/trunk/doc
A C:\cygwin64\tmp\TCL61838/trunk/doc/dialog.n
A C:\cygwin64\tmp\TCL61838/trunk/doc/keynav.n
A C:\cygwin64\tmp\TCL61838/trunk/icons.tcl
A C:\cygwin64\tmp\TCL61838/trunk/keynav.tcl
A C:\cygwin64\tmp\TCL61838/trunk/license.terms
A C:\cygwin64\tmp\TCL61838/trunk/pkgIndex.tcl
Checked out revision 7.
svn: E155007: '/home/alan/C:\cygwin64\tmp\TCL61838\trunk\*.tcl' is not a working copy
while executing "exec {*}[auto_execok svn] {*}$args <#stdin >#stdout"
(procedure "svn" line 2)
invoked from within "svn ls $targets"
invoked from within "split [svn ls $targets] \n"
invoked from within "foreach filnam [split [svn ls $targets] \n] {
if {[string match *.tcl $filnam] && [lsearch $sourceFiles $filnam] < 0} {
lappend sourceFiles $filn..."
(file "foo.tcl" line 30)
$ ls /tmp/TCL61838/
$
The directory /tmp/TCL61838 is empty, so it seems the svn checkout command didn't complete completely happily. I also see an unpleasant mixture of forward slashes and backslashes being reported by svn.
Thanks in advance for any more help.

Given the error message, it looks like you're getting word boundaries wrong in the code that you've not shown us; while you might believe the code “boils down to” to that exec, it's not actually done that. Also, you've flipped the slashes in the URL which won't work, but that's probably a side-effect of something else.
Alas, I can't quite guess how to fix things for you. There's just too many options. I provide a suggestion below, but there's no telling for sure whether it will work out.
Diagnosis Methodology
The evidence for why I believe that the problem is what I say? This interactive session log (on OSX, but the generic behaviour should be the same):
% exec cat asdkfajh
cat: asdkfajh: No such file or directory
% exec "cat akjsdhfdkj"
couldn't execute "cat akjsdhfdkj": no such file or directory
% exec "cat aksdjhfkdf" skdjfghd
couldn't execute "cat aksdjhfkdf": no such file or directory
The first case shows an error from an external program. The second case shows an error due to no-such-program. The third case shows that arguments are not reported when erroring due to to no-such-program.
This lets me conclude that both C:\cygwin64\bin\svn.exe and its arguments (checkout, https:\github.com\jenglish\tile-extras.git and C:\cygwin64\tmp\TCL61416) were actually passed as a single argument to exec, a fairly common error, and that the problems lie in the preparatory code. You don't show us the preparatory code, so we can't truly fix things but we can make suggestions that address the common problems.
Suggested Approach
A good way to reduce these errors is to write a small wrapper procedure:
proc svn {args} {
# I add in the I/O redirections so svn can ask for a password
exec {*}[auto_execok svn] {*}$args <#stdin >#stdout
}
This would let you write your call to svn as:
svn checkout $theURL [file nativename $theDirectory]
and it would probably Just Work™. Also note that only the directory name goes through file nativename; the URL does not. (We could embed the call to file nativename in the procedure if we were making a specialised procedure to do checkouts, but there's too much variation in the full svn program to let us do that. The caller — you — has to deal with it.)

Related

Invoke command line as in a batch script

I have this batch file with command:
xyz.exe -parm1 path1/a.wav > path2/a.txt
How can this command be executed in TCL script, so that xyz.exe when called with arguments as parm1 and input path path1 of wav file a.wav, produces op that is redirected to a.text file in path2?
I have very basic TCL knowledge, but I couldn't find way of redirection.
sedy
Tcl's exec command knows how to redirect command output into a file. You can just do this:
exec xyz.exe -parm1 path1/a.wav > path2/a.txt
Got it working as below:
set ip_pth path1/a.wav
set op_pth path2/a.txt
set lf [open $op_pth w]
puts $lf [exec xyz.exe -parm1 $ip_pth]
close $lf

Tcl open channel

how does one open a channel that is not a filename in tcl? I've read the docs but I'm not a programmer so I must not understand the open and chan commands because when I try to open a new custom channel
open customchannel1 RDWR
I get errors such as
couldn't execute "customchannel1": no such file or directory
And I'm fully aware that I don't do this correctly:
chan create read customchannel1
invalid command name "customchannel1" ...and... invalid command name "initialize"
All I want is two tcl scripts to be able to talk to each other. I thought I could use channels to do this.
I have, however, successfully created a socket test version of what I want:
proc accept {chan addr port} {
puts "$addr:$port says [gets $chan]"
puts $chan goodbye
close $chan
}
puts -nonewline "master or slave? "
flush stdout
set name [gets stdin]
if {$name eq "master"} {
puts -nonewline "Whats the port? "
flush stdout
set port [gets stdin]
socket -server accept $port
vwait forever
} else {
puts "slave then."
puts -nonewline "Whats the id? "
flush stdout
set myid [gets stdin]
set chan [socket 127.0.0.1 $myid]
puts $chan hello
flush $chan
puts "127.0.0.1:$myid says [gets $chan]"
close $chan
}
In the above example I can run 3 instances of the program: 2 'masters' with different port numbers, and a 'slave' that can talk to either one depending on the port/'id' it chooses.
If I knew how to open a channel with the open command instead of the socket command I could implement the above code without using sockets, or jimmy-rigging the ports to be used as uniq ids, but every example I can find opens files and writes out to files or standard out which you don't have to create in the first place.
Thanks for helping me understand these concepts and how to implement them better!
A channel is simply a high level method for working with already open files or sockets.
From the manual page:
This command provides several operations for reading from, writing to and otherwise manipulating open channels (such as have been created with the open and socket commands, or the default named channels stdin, stdout or stderr which correspond to the process's standard input, output and error streams respectively).
So what you are doing with sockets is correct. You can use the chan command to configure the open socket.
When connecting two scripts together, you might think in terms of using a pipeline. For example, you could run one script as a subordinate process of the other. The master does this:
set pipe [open |[list [info nameofexecutable] $thescriptfile] "r+"]
to get a bidirectional (because r+) pipeline to talk to the child, which can in turn just use stdout and stdin as normal.
Within a process, chan pipe is available, which returns a pair of channels that are connected by an OS anonymous pipe.
When working with these, it really helps if you remember to use fconfigure to turn -buffering to none. Otherwise you can get deadlocks while output to a pipe sits in a buffer somewhere, which you don't want. The ultimate answer to that is to use Expect, which uses Unix ptys instead of pipes, but you can be quite productive provided you remember to tune the buffering.

How to invoke windows 'move' CLI from TCL program

I am using TCL on a Windows 7 machine. And I need to invoke the Windows move command via exec. However I cannot get it to work.
I am aware that TCL has the file rename ability, but for reasons I cannot get into I'm being asked to use the Windows move CLI.
When I use the auto_execok with move, that command returns an empty string. I've also tried with the {*} but it never works.
% info tclversion
8.6
%
% move src dest
invalid command name "move"
%
% [auto_execok move] src dest
ambiguous command name "": after append apply array auto_execok auto_import auto _load auto_load_index auto_qualify binary break case catch cd chan clock close c oncat continue coroutine dict encoding eof error eval exec exit expr fblocked fc onfigure fcopy file fileevent flush for foreach format gets glob global history if incr info interp join lappend lassign lindex linsert list llength lmap load l range lrepeat lreplace lreverse lsearch lset lsort namespace open package pid pr oc puts pwd read regexp regsub rename return scan seek set socket source split s tring subst switch tailcall tclLog tell throw time trace try unknown unload unse t update uplevel upvar variable vwait while yield yieldto zlib
%
I've also looked at the contents of the auto_execok command using the info body auto_execok and it almost looks like they didn't add 'move' to the list of suported commands....
Any suggestions on how to interface with the Windows move command from a TCL program?
Is move built into cmd? You might try:
exec {*}[auto_execok cmd] /c move src dest
I don't have a windows box to test with right now.

TCL Directory Access

My problem is that I have been trying to run the following command on the shell.
set a [file mkdir ./Desktop/New]
set b [open $a/new.rpt w]
Hoping that a would reference the new folder
The interpreter does not return any error so I assumed that I could do this-->
set b [open $a/new.rpt w]
This time an error is displayed saying-->
couldn't open "/new.rpt": permission denied
Could someone please help me out here??
The result (on success) of file mkdir is an empty string. (On failure, you get an exception that you can catch or try … trap ….) To do what you want, put the value in the variable first:
set a ./Desktop/New
file mkdir $a
set b [open $a/new.rpt w]
You might want to do this at some point as well:
set a [file normalize $a]
This gets rid of the reference to the current directory and converts $a to be an absolute filename, which is useful if you're going to do cd /somewhere/else sometime.

how to configure the ethernet link of a pc using a tcl code

I am working on my linux machine with Ubuntu 10.04 and Tcl 8.4. I want to have a Tcl script which can configure the ethernet link for this PC. Can any body share the required code for this?
The easiest way to handle ethernet configuration is to use Tcl's exec command to invoke the commands listed in the relevant Ubuntu help pages, with some adaptation to deal with the fact that Tcl's very good at string manipulation itself. (If you're used to bash scripting, note that Tcl's exec is quite different. In particular, it doesn't replace the Tcl process; it instead waits for the subprocess to finish and returns its standard output.) For example, you might create a command to list the network interfaces like this:
proc listNetworkInterfaces {} {
foreach line [split [exec ifconfig -a] "\n"] {
if {[regexp {^(\w+)} $line -> ifc]} {
lappend result $ifc
}
}
return $result
}
# Demonstrating use...
puts "my network interfaces are [join [lsort [listNetworkInterfaces]] ,]"
And you'd set the IP address, netmask and gateway for a link like this (assuming that you run tclsh yourscript.tcl inside sudo; you need administrative privileges to configure networking):
proc setNetworkAddress {interface address mask gateway} {
exec ifconfig $interface $address netmask $mask
exec route add default gw $gateway $interface
}
There are rather a lot of things you can configure on a network interface, but the general principle for easy coding is to use exec to call a system tool. It's not quite as fast as other, more-complicated solutions (such as writing a C interface that connects Tcl's script layer directly to the underlying system calls), but it is dead simple to get going with.