diff --git a/2018/burton1/README.md b/2018/burton1/README.md index 847c21fce..c0b8100f1 100644 --- a/2018/burton1/README.md +++ b/2018/burton1/README.md @@ -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:
Spoiler below; scroll down.
-### 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: diff --git a/2018/burton1/scripthd b/2018/burton1/scripthd index 3133ed75a..de09af623 100755 --- a/2018/burton1/scripthd +++ b/2018/burton1/scripthd @@ -1,6 +1,6 @@ #!/usr/bin/env bash -hd=${HD:=prog} +hd=${HD:=./prog} cat $* | $hd |