
Unix V5, OpenBSD, Plan 9, FreeBSD, and GNU Coreutils Implementations of Echo.c - wasd
https://gist.github.com/dchest/1091803
======
jws
Looks like Plan 9 and FreeBSD are the only ones to correctly handle write
errors. The others just silently ignore them. I guess the moral is "Don't rely
on echo to save important data."

Also, the comment…

    
    
        /* This utility may NOT do getopt(3) option parsing. */
    

… which appears in two of them certainly cries out for an explanation. Was it
true in NetBSD? Is it still true? Will people call us names if we do?

Update: bash does not handle errors. zsh might, it checks the result of
fclose() or if stdout, fflush(). I'm not sure that is sufficient after
ignoring all the fwrite() and fputc() errors. Plus it's in an 850 line
function and my monitor is only 20" tall, so I'm not following all of it.

Update2: asveikau is correct. The only ones to attempt error handling miss
short reads. FreeBSD also misses EAGAIN/EINTR handling. Plan9 doesn't need
that, but its man page explicitly states that short writes should be
considered errors by the caller.

I guess it's official. The unix read/write system calls are too complicated to
be used by experts in the operating system. (I've rather thought that for some
time now.)

~~~
asveikau
> Looks like Plan 9 and FreeBSD are the only ones to correctly handle write
> errors.

