Merge pull request #243 from xexyl/2018-burton1

This commit is contained in:
Landon Curt Noll authored and GitHub committed 2023-04-01 14:15:35 -07:00
commit b4edb73873
2 files changed
+63 -55

No files matched your search

+62 -54
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
hd=${HD:=prog}
hd=${HD:=./prog}
cat $* |
$hd |