Tcl the misunderstood (2006)
antirez.com
antirez.com
I blurted out "Don't those two things contradict each other? You can't have something that's easy and have regular expressions!".
I immediately figured I'd totally blown my chances and discovered afterwards from the recruiter that the one thing he really liked in candidates was to be challenged. Me and my big mouth got me that job.
I'm curious what your thoughts on Tcl the language are today, given your former position at Scriptics.
My first developer job was for a company which used Vignette StoryServer which used Tcl for its primary scripting language. You actually wrote all the Tcl inside a Java based tool (I think there was a way to use an external editor but it was clunky has hell(. We actually did most of our dynamic stuff in Perl via traditional CGI but all the straight CMS work was done inside StoryServer's tools. One of the vagaries of that system was that if you needed to escape an opening bracket "[" you needed to use 2x + 1 (memory is hazy on the exact number) the number of backslashes for every level of brackets you were in. So if you were 3 brackets deep you needed to escape a bracket it would be "\\\\\\\\\["
Otherwise actually I ended up kind of liking Tcl (in some ways I liked the language more then Perl, but the environment we were working in was pretty terrible) and that experience actually translated really well when I play around with objective C for iOS.
As an example, you can do the following in Tcl as the article points out:
set a pu
set b ts
$a$b "Hello World"
Awesome! I'll use this kind of dynamic behavior to let users of the bot have more freedom. Bzzzzt. This was back in the 90s and the idea of 'SQL injection' wasn't as widespread as it is now... Getting around these kind of injections in Tcl requires constant vigilance and sometimes is very confusing.Another interesting issue is the lack of types and how Tcl interprets them. Ask the user for a number and check if its between 1 and 3, and reject if it isn't. Works fine until the user tries the number 0x01, which matches 1 in some places but not in others. Gave me a lot of appreciation for typed languages where an int really is an int.
$ a=ec
$ b=ho
$ $a$b "Hello World"If you want to relegate TCL to short scripts, i guess it's the same as shell scripting, but if you want to use it for more than that this behavior is dangerous.
You're literally passing user input to a language interpreter - what else could you expect? I don't believe there needs to be "understanding" to realise that you just wrote exec($userinput) and that might be bad.
Those attitudes and behaviors spilled over into web development. I know I built more than one perl cgi script that concatenated user-input values together without thought or concern.
It wasn't until very near 2000 that the collective consciousness started to come around the new and different problems web programming brought. It still isn't completely there.
Although, maybe it's just a different mindset. Some developers seem to aim to develop "easiest thing that can work" and others aim for "easiest thing that won't break".
PS: There is a reason that phrase refers to September 1993.
Lincoln Stein, who wrote the CGI.pm (the second widely popular CGI library), also wrote "Web Security: A Step-by-Step Reference Guide", published in 1998, which was two years after he started the "World Wide Web Security FAQ". Here's the 1998 archive of the FAQ: http://web.archive.org/web/19980213181713/http://www13.w3.or...
SQL wasn't that widely used in the early- to mid-1990s. While some places used it, I don't think they went mainstream until Philip Greenspun's "Database Backed Web Sites: the thinking person's guide to web publishing" in 1997. So early SQL injection examples are hard to point out.
But shell injection examples are a dime a dozen. Here's a posting from 1995 outlining the strategy for dealing with CGI input: http://groups.google.com/group/comp.infosystems.www.provider...
> The entire philosophy can be summed up as "Never trust input data." Most security holes are exploited by sending data to the script that the author of the script did not anticipate.
along with examples with the system() command:
Perl:
system("/usr/lib/sendmail -t $foo_address < $input_file");
C:
sprintf(buffer, "/usr/lib/sendmail -t %s < %s", foo_address, input_file);
system(buffer);
C++:
system("/usr/lib/sendmail -t " + FooAddress + " < " + InputFile);
plus variations through Perl's pipe, backtick, exec, and eval commands, and C's popen().It even mentions that using a
open(MAIL, "/usr/lib/sendmail -t")
print MAIL "To: $recipient\n";
doesn't go through the shell but does pass data unchecked to sendmail, so the one should make sure the code cannot be tricked.If you did HTTP in the early 1990s then you very likely used NCSA's httpd. The security page for httpd, from 1998, is still available from archive.org: http://hoohoo.ncsa.uiuc.edu/cgi/security.html .
> A well-behaved client will escape any characters which have special meaning to the Bourne shell in a query string and thus avoid problems with your script misinterpreting the characters. A mischevious client may use special characters to confuse your script and gain unauthorized access.
Replace "Bourne shell" with "SQL" and you have a SQL injection attack.
You wrote: "However, at the time, the collective understanding of security issues was much lower."
I think you underestimate the collective understanding of 15 years ago and overestimate the collective understanding of now. I've seen a bunch of code written in the last few years (mostly for internal use, by non-professional programmers) which still which sends arbitrary user commands to system() and to SQL.
Bad user inputs can cause crashes/catastrophic problems e.g. buying -1 shares from a stock market system.
Therefore computer science teaches to always sanitize inputs. I was taught this in high school in the 80s.
"I know I built more than one perl cgi script that concatenated user-input values together without thought or concern."
You have no excuse.
Back before the mid-2000s it used to take years/decades for ideas like that to bubble up into being "common wisdom" but in those years/decades, there would be hundreds to thousands of people exploiting the ideas, sharing information with other like-minded individuals privately (pre-everyone-has-a-blog), etc.
I call shenanigans.
Like you, I'm personally acquainted with a pretty good cross section of the best known people in vuln research in the '90s, and I was in the room with Peter Zatko and Dave Goldsmith and, at other times, San Mehat, Tim Newsham, and Ivan Arce as they figured out various ways to exploit overflows. This stuff was (weirdly!) new when 8lgm published it in '95. I sincerely doubt that it was old news to anyone when RTM used it in the worm.
If this was in any sense old hat to anyone, where are all the overflow exploits between 1988 and the Lopatic NCSA HTTPD exploit from 1995?
For the record, the standard database interface that Perl uses, DBI, was first released as early alpha on Oct 12, 1994. Its solution to SQL injection is bind parameters. As far as I can tell, that first release had support for bind parameters, but I don't have that version's documentation for that feature.
The first release of the first real database driver was 24th Feb 1995. (Again, early alpha, you couldn't actually fetch data.) From the comments in the change log, bind variables were always a priority, and finally arrived for DBD::Oracle on 22 Aug 1995. From that point on if you used Perl and connected using DBI in the way recommended by the documentation, you were protected from SQL injection attacks.
So there was "no understanding" in only a particular sense of the terms... (I doubt I was the only smart person to conceive of these attacks back in the day)
It so happens that I was getting into Lisp at the same time, and the parallels between them are obvious. You effectively get macros, so it's a pretty expressive language. There's even an object system in Tcl based on CLOS. But ultimately, it feels like a language with lots of good ideas, implemented poorly. I'm not a PL theorist or purist, but "everything is a string" constantly feels like a really poor abstraction.
Tcl is practical for small, quick projects, but you soon run into insane things like the fact that curly brackets are syntactically significant in strings - and comments.
The latter trips me up far more than I would like to admit :(
It's a great example of the nature of Tcl as a language, though. Sometimes I love the simplicity, sometimes consequences of it drive me nuts.
For those who haven't used Tcl: You write comments like this:
# this is a comment
But, in keeping with Tcl's simple and tiny nature, this isn't a special case. "#" is simply a procedure that ignores all arguments supplied to it!While this is extremely elegant, and means you could implement comments in Tcl itself rather than the interpreter, it does mean that you have to balance your curly brackets within comments, which is hugely annoying when you forget.
for {set i 0} {$i<$n} {incr i} {
# Some comment
for {set j 0} {$j<$n} {incr j} {
The script refused to run and eventually I figured out that the comment was somehow impeding execution. I still don't understand why. proc some_proc {a b} {
} # some_proc proc some_proc {a b} {
} ;# some_procAnother common mistake is that curly brackets are VERY significant in [expr]. The author writes statements like "expr $i * $i" in several places, and it makes me cringe for two reasons:
1- This code prints "5" because expr does a double-substitution:
set a 5
set b {$a}
puts [expr $b*1+0]
What's going on here is that $b is evaluated (to the string "$a") before calling expr, then expr itself evaluates its argument: $a*1+0
to 5. This is a problem because $b can be anything--for example, a call to [eval] arbitrary code.2- Calling expr without putting braces around the argument is unfathomably slow. Tcl precompiles procedures as much as possible, and calls to functions requiring dynamic evaluation (like eval or expr without braces) can't be precompiled.
I once solved a huge performance problem by finding and fixing calls to expr made without braces. What was a process that took 40+ hours to finish was reduced to around 45 minutes by this change alone.
edit: formatting
Plus, PHP users seem to be most likely to either go "err, so?" or "YOU DON'T UNDERSTAND THE TRUE GLORY OF PHP!!!" in response to being asked about it, whereas Tcl users generally tend to respond "yeah, you need to learn about that and get used to it" - which tends to significantly alter the next comment made.
(not trying to imply that PHP doesn't have users in the third category, just that the first two are the more visible and it alters the discussion's tone)
Command line programs are now passe in all except a few niches like routers, chip design etc, but IMHO there still isn't a better "/bin/sh++" that you can embed as command line shell interface between your C functions and the user.
Swig + Tcl + C/C++ works like a dream!
And I would further submit that the same language may not be the right choice for both tasks.
Tcl leans further towards being a pleasant CLI interface language, while AFAIK Lua leans further towards being a pleasant plugin development language.
IMHO.
https://bitbucket.org/ul5255/code-snippets/src/61958ee922cee...
I see where you're coming from, but IMHO:
Tcl's ideas make total sense if you understand why the language was invented. The name is the hint: Tool Command Language. It was never intended to be a "proper" programming language, AFAIK.
That is, a "sh++" CLI to be embedded in big programs. The role of the CLI is to be a thin layer between the data structures in the embedding program and the user.
The EIAS approach makes it easy to write commandline scripts. I've seen non-programmers abuse the hell out Tcl but the underlying C function still got the correct arguments.
I find the most unproductive use of my time at work is when writing TCL code. Switching to TDD has helped a lot, but I still find the language maddeningly frustrating.
I hate that this is both valid syntax with wildly different meanings:
set env(VAR) "value"
set $env(VAR) "value"
I hate that TCL error messages have no prefix, so there is no way to grep for them (the 3rd party tools merge STDOUT and STDERR to a single log).I hate that the TCL error message line numbers are RELATIVE TO THE PROCEDURE instead of relative to the file.
I hate that the language is fully interpreted and not compiled, so you can have a syntax error in a code branch that isn't triggered just lying there for months if you don't fully unit test every code path.
I do not understand how anyone could like TCL unless they were using a linting tool to give the robustness of debugging that most other languages have.
Because of our code base and the 3rd party tools I'm not able to lint our code and it is a brutally painful language to debug.
Nowadays Lua has mostly taken TCL's place as an embedded scripting language of choice and I must say I'm a little sorry, there are some things that I miss from TCL.
proc hello {} {
# I'd like to put an unmatched { in my comment
puts "Hello!"
}
This doesn't compile because of the unmatched {.But this does, by adding another unmatched } - in the following string:
proc hello {} {
# I'd like to put an unmatched { in my comment
puts "Hello!}"
}
In other news, Richard P. Gabriel can stop arguing with himself about whether worse is better. In this case, worse is pretty silly!The question is not "How can I put an unmatched curly bracket in my comment?"
The question is "Why are the semantics of the language - including, but not limited to brace matching - so peculiar and unexpected?"
Look at my second code example! The matching brace is in a string, in another command, on the next tine! And it works! And removing either one breaks the code!
I've lost hours to the syntax errors in comments before. Coupled with TCL's bad error messages it's maddening.
#syntax errors in comments are dumb
proc test {a b} {
} # test
% source tmp.tcl
wrong # args: should be "proc name args body"
How long does it take to find that one in a 1000 line file? It doesn't even give a file name or line number.Nagelfar can easily find these types of errors, plus you should be able to define syntax for your own commands (as in your previous comment.)
tclsh nagelfar.tcl engtech.tcl Checking file engtech.tcl Line 1: E Wrong number of arguments (5) to "proc" Argument 4 at line 2 Argument 5 at line 2
Comments are warty in Tcl, you would be better off to understand Tcl's syntax conventions, rather than assuming comments work like other languages. See item #10 at: http://wiki.tcl.tk/10259
F5 load balancers also use TCL for their iRules. With such a simple language you can do almost anything, such as rewriting the request, split test, fend of DDOS attacks.
TCL has quite a history with the network community. The "expect" language is built on top of TCL and was a popular way of automating CLI tasks on routers/switches before better tools came around.
As someone who works in the EDA industry, I can't say that our adoption of a given technology is exactly an endorsement!
I found out that with certain bugs in the TCL script, the device would hard crash (dump to console and restart). On top of that, Cisco's documentation was flat-out wrong sometimes, with incorrect names for functions. Even getting it to timeout a phone call after n seconds wasn't workable; at best you'd get 10-15 seconds resolution.
Sadly his apparent ignorance or lack of concern regarding Tcl's numerous flaws leads me to question his engineering maturity. For instance, nowhere did he warn of how simple typo errors in Tcl's variable names and interpolated strings leads to a layer of engineering hell beyond anything Dante imagined, a place I've been and vowed never to return.
If you really think you need to expose something like Tcl in your product, please think again. These days you would be better off with something like Javascript or Lua. I strongly suspect the Salvatore of 2012 would agree.
</comic book guy>
I find myself building projects where I implement system specific stuff in C with Tcl bindings and then use Tcl to tie the libraries together. It makes for modular software and easy testing, both unit testing through tcltest and testing stuff interactively in development.
It's not The Best Language(tm), but it really ties the room together.
Haha, very well put. A lot of the time, I just want something that lets me tinker interactively with C/++ code (changing variables, calling methods, etc) to make sure it's doing what I want without having to recompile, dive into the debugger, or write a custom command interface. Plus, some things are just far less painful to write in Tcl--namely, anything to do with strings.
The main drawback to it I've found is its insistence on being able to convert everything from an object to a string and then back again. It's all too easy, when using certain functions (I think `lappend' is the one that caused me a lot of bother), to have your objects silently converted into strings. This is one piece of shell insanity that Tcl has kept. It's not a good one.
If your objects aren't really representable as strings - in my case, I'm storing a pointer to some C/C++ object in a TclObj, and relying on Tcl's refcounting to manage the lifetimes - this is what you'd do in something like Lua - then this can leave you a bit stuck. And even if you did support this, this could well leave you with two equivalent objects where you'd rather have one individual one. Tcl should take a leaf out of Lua's book, or Python's, on this matter.
A shame. It's not the only thing I didn't really love, but it was the only thing that really bothered me. I considered changing language, but the Tcl syntax is very convenient. So in the end I just worked around it by reimplementing a bunch of list primitives that didn't turn things into strings so damn often, and used those instead.
For most purposes I've used it, the convenience of the language outweighs its problems, but I've thought about rewriting some of Tcl to fix the minor annoyances like these.
Expect can be used to automate CLI tasks, but for me it is indispensable for testing programs with text-only UIs (I get to write a lot of those).
Back in the 8.3 days, when expect was still a commercial add-on form ActiveState for TCL on Windows, I implemented my own Expect command in TCL based on 'socket'.
If you ever find yourself using Expect with telnet, if you need more performance, you can implement your own Expect using socket which will run at least 10x faster.
The only trick is sending the right TELNET opcodes to get your target device to actually respond with a prompt!
* Perl - https://metacpan.org/release/Expect
* Ruby - http://rubygems.org/gems/expectr
And while this kind of syntax issue might be seen as superficially trivial, consider the popularity of templating languages for generating HTML and other textual content. After a certain point its simpler to use a language where text is the default and code is marked up then it is to use an equivalently powerful library where the code is as usual but all the strings need to be quoted.
The key difference is the lisp-like expressivity of tcl vs the structured nature of python, which makes it harder to do things like override control structures or rewrite functions on the fly.
Everything in tcl is a string leads to some very interesting consequences, some of which are outlined in the article. It's also easier to write disastrously messy code in tcl than in Python, and the freedom leads to rival and poorly documented libraries - it's not an "institutional" language the way Python is.
Syntax is a red herring.
And I myself am surprised at how many photographers have managed to write their own scripts, even non-programmers. I've been thinking about adding support for python as the scripting language, but I can't justify it just for been fashionable.
I remember being annoyed by Tcl back then when I was modifying existing scripts. Although now I can see that it probably isn't the easiest language to learn for a beginner.
I'm sure a lot of people's first, and probably only, experience with Tcl was from Eggdrop bots.
"Well, maybe it works this way.... no. damn. Well, maybe this way... no. damn."
By the time you get it right, you've gone through so many possible models in your head, it's just mush.
I Learned a lot from "Tcl for Web Nerds" and its companion "SQL for web nerds" (all links listed here: http://openacs.org/doc/ ). Back when I started Tcl, I was more like a emacs-lisp script kiddie. Because Tcl was so easy to learn and because OpenACS was such a great MVC style framework, it didn't take long to master the framework itself and dive into the interesting things that were related more with the architecture of a web-app and not just the syntax of a programming language.
The "whereas" poem, as I like to call it, that announced Miguel Sofer's inclusion in the Tcl Core team was real fun to read too - http://code.activestate.com/lists/tcl-core/1983/
Miguel Sofer had described an algorithm for representing hierarchical data in a RDBMS. What he put forward could be thought of as a different kind of a nested set representation. Whereas the nested-set approach involved keeping track of two numeric values (left & right) for each node in our relational records based tree, Miguel Sofer's algorithm would use the ability to lexically sort a base159 encoded string. This way, tree operations could be implemented via sub-string matching and sorting.
His algorithm was implemented in OpenACS because it allowed an efficient implementation of the OpenACS nodes table. Each URI in OpenACS has a node record associated with it and all these nodes are hierarchical records - http://openacs.org/forums/message-view?message_id=16799
The OpenACS nodes system allows the implementation of a fine grained permissions system which enables a child node to auto-magically inherit the permissions of its parent node - this is, if the child node didn't have any specific permissions set on itself - http://openacs.org/doc/permissions-tediously-explained.html
When I was learning ruby I implemented this algorithm using active-record - https://github.com/gautamc/hierarchical_objects The utdt.edu link hosting the pdf that Miguel Sofer created is broken now; I found a copy here: http://www.tetilab.com/roberto/pgsql/postgres-trees.pdf
With a way of doing objects to table mapping, similar to what Rails has.
This back in 1999.
Ah those were the days.
I joined a bit later, remember? ;)
Among other things I remember adding named parameters to tcl procs in like a few hours - a feature some other languages consider a plus.
Is it missing in TCL? Just add it yourself :)
StoryServer was interesting, its one of those things that in at least one way was ahead of its time. It actually generated static HTML files instead of doing straight pulls from a db. It's "dynamic" nature was actually basically a 404 handler where if you hit the URL for a story and there wasn't a cached html doc it would then generate one from the db, write it to the filesystem and then output the page.
After reading this article I wonder how Lua and Tcl compare. It seems like Tcl is more like Lisp than Lua really. Does anyone have experience with any two of those languages and would care to comment on their differences and similarities?
Later working at our studio, I've stumbled on couple of sound tools written in TCL, and lately had to dabble once in a while in the MacPorts land (also TCL).
But then most of the people I've asked they don't know about it...
The core benefit to Tcl is also its main detriment. Tcl takes off in places like OpenOCD because its incredibly powerful simplicity makes it incredibly tiny. You can throw it in pretty much anywhere as an embedded language and it will buy you a TON of functionality. If you want it to be performant, however, you'll wind up writing a lot of piecewise optimizations which will ultimately cause you to lose the size benefit.
So no, it's not a toy language by any means. And yes, its simplicity yields incredible LISP-like power. However, it's not a language I'd _ever_ use to write the core of anything where performance is of concern.
set a pu
set b ts
$a$b "Hello World"
Seems pretty cool.
uplevel is neat as well. The modifications = DSL approach seems interesting, too.However, it seems slightly evil for general use unless really needed.
Playing with uplevel is really the first time I understood stack frames.
Shorter Tcl Complainers #2: Why does Tcl punish me for my insistence on following bad programming practice?
Shorter Tcl Complainers #3: I hate Tcl because of traits it shares with several popular languages which I love!
It was horrible.
Tk was nice, though.
Bane. Of. My. Life.
I never found a use for Tcl myself but I had no problem with it.
There are more legacy Tcl articles at "reddit.com/r/tcl".