Secho

Aaaiiieeee

Secho was inspired by a post to the Plan 9 mailing list, where, after a round of bashing on the Unix implementation of echo, the discussion went to bashing a hypothetical FSF-style implementation of echo and eventually to:

NAME

echo: echo arguments

SYNOPSIS

echo [-1abCDEeilmNnOqrtuVvwXx] [-B base] [-c cmd] [-d char] [-f file] [-L len] [-o file] [-S voice] [-s char] [args…]

DESCRIPTION

echo outputs its arguments. It takes the following switches:

=-1= One argument per line. =-a= Output in ASCII. The default. =-B base= Output in given base, 2..32. Unless -u also given, base > 10 shows lowercase. =-b= Output in binary. =-C= Don’t echo anything, just print the number of fields. =-c cmd= Run cmd on each argument, replacing $? with the argument itself. =-D= Output in decimal. =-d char= Field delimiter. Default is end of argument. =-E= Print to standard error instead of to standard output. =-e= Allow escape sequences =-f file= Read from file, then from command line (if any). =-i= Read arguments from standard input. =-L len= Line width set to len. Default is to ignore line lengths. =-l= Turn uppercase to lowercase. =-m= Multi-column output. =-N= One field per line, numbering each field. =-n= Suppress newline. =-O= Output in octal. =-o file= Write to file instead of standard output. =-q= “Quiet mode:” redirect output to /dev/null if not to a file. =-r= Print every string that matches each regular expression. Regular expressions cannot contain + or * modifiers. =-S voice= Send to speaker, having the given voice say it. If voice is a null string, use the default voice. =-s char= Separate fields with char, default space. =-t= Separate fields with tabs. =-u= Convert lowercase to uppercase. With -B, output in uppercase letters for base > 10. =-V= Strip non-printing characters. =-v= Make non-printing characters visible. =-w= If -L is given, word wrap instead of character wrap. Otherwise, ignored. =-X= Output in uppercase hexadecimal. =-x= Output in lowercase hexadecimal.

Test for everyone: write this echo in as little code as possible. C or rc is permitted. The rules:

  • for C: either Standard C (no other libraries) or only libc (no other Plan 9 libraries)
  • for rc: only use programs in the core Plan 9 distribution - no programs that I have to get myself
  • match the behavior EXACTLY as above
  • shortest code and fastest run time wins

Winner gets something cool.

Oh ho,” said I, “A challenge!

Source Code


=v0.06= How silly of me; I go to all this trouble to put long options in and I forgot to add --version or --copyright.

=v0.05= It just isn’t the full modern Unix experience unless the program accepts long options.

=v0.04= Time to add a new feature!

The `-R` option prints out the ascii codes for each character,
but to improve readability it does it in roman numerals.

=v0.03= Oh, look, the code wasn’t “portable” to FreeBSD 4.10. Tch, tch, I’ll have to fix that. And while I’m at it, let me fix the feature that stopped -r from working when you specified non-ascii output (I was running the re on the formatted buffer, which may be useful if you’ve memorized ascii->base13 conversions, but is not so useful if you’re a mortal.)

=v0.02= Oh, what the hell, let’s make this code “portable” and add a few more features: 1. Implement -L & -w. 2. Use a STRING(regex) to hold regular expressions, so I can have a potentially unlimited number of them. 3. Have Cprintf() write directly into the destination Cstring instead of using malloced memory and a copy. 4. Use xmalloc() and xrealloc() so that failed memory allocations will properly error out. 5. Use configure.sh to make this code “portable”. 6. use a built-in basename() if a reentrant basename can’t be found. 7. Add a copyright notice. 8. And some comments.

=v0.01= Three hours of coding resulted in v0.01, which implements most of the flags in the challenge. V0.01 doesn’t implement -L, -w, -m, and -S, plus it doesn’t implement -r exactly as specified (I use Henry Spencer’s regex for the regular expressions, and it implements *, so I’d need to hack it to not support that feature and it’s not that important to be feature-complete.)

To make up for losing these features, I've added `-0` (when
taking input from a file or stdin, fields are separated by
nulls), `-9` ([Plan 9][] compatability -- if no arguments
are given, don't print the trailing newline.), and `-?` (spit
out a usage message.)

Trivia


Like every other piece of code I’m writing these days, I’m keeping track of code bloat. You can too!