Extra characters after close quote in conditional operator - tcl

I am using a conditional operator but getting the error "extra characters after close-quote". fdx is a parameter I am trying to pass through the function argument and I am checking if the passed argument is "fdx" or not and based on that the value to be written will be decided.
proc set_ifg_seville2 {port sgmii speed fdx} {
case $speed {
10 {
erf_wr -s dev_$port mac_ifg_cfg tx_ifg [expr ($fdx == "fdx") ? 5:4]
}
} #Closing procedure

The expression for the ternary operation in your code should be changed as,
erf_wr -s dev_$port mac_ifg_cfg tx_ifg [expr {$fdx == "fdx" ? 5:4}]
Examples :
% set fdx "fdx"
fdx
% set result [expr {$fdx=="fdx" ? "pass" : "fail" }]
pass
% set result [expr {$fdx=="stackoverflow" ? "pass" : "fail" }]
fail
%
Reference : expr

Related

How to check the string value of an argument passed to expect script?

I'm writing a expect script that takes command line arguments. I would like to be able to detect whether the first argument is "--help" and print a Usage string then. Otherwise use the argument as a port number with a specific default (let's say 1818).
I tried this code that fails:
#!/usr/bin/expect
if {[llength $argv] != 1} {
puts "No Port number specified, defaulting to port 1818."
set port 1818
} else {
if {[lindex $argv 0] eq "--help"} {
puts "Usage: testit [--help] [port]"
exit
} else {
set port [lindex $argv 0]
}
}
The error is:
invalid command name "--help"
while executing
"--help"
invoked from within
"if {[llength $argv] != 1} {
puts "No Port number specified, defaulting to port 1818."
set port 1818
} else {
if {[lindex $argv 0] eq "--he..."
Obviously it is trying to interpret the content of the "--help" string while I'm trying to make the script compare the value of argument 0 to "--help".
What is wrong in the above logic or syntax?
I tried using other strings, like "help" instead of "--help" but the outcome is the same.
I'm not that familiar with expect and tcl, but I tried the expression in tclsh and the same thing happens there. So this issue has to do with invalid tcl code. The following tcsh session shows that the syntax if {$variable=="--help"} {...} is OK, but removing the white space in my string comparison attempt above does not solve the problem.
Here's the tcsh session:
% set v1 "--help"
--help
% if [v1 == "--help'] { puts "allo"}
extra characters after close-quote
% if [v1 == "--help"] { puts "allo"}
invalid command name "v1"
% if [$v1 == "--help"] { puts "allo"}
invalid command name "--help"
% if $v1 == help {puts "allo"}
invalid bareword "help"
in expression "--help";
should be "$help" or "{help}" or "help(...)" or ...
% if $v1 == "--help" {puts "allo"}
invalid bareword "help"
in expression "--help";
should be "$help" or "{help}" or "help(...)" or ...
% if {$v1=="--help"} {puts "allo"}
allo
%
The problem is this line:
puts "Usage: testit [--help] [port]"
And the problem with it is that [...] does command substitution in that situation. You need to add a couple of backslashes in there to prevent that, like this:
puts "Usage: testit \[--help] \[port]"
Or you can enclose the string in braces to inhibit all substitutions:
puts {Usage: testit [--help] [port]}
Either will work (and they'll get compiled to exactly the same thing so use whichever you prefer).

Is it possible to pass argument by name in TCL procedure?

I have the following procedure:
proc test {a {b 10} {c 30}} {
puts "$a $b $c"
}
I would like to call the test procedure by passing value to argument a and c and keep the default value of the argument b. In other words, I want to pass arguments value by name.
Is it possible to do it in TCL?
No.
The usual method to do this is:
proc test { args } {
# set up the defaults
set a(-a) {}
set a(-b) 10
set a(-c) 30
set len [llength $args]
# some basic argument checking
if { $len > 6 || $len < 2 || $len % 2 != 0 } {
error "Invalid arguments"
}
array set a $args
...
}
set result [test -a 3 -c 40]

What is the magic behind the TCL command expr with the operator eq

I would like to understand the TCL command expr for string comparison:
I tried the following:
expr {t eq t}
=> 1
expr {tru eq tr}
=> 0
expr {tru eq truee}
=> invalid bareword "truee" ...
expr {a eq a}
=> invalid bareword "a" ...
What is the magic behind the words t, tr, tru? Does Tcl special handling for these strings? I learnt that I had to quote the string if I use expr with eq, but I have some legacy programs which use this form of comparison. I would like to understand it. Thanks.
In Tcl, eq and ne does the string comparison while == does the numerical comparison.
% expr {1 == 1.0}
1
% expr {1 eq 1.0}
0
%
Whenever you are using the eq and if the input non-numeric, then it should be used with double quotes or with a variable reference. You can't use the literal bareword string notation.
For e.g
% expr {"a" eq "b"}; # Strings with double quotes
0
% set i a
a
% expr {$i eq "a"}; # String with variable reference
1
% expr {a eq b}; # Literally using the string as bareword
invalid bareword "a"
in expression "a eq b";
should be "$a" or "{a}" or "a(...)" or ...
%
There is a exception in this rule, where the Tcl boolean comes into play.
In Tcl, a proper boolean value is either a proper integer, with, like in C, zero meaning false and non-zero meaning true, or one of the following:
yes, true, on --> Boolean 1
no, false, off --> Boolean 0
When they are used partially, Tcl tends to match with any of the known items and evaluation happens accordingly.
If you evaluate expr with these special words, then it will return the same.
% expr {true}
true
% expr {false}
false
% expr {t}; # Tcl automatically matches internally and map it to 'true'
t
% expr {fa} ; # Similary, mapped to 'false'
fa
% expr {o} ; # Will throw error as it is conflicting with 'on' & 'off'
invalid bareword "o"
in expression "o";
should be "$o" or "{o}" or "o(...)" or ...
% expr {on}
on
% expr {of}; # Matching 'off' boolean
of
% if true {puts ya}
ya
% if n {puts ya}; # Matching boolean 'no'
% if f {puts ya}; # Matching boolean 'false'
% if false {puts ya}
% if yes {puts ya}
ya
% if y {puts ya}; # Matching boolean 'y'
ya
So, If your input is mapping with boolean, they are still valid, but treated as string only.
Now, lets go back to your original question.
% expr {t eq t}; # Tcl mapped the 'true' boolean and string-wise both are same. So, returned 1
1
% expr {tru eq tr}; # Both mapped 'true'. But, string-wise differs. So, returned 0
% expr {tru eq truee}; # 'tru' mapped to 'true' and 'truee' is unknown. So error
invalid bareword "truee"
% expr {a eq a}; # Both are used literally, so error throw.
invalid bareword "a"
Now, your question from the comments,
% expr {yes == true}; # String-wise, both are different. So, returned 0
0

TCL conditional commands using ternary operator

is it possible to run conditional commands using TCL's ternary operator?
using if statement
if {[string index $cVals $index]} {
incr As
} {
incr Bs
}
I would like to use ternary operator as follows but I get an error
invalid command name "1" while executing "[string index $cVals $index]
? incr As : incr Bs"
[string index $cVals $index] ? incr As : incr Bs
For ternary conditions, we should be using boolean values only, either 0 or 1.
So, you can't use string index directly, since it will return either a char or empty string. You have to compare whether the string is empty or not.
Also, the for the pass/fail criteria of conditions, we have to give literal values. You should use expr to evaluate the expressions.
A basic example can be ,
% expr { 0 < 1 ? "PASS" : "FAIL" }
PASS
% expr { 0 > 1 ? "PASS" : "FAIL" }
FAIL
%
Note that I have used double quotes for the string since it has the alphabets. In case of numerals, it does not have to be double quotes. Tcl will interpret numbers appropriately.
% expr { 0 > 1 ? 100 : -200 }
-200
% expr { 0 < 1 ? 100 : -200 }
100
%
Now, what can be done to your problem ?
If you want to use any commands (such as incr in your case), it should be used within square brackets, to mark that as a command.
% set cVals "Stackoverflow"
Stackoverflow
% set index 5
5
% # Index char found. So, the string is not empty.
% # Thus, the variable 'As' is created and updated with value 1
% # That is why we are getting '1' as a result.
% # Try running multiple times, you will get the updated values of 'As'
% expr {[string index $cVals $index] ne {} ? [incr As] : [incr Bs] }
1
% info exists As
1
% set As
1
% # Note that 'Bs' is not created yet...
% info exists Bs
0
%
% # Changing the index now...
% set index 100
100
% # Since the index is not available, we will get empty string.
% # So, our condition fails, thus, it will be increment 'Bs'
% expr {[string index $cVals $index] ne {} ? [incr As] : [incr Bs] }
1
% info exists Bs
1
%

TCL incr gives wrong value for zero padded integer

I was trying to increment a number which is padded by zeroes to become a six digit number. But strangely any value other than single digit gives a wrong value. like
set x 000660
incr x 1
gives result 433. Also tried with smaller number like 010 but the result is 9. Why is this happening ?
What is the proper way to solve this issue ?
You can try this way too.
proc getIntVal { x } {
# Using 'scan' command to get the literal integer value
set count [ scan $x %d n ]
if { $count!= 1 } {
return -1
}
return $n
}
proc padZero { x } {
# Using 'format' to pad with leading zeroes.
return [ format "%05d" $x ]
}
set val 00060
puts "Initial value : $val"
set tmp [ getIntVal $val ]; # 'tmp' will have the value as '60'
incr tmp;
set val [ padZero $tmp ]; # Padding with zero now
puts "Final value : $val"
Numbers beginning with 0 like
000660
are octet integers. It's equivalent to decimal 432.
The same for 010 (the same as 8 in decimal)
To strip off zeros, try this:
proc stripzeros {value} {
regsub ^0+(.+) $value \\1 retval
return $retval
}
For more information, see Tcl FAQ: How can I use numbers with leading zeroes?.
Yu Hao already explained the problem of octets, and Dinesh added some procs to circumvent the issue. I am suggesting creating one proc that will take on a zero padded integer and return another zero padded integer of the same format and which should work just like incr:
proc incr_pad {val args} {
# Check if increment is given properly
if {[llength $args] == 0} {
set args 1
} elseif {[llength $args] > 1} {
return -code error {wrong # args: should be "incr_pad varName ?increment?"}
}
# Check for integers
if {![regexp {^[0-9]+$} $val]} {
return -code error "expected integer but got \"$val\""
} elseif {![regexp {^[0-9]+$} $args]} {
return -code error "expected integer but got \"$args\""
}
# Get number of digits
set d [regexp -all {[0-9]} $val]
# Trim 0s to the left
set newval [string trimleft $val 0]
# Now use incr
incr newval $args
# Return back the number formatted with the same zero padding as initially given
return [format "%0${d}d" $newval]
}
With this...
% incr_pad 000660 1
000661
% incr_pad 2.5 1
expected integer but got "2.5"
% incr_pad 02 1.5
expected integer but got "1.5"
% incr_pad 010 2
012
% incr_pad 1 2 3
wrong # args: should be "incr_pad varName ?increment?"
% incr_pad 00024
00025
% incr_pad 999
1000
Of course, you can change the name of the function to a shorter one or one which you find more appropriate.