Merge pull request #243 from xexyl/2018-burton1
This commit is contained in:
2 files changed
+63
-55
No files matched your search
+62
-54
@@ -24,8 +24,8 @@ make
|
||||
|
||||
### Alternate code:
|
||||
|
||||
An alternate version of this entry, `prog.alt.c`, is provided
|
||||
This alternate code was supposed compile without warnings, however
|
||||
An alternate version of this entry, [prog.alt.c](prog.alt.c), is provided
|
||||
This alternate code was supposed to compile without warnings, however
|
||||
with modern compilers this is no longer the case.
|
||||
|
||||
To compile this alternate version:
|
||||
@@ -42,7 +42,7 @@ One line, one array, one loop and one statement but it prints many bytes. It won
|
||||
|
||||
Feed this anything on stdin.
|
||||
|
||||
The included script may be used as a driver program for this entry.
|
||||
The [included script](scripthd) may be used as a driver program for this entry.
|
||||
It adds several nice-to-have features, and allows salt-to-taste customizability.
|
||||
|
||||
This code has been compiled and tested on:
|
||||
@@ -57,21 +57,25 @@ This code has been compiled and tested on:
|
||||
|
||||
<div style="margin-bottom:60em;margin-top:4em"><strong>Spoiler below; scroll down.</strong></div>
|
||||
|
||||
### SPOILER
|
||||
### SPOILER:
|
||||
|
||||
This started life as a personal challenge: write the shortest hex dump utility in C.
|
||||
|
||||
The self-imposed ground rules in the hunt for shortest:
|
||||
the code has to output correctly,
|
||||
no extraneous newlines, the code must be portable,
|
||||
and without bypassing the solution using execve(2),
|
||||
or functions built upon it (system(3), popen(3), etc). E.g:
|
||||
|
||||
- the code has to output correctly,
|
||||
- no extraneous newlines, the code must be portable,
|
||||
and without bypassing the solution using execve(2), or functions built upon it (system(3), popen(3), etc).
|
||||
|
||||
E.g:
|
||||
|
||||
|
||||
```c
|
||||
main(){system("hd");} // it's turtles all the way down!
|
||||
```
|
||||
|
||||
is not acceptable.
|
||||
|
||||
And, "output correctly" is the output format of this entry. QED.
|
||||
|
||||
Early attempts ended up at a reasonably short 157 characters.
|
||||
@@ -79,6 +83,7 @@ This was too long for a one-liner, so it had to be formatted as something more i
|
||||
This solution is pleasing, but a true one-liner was the hoped-for goal.
|
||||
Here is the 157 character version in it's entirety:
|
||||
|
||||
|
||||
```c
|
||||
char O,o[ 20];L(O){
|
||||
putchar ( O+ 48+39 *(9<
|
||||
@@ -153,67 +158,70 @@ An obfuscated hex dump has been done before:
|
||||
[1986/bright/bright.c](http://ioccc.org/1986/bright/bright.c) is similar in function,
|
||||
but it uses more characters in just setting up its obfuscation than this entire solution!
|
||||
|
||||
### BIGGER SPOILER
|
||||
### BIGGER SPOILER:
|
||||
|
||||
If you are unhappy with the way DEL (character 127) is handled,
|
||||
replace the cryptic expression `4<(4^l>>5)?l:46` with `32>l|l>126?46:l`.
|
||||
Both take the same number of characters, but the former is a bit more interesting.
|
||||
Bonus question: how does the first expression work?
|
||||
|
||||
### BIGGEST SPOILER (and some history)
|
||||
### BIGGEST SPOILER (and some history):
|
||||
|
||||
Technically, `char o[0];` is illegal (SS 6.7.6.2) according to ISO (N1570).
|
||||
And `char o[];` is a conditionally supported feature (SS 6.7.6.2, 6.10.8.3).
|
||||
However, it works in modern C implementations as an extension,
|
||||
including GCC, Clang and TCC, for X86 and ARM,
|
||||
on multiple operating systems (Linux, OSX, FreeBSD), so it is _functionally_ correct.
|
||||
MicroSoft Visual C++ does not allow `char o[]` or `char o[0]`,
|
||||
and implements its close relative `char o[1]`
|
||||
such that writing more than a single char will cause a fault, which is _technically_ correct.
|
||||
Technically, `char o[0];` is illegal (SS 6.7.6.2) according to ISO (N1570). And
|
||||
`char o[];` is a conditionally supported feature (SS 6.7.6.2, 6.10.8.3).
|
||||
However, it works in modern C implementations as an extension, including GCC,
|
||||
Clang and TCC, for X86 and ARM, on multiple operating systems (Linux, OSX,
|
||||
FreeBSD), so it is _functionally_ correct. MicroSoft Visual C++ does not allow
|
||||
`char o[]` or `char o[0]`, and implements its close relative `char o[1]` such
|
||||
that writing more than a single char will cause a fault, which is _technically_
|
||||
correct.
|
||||
|
||||
But how does this work at all, even as an extension?
|
||||
`o[]` allocates one(!) byte in a zero-filled data area (formerly BSS).
|
||||
The UNIX-derived systems this code was run upon all place the
|
||||
storage of `O` and `o` into a zero-filled VM page with nothing following:
|
||||
thus, there are plenty of available and unused zero bytes following `O`, addressed via `o`,
|
||||
and this code only needs 17.
|
||||
This is _undefined_ behavior, but as shown, it is widely available.
|
||||
But how does this work at all, even as an extension? `o[]` allocates one(!)
|
||||
byte in a zero-filled data area (formerly
|
||||
[BSS](https://en.wikipedia.org/wiki/.bss)). The UNIX-derived systems this code
|
||||
was run upon all place the storage of `O` and `o` into a zero-filled VM page
|
||||
with nothing following: thus, there are plenty of available and unused zero
|
||||
bytes following `O`, addressed via `o`, and this code only needs 17. This is
|
||||
_undefined_ behavior, but as shown, it is widely available.
|
||||
|
||||
The [1979 Seventh Edition UNIX][1] (v7) system pre-dates the IOCCC by several years,
|
||||
and the source code to `sh`(1) was a [significant inspiration for this contest][2].
|
||||
There are two C compilers present: `cc` written by Dennis Ritchie, and `pcc` written by Steven Johnson.
|
||||
The nested conditional expressions in 116 and smaller are too complex for `cc` ("Illegal conditional")
|
||||
but `pcc` handles them correctly, and neither compiler accepts `char o[]` or `char o[0]` nor puts the bytes in
|
||||
a zero-filled page (as a paged VM was not yet written for UNIX).
|
||||
but `pcc` handles them correctly, and neither compiler accepts `char o[]` or
|
||||
`char o[0]` and neither put the bytes in a zero-filled page (as a paged VM was
|
||||
not yet written for UNIX).
|
||||
|
||||
[1]: http://minnie.tuhs.org/cgi-bin/utree.pl
|
||||
[2]: http://ioccc.org/all/README
|
||||
[3]: http://www.computerworld.com.au/article/279011/a-z_programming_languages_bourne_shell_sh/?pp=4
|
||||
[3]: https://web.archive.org/web/20180521074522/http://www.computerworld.com.au/article/279011/a-z_programming_languages_bourne_shell_sh/?pp=4
|
||||
|
||||
More significant is that v7 `printf`(3) does not report the number of characters written,
|
||||
and therefore 111 compiles but does not work correctly;
|
||||
113 is the shortest code that compiles and runs correctly in Ancient UNIX.
|
||||
More significant is that v7 `printf`(3) does not report the number of characters
|
||||
written, and therefore 111 compiles but does not work correctly; 113 is the
|
||||
shortest code that compiles and runs correctly in Ancient UNIX.
|
||||
|
||||
> Interesting digression: v7 `printf` (`_doprnt`) was written in PDP-11 assembler.
|
||||
> It pre-dates `varargs.h`, and assembly was the only way to gain access to the variable argument types.
|
||||
> The first edition _The C Programming Language_, 1978, page 71, remarks:
|
||||
Interesting digression: v7 `printf` (`_doprnt`) was written in PDP-11 assembly.
|
||||
It pre-dates `varargs.h`, and assembly was the only way to gain access to the
|
||||
variable argument types. The first edition _The C Programming Language_, 1978,
|
||||
page 71, remarks:
|
||||
|
||||
>> By the way, there is no entirely satisfactory way to write a portable function
|
||||
>> that accepts a variable number of arguments, because there is no portable way
|
||||
>> for the called function to determine how many arguments where actually passed to it in a given call.
|
||||
>> ...
|
||||
>> `printf`, the most common C function with a variable number of arguments,
|
||||
>> ... is also non-portable and must be modified for different environments.
|
||||
> By the way, there is no entirely satisfactory way to write a portable function
|
||||
> that accepts a variable number of arguments, because there is no portable way
|
||||
> for the called function to determine how many arguments where actually passed
|
||||
> to it in a given call.
|
||||
> ...
|
||||
> `printf`, the most common C function with a variable number of arguments,
|
||||
> ... is also non-portable and must be modified for different environments.
|
||||
|
||||
> It is fascinating to explore the roots of C through the history of the operating system
|
||||
> it was birthed within. C is often criticized as a difficult, inscrutable language,
|
||||
> but as this contest demonstrates, this is in parody.
|
||||
> _Shell-gol_ is one of those instances where someone [preferred a different language][3],
|
||||
> and (ab)used the preprocessor to re-mold C into something they found comfortable.
|
||||
> Most of the rest of the system, especially in the kernel,
|
||||
> are models of clarity and efficient expression.
|
||||
It is fascinating to explore the roots of C through the history of the operating
|
||||
system it was birthed within. C is often criticized as a difficult, inscrutable
|
||||
language, but as this contest demonstrates, this is in parody. _Shell-gol_ is
|
||||
one of those instances where someone [preferred a different language][3], and
|
||||
(ab)used the preprocessor to re-mold C into something they found comfortable.
|
||||
Most of the rest of the system, especially in the kernel, are models of clarity
|
||||
and efficient expression.
|
||||
|
||||
### TL;DR
|
||||
### TL;DR:
|
||||
|
||||
So which version is the shortest, portable hex dump?
|
||||
|
||||
@@ -222,16 +230,16 @@ So which version is the shortest, portable hex dump?
|
||||
* 113 works on Ancient UNIX and the portable C compiler.
|
||||
* 119 works on all platforms, including the original Ritchie PDP-11 C compiler.
|
||||
|
||||
### Coda
|
||||
### Coda:
|
||||
|
||||
Clearly, the smallest possible program violates modern best practice.
|
||||
It is not even possible to compile cleanly, but it will compile correctly.
|
||||
|
||||
Included is `prog.alt.c`, a modern C implementation that is -- perhaps -- the smallest,
|
||||
clean-compiling version using `clang` or `gcc` with `-Wall -pedantic`.
|
||||
Note that while this is larger than all but the original attempt,
|
||||
it will *not* work with the original Ritchie PDP-11 C compiler,
|
||||
since the expression within the ternary remains too complex.
|
||||
Included is [prog.alt.c](prog.alt.c), a modern C implementation that is --
|
||||
perhaps -- the smallest, clean-compiling version using `clang` or `gcc` with
|
||||
`-Wall -pedantic`. Note that while this is larger than all but the original
|
||||
attempt, it will *not* work with the original Ritchie PDP-11 C compiler, since
|
||||
the expression within the ternary remains too complex.
|
||||
|
||||
Try:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
hd=${HD:=prog}
|
||||
hd=${HD:=./prog}
|
||||
|
||||
cat $* |
|
||||
$hd |
|
||||
|
||||
Reference in new issue
Block a user