C Modifications on [T]csh

I've introduced some modifications on [T]csh.
  • Functions (only for scripts)
  • Read files into a variable
  • Comments on interactive sessions
  • Tell whether stdin is empty
I'd appreciate for any feedback, suggestion or contribution. Contributions of any kind are very welcome.

A key [T]csh developer asked me to create documentation and tests for my functions invention. I'd appreciate if any of you test.

The source code is here. The modifications are separated in branches. Later on, I'll create a branch to include all modifications.

I've a repository for the original Csh, but only functions are there.
 
I think functions based on pipes makes a simpler feature, as well as allows for use in interactive sessions, resembling Bourne-compatible Shells better. Unlike the goto-based version, functions may only be called if they were previously declared (i.e: no forward jumps), making a similar behavior to Bourne-compatible Shells.

This new version relies on a tree derived from variables and aliases. Unlike to aliases and variables, the tree is restrictive. Once a function is declared, may not be redeclared or undeclared.

I was afraid this wouldn't work out for some operations, such as loops and gotos, because pipes cannot rewind. Fortunately, I was wrong, and the fact these operations are possible from interactive sessions, from a terminal, makes the assumption just as wrong, though I'm clueless as to how the Shell handles rewinding on unsupported sources.

The linked commit is outdated. I've discarded fork(2) and introduced a limit on recursion and nesting. Though not strictly followed, POSIX defines pipes to have a buffer size of 512 bytes. Fortunately, a pipe is only blocked when the buffer is full. The idea is to write a byte a time. I'd consider writes as large as PIPE_BUF - 1, for performance, but rendered broken reads.

Along the development, I noticed some operations rendered the Shell exit. longjmp(3) remedied the issue, though I'm afraid doesn't make the best solution.
 
I really don't want to be dismissive of what is clearly a large amount of work that you've put in, but I do need to ask: why not use zsh? To the best of my knowledge, zsh can do everything tcsh can.

I used tcsh for a long time, because it's the default on FreeBSD and FreeBSD was the first Unix system I seriously used. I forgot what exactly the motivation was to look at zsh – I think I was using something very hacky to work around functions for the umpteempth time and just had enough – but overal I'm very happy I (finally) did. I had quite a bespoke tcshrc file, and the only thing I've missed was noclobber=(notempty), but that's since been added to zsh (at my request). And it has so many useful features.
 
for me it is the availability: csh / tcsh is available on all platforms I use, but zsh needs to be installed.
 
I really don't want to be dismissive of what is clearly a large amount of work that you've put in, but I do need to ask: why not use zsh? To the best of my knowledge, zsh can do everything tcsh can.

I used tcsh for a long time, because it's the default on FreeBSD and FreeBSD was the first Unix system I seriously used. I forgot what exactly the motivation was to look at zsh – I think I was using something very hacky to work around functions for the umpteempth time and just had enough – but overal I'm very happy I (finally) did. I had quite a bespoke tcshrc file, and the only thing I've missed was noclobber=(notempty), but that's since been added to zsh (at my request). And it has so many useful features.
For fun and learning, as well as because I'm much of a retro fan. Csh emerged as a superset of the PWB shell. If one have a look at the early Csh and PWB shell codes, they can tell they're similar. William Joy haven't taken these keywords and defines from nowhere. I think he meant to reproduce an open-source version of the PWB shell. The similarity is even clearer by the fact they share issues (e.g: if is troublesome on pipes and redirections). Tcsh and Csh are the only shells to date resembling the forgotten V6 and PWB shells.

While some people have bad views on Csh and Tcsh, for me, was and is enjoyable. It does lack a couple of features found in Bourne-compatible shells, but I found it better than Bourne-compatible shells. There always exist a workaround for a particular goal. E.g: source and goto make a great combination for functions.
Code:
#!/bin/tcsh -f

if ( ! "$?main" ) then
    if ( ! "$?0" ) then
        echo You may only run this script by explicitly calling its file.
        exit -1
    endif
    set -r main = "$0"
    alias function 'source "$main" \!*'
    exit ( { function myfunc 32 } )
endif

if >& /dev/null ( ! { goto "$1" } ) then
    echo Function "$1" not set.
    exit -1
endif
goto "$1" ; shift

myfunc:
    set myrand
    set myrand[1] = "`grep -a -o "\''[\\!-~]'\'" /dev/urandom | head -"\"'$1'\""`"
    set myrand[1] = "$myrand[1]:ags/ //"
    echo "$myrand[1]"
Note how I use goto in the if statement. In sub-shells, goto may seek labels, but may not transfer and resume execution. Some will see and take this as a clear failure; others, like myself, may see it as a feature for telling whether a label exists or not. Makes no sense for transferring and resuming execution, since wouldn't evaluate the exit status. It's only meant for jumps, not for evaluating procedures.

Also, note how I escape characters in a command substitution. I've read some complaints regarding escaping in quotes and command substitution nesting are impossible. Both claims are false. Csh and Tcsh only support escaping off quotes, be single or double. It's an exception for exclamation marks (I don't get why), which are used for history substitution, though. Escaping and command substitution nesting aren't impossible, but are ugly and tricky.

This, along with others' complaints, is the motivation and inspiration behind implementing functions and other features. I've about ten branches implementing new features. I've ideas I haven't put in construction yet, such as file descriptor handling and independent stderr redirection. These, and others, are yet to come, hopefully.

By the way, that "Read files into a variable." idea evolved better, allowing pipes and different variable names, resembling Bourne-compatible shells better.

The "Tell whether stdin is empty." idea was approved.
 
Fixed variable expansion on expressions.

It's known variables expand earlier than expression evaluations. The procedure
Code:
if ( $?a && "$a" != ) echo "$a"
would fail if a isn't set. The correct behavior is to evaluate $?a first, and, if expanded to zero, cancel further processing.

This work remedies the issue by postponing variable expansions during expression evaluations, to the function exp6. The function Dfix1 is used for I/O redirections, and fails if the expansion is, if not quoted, null or larger than one word/vector. I believe this behavior is fine.

This work was also supposed to fix $< expansions on pipes and redirections. I had some success with a fix, but ends up blocking the shell, making it unusable and uninterruptible.
 
Back
Top