Both only check for negative return codes. If stdout were piped/redirected to
something where write(2) may return a short byte count (let's say a socket)
neither of them will detect it.

> /* This utility may NOT do getopt(3) option parsing. */

I noticed this too. My guess, totally not backed up by anything but by
instinct, is getopt(3) tried to do too much which doesn't make sense for echo.
For example if it tried to parse "echo -foobar" as anything other than "you
spit out the string '-foobar'" I could see it being problematic.

~~~
johnny22
as mentioned above, it's a POSIX thing.

------
jcoffland
There is nothing wrong with complex code that is correct and readable. I
imagine those who have a problem with the GNU version have not done a lot of
shell scripting and therefore don't see a need for the complexity. The system
V version would really suck in a lot of situations. Of course people love to
criticize because it makes it sound like they know something.

~~~
angersock
Generally no, other than the fact that it is complex--complexity itself is a
sin. Always has been, always will be, though it can be forgiven.

Anyways, what's the issue with the System V version, other than not handling
newlines?

Also, what is the purpose with the Plan 9 loading the thing into a buffer
first, other than perhaps to get the benefit of an all-or-nothing write? I
suspect weirdness with the Plan 9 file handling but am not certain.

~~~
sjolsen
>complexity itself is a sin

Complexity is inherent to any task in some degree. That complexity must be
expressed in full in any program which performs that task correctly, whether
in terms of language/library constructions or the ways in which those are
combined. When the tools with which you implement a task (here, C and the core
Unix API) are structured substantially differently than the high-level
concepts of which that task is composed, there is additional complexity
inherent to mapping those concepts.

In light of that, I don't understand what you mean by, "complexity itself is a
sin," unless you're referring specifically to the strictly unnecessary
complexity introduced by a sub-optimal mapping of high-level semantics to core
constructs. I don't think that's the kind of complexity we're seeing here. I
think we're seeing the complexity inherent to mapping shell-level semantics to
C semantics and the semantics of the Unix API, specifically error-handling
semantics.

I agree with jws that what this code demonstrates is that the core Unix API is
difficult to use for writing precisely correct high-level programs, even ones
as simple as echo. I feel that this is exactly why it's important that _core_
high-level programs like echo encapsulate as much of the complexity of mapping
to C as possible (that is, in the style of GNU rather than SysV): so that one
can easily write reliable programs with high-level tools like the shell
without worrying about that complexity.

------
f2f
what this fine list sorely misses is a true retelling of the "Plan 9 and the
echo" story, as found here, on the Plan 9 mailing lists some millenia ago:

The Plan 9 and the Echo:

[http://9fans.net/archive/2001/09/54](http://9fans.net/archive/2001/09/54)

(for the history buffs: yes, that is dennis ritchie posting in that thread)

~~~
BuildTheRobots
Excellent link; thank you.

It references _The Unix Programming Environment_, Kernighan & Pike (1984), pp.
77-79, "A digression on echo" which I found in PDF and is an immensely
enjoyable and enlightening read :)

[1] [http://books.cat-v.org/computer-science/unix-programming-
env...](http://books.cat-v.org/computer-science/unix-programming-
environment/The.Unix.Programming.Environment.pdf)

------
ori_b
Note that the plan9 version does some extra work so that it can send the
entire buffer in one write() operation. This is because Plan 9 guarantees that
messages smaller than the size of a pipe buffer are sent as an atomic write,
given a sufficiently large recieve buffer on the other end.

This simplifies quite a bit of code that reads and writes commands into ctl
files.

~~~
lallysingh
Why copy it all instead of writev()?

~~~
ori_b
[http://plan9.bell-
labs.com/sources/plan9/sys/src/libc/9sys/w...](http://plan9.bell-
labs.com/sources/plan9/sys/src/libc/9sys/writev.c)

In other words, writev on plan9 isn't a system call -- it does the same
copying that echo.c does. Also, echo.c probably predates writev.c.

------
dmm
Two months ago OpenBSD removed the unused stdlib.h include which was present
since they forked from NetBSD in March 1995.

[http://cvsweb.openbsd.org/cgi-
bin/cvsweb/src/bin/echo/echo.c...](http://cvsweb.openbsd.org/cgi-
bin/cvsweb/src/bin/echo/echo.c.diff?r1=1.7&r2=1.8&f=h)

------
userbinator
Interestingly enough there's another implementation of echo on its wiki page,
which is as succinct and simple as the Unix V5 version:

[http://en.wikipedia.org/wiki/Echo_(command)](http://en.wikipedia.org/wiki/Echo_\(command\))

I wonder if, like a "Hello world" program, some of these simpler utilities
might not even meet the minimum level of creativity to be eligible for
copyright, so complexity was added - true.c and false.c are the other two
examples I can think of; the "true" utility is basically generated by the
default code template provided by many IDEs.

There's also a fun story floating around called "The UNIX and the Echo" about
what it should do without any arguments, or whether or not it should accept
options or escape sequences:

[http://stackoverflow.com/questions/3290683/bloated-echo-
comm...](http://stackoverflow.com/questions/3290683/bloated-echo-command)

The official POSIX definition leaves it implementation-defined, with XSI
forbidding options and requiring escape sequences:

[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ec...](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html)

It's amazing how much variety can be present for functionality that seems so
simple at first glance.

~~~
fysac
What's the minimum amount of creativity required to copyright software? Or is
it really complexity, and not creativity?

------
tbirdz
It would be more worthwhile to show various shells implementations of echo,
because most of the time you are going to be running your shell's builtin echo
instead of /bin/echo.

~~~
pritambaral
Bash:
[http://git.savannah.gnu.org/cgit/bash.git/tree/builtins/echo...](http://git.savannah.gnu.org/cgit/bash.git/tree/builtins/echo.def)

ZSH:
[http://sourceforge.net/p/zsh/code/ci/a4ff8e69570cbdb8e7d5bf1...](http://sourceforge.net/p/zsh/code/ci/a4ff8e69570cbdb8e7d5bf1d5cc4000ffe63e15e/tree/Src/builtin.c#l3732)

~~~
arh68
Thanks for posting the bash source. It looked a _lot_ like the GNU source,
then I noticed at the top of it:

    
    
        /* echo.c, derived from code echo.c in Bash.

------
Zenst
Wonderful to see the various flavours of the same simple command. Though GNU
using a goto for what at quick glance is a non error situation does not sit
well with me, but probably personal taste in code.

ALso surprised how many library's the FreeBSD and GNU flavours want to pull
in.

~~~
stock_toaster
Looking at the history of echo.c in the freebsd tree[1] is pretty interesting
too. Seems before this [2] commit (apparently fixes for performance with many
arguments) it was pretty simple.

[1]:
[https://github.com/freebsd/freebsd/blob/master/bin/echo/echo...](https://github.com/freebsd/freebsd/blob/master/bin/echo/echo.c)

[2]:
[https://github.com/freebsd/freebsd/commit/cbf5708f4336b824b8...](https://github.com/freebsd/freebsd/commit/cbf5708f4336b824b85366d55d42235852183b83?diff=split#diff-
adbe8d3b33ea648fcf834ed6610325c6R114)

~~~
userbinator
To me this feels like a classic case of "premature optimisation" \- by
increasing the complexity of the code quite a bit (even adding dynamic
allocation), they managed to save only 40 _milliseconds_ on a benchmark
involving the echoing of _1000 arguments_.

Using 1000 arguments does not seem at all a typical use case for echo.

~~~
jlarocco
I'm not sure "premature" is the right word. I mean, they benchmarked, and
maybe even profiled, and made it faster.

My guess is that somone had a weird use case for echo where the old version
was slow. He benchmarked, profiled, made some changes, and submitted a patch.
"The source code is too long," is a crappy argument, so might as well use the
faster, longer code.

------
twic
The Rust version, from the coreutils rewrite project:

[https://github.com/uutils/coreutils/blob/master/src/echo/ech...](https://github.com/uutils/coreutils/blob/master/src/echo/echo.rs)

------
oso2k
Here's the 31 line (not sloc) echo in sbase [0].

[0]
[http://git.suckless.org/sbase/tree/echo.c](http://git.suckless.org/sbase/tree/echo.c)

------
muyuu
Hehe I went in fully expecting GNU to have managed making something as simple
as echo.c relatively complex and with inconceivable options. Was not
disappointed. They probably even managed to include a GNUism or two.

~~~
abusque
What do you mean by GNUism?

~~~
pjmlp
Many young generations not touched by UNIX think way GNU tools behave == UNIX,
when they usually offer more features than the real ones.

~~~
masklinn
Then again, so do the BSD tools. And of course the options are often not
compatible between the two e.g. bsd sed's -l enables line buffering, gnu sed's
-l specifies line-wrapping length; bsd sed's -i requires an extension (empty
for no backup) while gnu sed's does not.

POSIX sed supports exactly 3 options (-n, -e and -f), the latest FreeBSD sed
supports 10 (adding -E, -a, -I, -i, -l, -r and -u — the last two being GNUism
compatibility options not necessarily available on older versions, they are
not on my OSX machine) and GNU sed supports 9 short and an additional 4 long
options. And that's not counting the extensions to the sed command set.

~~~
pjmlp
True, which is why it is hard to keep up with multiple UNIX implementations
and to know what is supported on a given box without having to reach for man
first.

~~~
masklinn
I long for a straight POSIX-and-absolutely-nothing-else version of the core
binaries, but haven't seen one so far.

Definitely less convenient, but would make building cross-platform scripts
much easier.

------
vezzy-fnord
Previously on Hacker News:
[https://news.ycombinator.com/item?id=2780661](https://news.ycombinator.com/item?id=2780661)

~~~
agumonkey
I read this as a tv show introduction...

------
_ZeD_
following one of the comment:

    
    
        @astro Yes. An XSI compliant echo actually supports no options at all. See here for how echo is supposed to work: POSIX. 2008 §echo
    

the link goes to
[http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ec...](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html)
; here I read

    
    
         OPERANDS
         
         The following operands shall be supported:
         
         string
             A string to be written to standard output.
             [...]
             The following character sequences shall be recognized on XSI-conformant systems within any of the arguments:
             [...]
             \c
                 Suppress the <newline> that otherwise follows the final argument in the output. All characters following the '\c' in the arguments shall be ignored.
    

wait, what???

------
halayli
For the curious, argv[0] can be null if exec* function didn't pass arguments
to the program.

~~~
augustk
Although GCC issues the warning "null argument where non-null required" if
-Wnonull is present.

------
williammmeyer
If you find this sort of thing interesting and are located in NYC there is a
meetup devoted to it:

[http://www.meetup.com/Classical-Code-Reading-Group-of-New-
Yo...](http://www.meetup.com/Classical-Code-Reading-Group-of-New-
York/events/219962229/)

------
andrewstuart2
Is there a good reason to do `argc--` and then `<=` in the if statement versus
just using `<`?

I know those guys were pretty smart, so I'm legitimately curious.

~~~
zarriak
I think the reason they used it was to make the

i==argc? '\n': ' '

part of the print statement cleaner.

------
pjmlp
A good example of how even simple commands like echo, behave across multiple
UNIX implementations.

------
arbre
What if argc == 1 in the OpenBSD version ? You evaluate *++argv which is not
defined.

~~~
andrewf
argv[argc] is defined to be a null pointer.

------
emmelaich
I don't like 'nflag'. Something like noecho reads much more nicely.

~~~
mackal
Just noecho makes no sense. Passing -n suppresses printing the new line,
noecho sounds like it suppresses all output

~~~
emmelaich
Ah yeah. Braino.

I meant --no-nl or similar.

------
cogburnd02
Is there any particular reason the busybox version is missing?

~~~
cogburnd02
Well, if you're going to downvote me, at least explain why you disagree; don't
hide behind the mouse-click. I was merely asking why another of the freely
available versions of echo was missing from this list; what's wrong with that?

~~~
Jach
Are you honestly expecting the author of this 4 year old commit to respond?
Maybe if you included a link to the other version (even though it's already in
a github comment) your first comment would have been useful and not downvoted,
but as it is it's just pointlessly shared wondering that if serious would be
better done as a private message to the author... as for your second,
complaining about downvotes generally results in more.

~~~
anonbanker
At least we can surmise who was responsible for the downvoting.

~~~
Jach
I downvoted the post complaining about downvotes, but not the first post,
since it was already downvoted. (Few things need to be downvoted/flagged to
oblivion.) Since my attempt at explaining why the first post was downvoted
received upvotes, at least we can surmise it's not totally off the mark to
what others may think.

