Add 2015/burton/try.sh + format/typo checks
Renamed road.to.obscurity road.to.obscurity.md and make it markdown format. Add try.sh and try.this.txt which the script uses. Format check README.md file, road.to.obscurity.md, spoilers.md. The man pages have not been typo checked yet. The try.sh script might be improved and there is another issue that is pending that prevents completion of this entry (or potentially does) but it is mostly complete.
This commit is contained in:
1 parent
4d0b3a25fc
commit
47874d3820
8 files changed
+252
-179
No files matched your search
@@ -7,3 +7,4 @@ indent.o
|
|||||||
prog
|
prog
|
||||||
prog.alt
|
prog.alt
|
||||||
prog.orig
|
prog.orig
|
||||||
|
rule2
|
||||||
+43
-39
@@ -23,42 +23,40 @@ Type in expressions on standard input.
|
|||||||
### Try:
|
### Try:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
./prog '?0x3343<<2'
|
./try.sh
|
||||||
|
```
|
||||||
|
|
||||||
echo '?0x3343<<2' | ./prog
|
Notice how the script reads in from a file. If you type just:
|
||||||
|
|
||||||
|
```sh
|
||||||
./prog
|
./prog
|
||||||
```
|
```
|
||||||
|
|
||||||
Enter:
|
it will prompt you in the same way. Send ctrl-c to exit.
|
||||||
|
|
||||||
|
|
||||||
|
Finally, if you wish to read the man page:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
a=0xfeedface
|
|
||||||
b=0xcafe
|
|
||||||
a << 16 + b
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
grep '[0-9]' prog.c
|
|
||||||
|
|
||||||
./prog < test.in
|
|
||||||
|
|
||||||
man ./calc.1
|
man ./calc.1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Alternate code:
|
## Alternate code:
|
||||||
|
|
||||||
An alternate version of this entry, `prog.alt.c`, is provided.
|
An alternate version of this entry, [prog.alt.c](prog.alt.c), is provided.
|
||||||
This alternate code is discussed in the "prog.c vs prog.alt.c" section of the
|
This alternate code is discussed in the "prog.c vs prog.alt.c" section of the
|
||||||
author's remarks below.
|
author's remarks below.
|
||||||
|
|
||||||
To compile this alternate version:
|
|
||||||
|
### Alternate build:
|
||||||
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make alt
|
make alt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Alternate use:
|
||||||
|
|
||||||
Use `prog.alt` as you would `prog` above.
|
Use `prog.alt` as you would `prog` above.
|
||||||
|
|
||||||
|
|
||||||
@@ -75,17 +73,19 @@ process.
|
|||||||
|
|
||||||
## Author's remarks:
|
## Author's remarks:
|
||||||
|
|
||||||
|
|
||||||
### calc - an integer expression calculator that outputs in both hex and decimal
|
### calc - an integer expression calculator that outputs in both hex and decimal
|
||||||
|
|
||||||
All useful tools should include sufficient documentation.
|
All useful tools should include sufficient documentation.
|
||||||
See the included man page. ;-)
|
See the included man page. ;-)
|
||||||
|
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
This program is named calc. Onomatopoeia of sorts, in the visual realm. The
|
This program is named `calc`. Onomatopoeia of sorts, in the visual realm. The
|
||||||
formatting was easier: Form Follows Function (although [Hou][1] did this
|
formatting was easier: Form Follows Function (although [Hou][1] did this
|
||||||
better). It was tempting to call it iCalc, with i for integer (and it was
|
better). It was tempting to call it iCalc, with `i` for integer (and it was
|
||||||
written on an iDevice), but sanity prevailed.
|
written on an `iDevice`), but sanity prevailed.
|
||||||
|
|
||||||
Printed in 8 pt Courier, it makes aesthetically pleasing 8.5 x 11 wall art.
|
Printed in 8 pt Courier, it makes aesthetically pleasing 8.5 x 11 wall art.
|
||||||
|
|
||||||
@@ -93,14 +93,15 @@ Printed in 8 pt Courier, it makes aesthetically pleasing 8.5 x 11 wall art.
|
|||||||
|
|
||||||
### prog.c vs prog.alt.c
|
### prog.c vs prog.alt.c
|
||||||
|
|
||||||
Dominik Muth observed a syntax error on `1+1`, `1-1`, `1|1`, and `1^1` when
|
[Dominik Muth](/winners.html#Dominik_Muth) observed a syntax error on `1+1`,
|
||||||
using an ARM based computer, although `1*1` and others worked correctly. This
|
`1-1`, `1|1`, and `1^1` when using an ARM based computer, although `1*1` and
|
||||||
occurs because some platforms by default treat `char` as `unsigned char`. The
|
others worked correctly. This occurs because some platforms by default treat
|
||||||
original code has this weakness, which can be corrected by adding
|
`char` as `unsigned char`. The original code has this weakness, which can be
|
||||||
`-fsigned-char` to the compile line.
|
corrected by adding `-fsigned-char` to the compile line.
|
||||||
|
|
||||||
The modified code is agnostic to `char` signedness.
|
The modified code is agnostic to `char` signedness.
|
||||||
|
|
||||||
|
|
||||||
### Why is this entry obfuscated/interesting?
|
### Why is this entry obfuscated/interesting?
|
||||||
|
|
||||||
Yes, this is another calculator program.
|
Yes, this is another calculator program.
|
||||||
@@ -117,16 +118,17 @@ is only one single, short string, and it does not contain any obfuscated digits.
|
|||||||
And yet the output is in decimal and hexadecimal, and it accepts octal, decimal,
|
And yet the output is in decimal and hexadecimal, and it accepts octal, decimal,
|
||||||
and hex input.
|
and hex input.
|
||||||
|
|
||||||
Flow control is by `for` and `return`. It is if-less, switch-less,
|
Flow control is by `for` and `return`. It is `if`-less, `switch`-less,
|
||||||
while-less, and hopeless. Literally, if not figuratively. ;-)
|
`while`-less, and hopeless. Literally, if not figuratively. ;-)
|
||||||
|
|
||||||
The code size is intentionally 2015, as stated by iocccsize -i, and can be
|
The code size is intentionally 2015, as stated by `iocccsize -i`, and can be
|
||||||
reformatted without changes (as in rule2.c) to exactly 4096 octets.
|
reformatted without changes (as in [rule2.c](rule2.c)) to exactly 4096 octets.
|
||||||
|
|
||||||
Finally, calc is interesting, if for no other reason than it is practically
|
Finally, `calc` is interesting, if for no other reason than it is practically
|
||||||
useful: Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and
|
useful: Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and
|
||||||
logical operations with memory, on the command line, or interactively.
|
logical operations with memory, on the command line, or interactively.
|
||||||
|
|
||||||
|
|
||||||
### But the question is why is the entry obfuscated?
|
### But the question is why is the entry obfuscated?
|
||||||
|
|
||||||
The code follows many industry best-practices:
|
The code follows many industry best-practices:
|
||||||
@@ -161,8 +163,8 @@ and `long`; 10 if `#include` and `#define` are counted.
|
|||||||
- The code helpfully declares `"syntax_error!"` right up front, filling the
|
- The code helpfully declares `"syntax_error!"` right up front, filling the
|
||||||
reader with confidence.
|
reader with confidence.
|
||||||
|
|
||||||
- In spite of its inherent clarity, the code was presented to both *indent*(1)
|
- In spite of its inherent clarity, the code was presented to both `indent(1)`
|
||||||
and *clang-format*(1), after running it through the preprocessor, sustaining its
|
and `clang-format(1)`, after running it through the preprocessor, sustaining its
|
||||||
better qualities.
|
better qualities.
|
||||||
|
|
||||||
- That the code is obfuscated is spelled out two different ways in the source;
|
- That the code is obfuscated is spelled out two different ways in the source;
|
||||||
@@ -170,20 +172,21 @@ it helps to reformat the code to see the second spelling.
|
|||||||
|
|
||||||
- The author is overly fond of the code that handles input. There is no memory
|
- The author is overly fond of the code that handles input. There is no memory
|
||||||
allocation, nor pre-allocation of arrays, so where does the memory come from?
|
allocation, nor pre-allocation of arrays, so where does the memory come from?
|
||||||
The multi-base strtol() code is pretty tricky. No special attempt was made to
|
The multi-base `strtol(3)` code is pretty tricky. No special attempt was made to
|
||||||
replace operations with obscure mathematical relations, as the (lack of) beauty
|
replace operations with obscure mathematical relations, as the (lack of) beauty
|
||||||
is in the parsing -- ask not where is the modulus operation, but how does it
|
is in the parsing -- ask not where is the modulus operation, but how does it
|
||||||
know when to mod -- okay, but where is add and subtract? And really, who
|
know when to mod -- okay, but where is add and subtract? And really, who
|
||||||
doesn't enjoy a setjmp() in the argument list?
|
doesn't enjoy a `setjmp(3)` in the argument list?
|
||||||
|
|
||||||
- Finally, documentation of the source code structure is helpfully provided in
|
- Finally, documentation of the source code structure is helpfully provided in
|
||||||
rot13 format. But isn't it unsportsmanlike to rely upon it? However, even with
|
rot13 format. But isn't it unsportsmanlike to rely upon it? However, even with
|
||||||
this helpful guide, things like reading input lines remain as tricky to read as
|
this helpful guide, things like reading input lines remain as tricky to read as
|
||||||
they are to write.
|
they are to write.
|
||||||
|
|
||||||
|
|
||||||
### Compiler warnings
|
### Compiler warnings
|
||||||
|
|
||||||
Calc is C99 compliant, and is ANSI/C90 compliant except for wanting 64-bit `long
|
`calc` is C99 compliant, and is ANSI/C90 compliant except for wanting 64-bit `long
|
||||||
long`. Changing the two `long long` typedefs to `long`, and compiling with `gcc
|
long`. Changing the two `long long` typedefs to `long`, and compiling with `gcc
|
||||||
-m32` yields a program that operates on 32-bit values, with wrap-around of
|
-m32` yields a program that operates on 32-bit values, with wrap-around of
|
||||||
shifts mod 32:
|
shifts mod 32:
|
||||||
@@ -193,8 +196,8 @@ shifts mod 32:
|
|||||||
65536 0x00010000
|
65536 0x00010000
|
||||||
```
|
```
|
||||||
|
|
||||||
gcc 4.2.1 without arguments compiles cleanly (e.g. cc prog.c -o prog).
|
gcc 4.2.1 without arguments compiles cleanly (e.g. `cc prog.c -o prog`).
|
||||||
This is my workaday compiler. gcc is also clean with -ansi, and -std=c99.
|
This is my workaday compiler. gcc is also clean with `-ansi`, and `-std=c99`.
|
||||||
|
|
||||||
clang 3.6.0 lives up to its namesake, resounding on non-problems.
|
clang 3.6.0 lives up to its namesake, resounding on non-problems.
|
||||||
To stifle these complaints:
|
To stifle these complaints:
|
||||||
@@ -203,19 +206,20 @@ To stifle these complaints:
|
|||||||
-Wno-return-type -Wno-parentheses -Wno-empty-body
|
-Wno-return-type -Wno-parentheses -Wno-empty-body
|
||||||
```
|
```
|
||||||
|
|
||||||
When invoked with -ansi -pedantic, there are two warnings, which can be ignored:
|
When invoked with `-ansi -pedantic`, there are two warnings, which can be ignored:
|
||||||
|
|
||||||
* `ISO C90 does not support 'long long'`
|
* `ISO C90 does not support 'long long'`
|
||||||
|
|
||||||
With -Wall -std=c99, both are quite noisy, and all can be ignored:
|
With `-Wall -std=c99`, both are quite noisy, and all can be ignored:
|
||||||
|
|
||||||
* `array subscript has type 'char' [-Wchar-subscripts]`
|
* `array subscript has type 'char' [-Wchar-subscripts]`
|
||||||
* `value computed is not used [-Wunused-value]`
|
* `value computed is not used [-Wunused-value]`
|
||||||
* `operation on 'j' may be undefined [-Wsequence-point]`
|
* `operation on 'j' may be undefined [-Wsequence-point]`
|
||||||
|
|
||||||
|
|
||||||
### Test Suite:
|
### Test Suite:
|
||||||
|
|
||||||
If the program name begins with an "e", it echoes stdin to stdout.
|
If the program name begins with an `e`, it echoes `stdin` to `stdout`.
|
||||||
This allows for a convenient test suite:
|
This allows for a convenient test suite:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
Development of the obfuscated calculator.
|
# Development of the obfuscated calculator.
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
I wanted to enter the IOCCC....
|
I wanted to enter the IOCCC....
|
||||||
|
|
||||||
@@ -8,83 +7,86 @@ is fierce, but not a closed door.
|
|||||||
|
|
||||||
I needed an interesting program, that had a reasonable chance of being shrunk
|
I needed an interesting program, that had a reasonable chance of being shrunk
|
||||||
within 4096 bytes, and lent itself to obfuscation. I had been revisiting
|
within 4096 bytes, and lent itself to obfuscation. I had been revisiting
|
||||||
utilities I've written over the years. One useful program was calc, a simple
|
utilities I've written over the years. One useful program was `calc`, a simple
|
||||||
recursive descent signed integer expression parser I developed from first
|
recursive descent signed integer expression parser I developed from first
|
||||||
principles as a demonstration of how computers work for my son. That program
|
principles as a demonstration of how computers work for my son. That program
|
||||||
did the basic four operations, but as a programmer, I needed more, so those
|
did the basic four operations, but as a programmer, I needed more, so those
|
||||||
were added over time.
|
were added over time.
|
||||||
|
|
||||||
Calc is a useful, functioning utility. The operators before the obfuscation
|
`calc` is a useful, functioning utility. The operators before the obfuscation
|
||||||
dance began (this is called fore-shadowing):
|
dance began (this is called foreshadowing):
|
||||||
|
|
||||||
() forced precedence
|
```
|
||||||
- ~ ? unary minus, compl, byteswap
|
() forced precedence
|
||||||
* / % & << >> mul, div, mod, bitand, shift: left, right
|
- ~ ? unary minus, compl, byteswap
|
||||||
+ - | ^ add, sub, bitor, xor
|
* / % & << >> mul, div, mod, bitand, shift: left, right
|
||||||
NUM 0-9
|
+ - | ^ add, sub, bitor, xor
|
||||||
|
NUM 0-9
|
||||||
|
```
|
||||||
|
|
||||||
This seemed like a reasonable candidate to experiment with obfuscation; perhaps
|
This seemed like a reasonable candidate to experiment with obfuscation; perhaps
|
||||||
it might yield good results.
|
it might yield good results.
|
||||||
|
|
||||||
Principle of operation
|
|
||||||
--------------------------------------------------
|
## Principle of operation
|
||||||
|
|
||||||
The algorithm was and is rather straightforward:
|
The algorithm was and is rather straightforward:
|
||||||
|
|
||||||
expr() {
|
```
|
||||||
n = term()
|
expr() {
|
||||||
t = token()
|
n = term()
|
||||||
if (t == ADD) return n += expr()
|
t = token()
|
||||||
if (t == SUB) return n -= expr()
|
if (t == ADD) return n += expr()
|
||||||
if (t == BOR) return n |= expr()
|
if (t == SUB) return n -= expr()
|
||||||
if (t == XOR) return n ^= expr()
|
if (t == BOR) return n |= expr()
|
||||||
ungettoken(t)
|
if (t == XOR) return n ^= expr()
|
||||||
return n
|
ungettoken(t)
|
||||||
}
|
return n
|
||||||
term() {
|
}
|
||||||
n = factor()
|
term() {
|
||||||
t = token()
|
n = factor()
|
||||||
if (t == MUL) return n *= term()
|
t = token()
|
||||||
if (t == DIV) return n /= term()
|
if (t == MUL) return n *= term()
|
||||||
if (t == AND) return n &= term()
|
if (t == DIV) return n /= term()
|
||||||
if (t == RSH) return n >> term()
|
if (t == AND) return n &= term()
|
||||||
if (t == LSH) return n << term()
|
if (t == RSH) return n >> term()
|
||||||
ungettoken(t)
|
if (t == LSH) return n << term()
|
||||||
return n
|
ungettoken(t)
|
||||||
}
|
return n
|
||||||
factor() {
|
}
|
||||||
t = token()
|
factor() {
|
||||||
if (t == LPAREN) {
|
t = token()
|
||||||
n = expr()
|
if (t == LPAREN) {
|
||||||
if token() == RPAREN
|
n = expr()
|
||||||
return n
|
if token() == RPAREN
|
||||||
error()
|
return n
|
||||||
}
|
|
||||||
if (t == SUB) return -factor()
|
|
||||||
if (t == CMP) return ~factor()
|
|
||||||
if (t == QUE) return byteswap(factor())
|
|
||||||
if (t == DIG) return strtol()
|
|
||||||
error()
|
error()
|
||||||
}
|
}
|
||||||
|
if (t == SUB) return -factor()
|
||||||
|
if (t == CMP) return ~factor()
|
||||||
|
if (t == QUE) return byteswap(factor())
|
||||||
|
if (t == DIG) return strtol()
|
||||||
|
error()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Obfuscation is a process
|
## Obfuscation is a process
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
Within the above algorithm, there are many details unmentioned, all ripe for
|
Within the above algorithm, there are many details unmentioned, all ripe for
|
||||||
obfuscation:
|
obfuscation:
|
||||||
|
|
||||||
- input
|
- input
|
||||||
- tokenization
|
- tokenization
|
||||||
- atoi input values
|
- `atoi(3)` input values
|
||||||
- sprintf results
|
- `sprintf(3)` results
|
||||||
- error recovery
|
- error recovery
|
||||||
- swapping bytes
|
- swapping bytes
|
||||||
- user interface (command line vs stdin)
|
- user interface (command line vs `stdin`)
|
||||||
|
|
||||||
Uncertain as to how this would progress, I started obfuscating/condensing
|
Uncertain as to how this would progress, I started obfuscating/condensing
|
||||||
everything except the conversion operations, leaving those to sprintf() and
|
everything except the conversion operations, leaving those to `sprintf(3)` and
|
||||||
strtol(). The first version of the obfuscated code used an homage to 1984 anon
|
`strtol(3)`. The first version of the obfuscated code used an homage to 1984 anon
|
||||||
for I/O, and it featured a #define for else's and the types, to help minimize
|
for I/O, and it featured a `#define` for `else`s and the types, to help minimize
|
||||||
the repetition. And of course, most functions were renamed with one or two
|
the repetition. And of course, most functions were renamed with one or two
|
||||||
letters, and most of the variables became single letter globals. Tokenization
|
letters, and most of the variables became single letter globals. Tokenization
|
||||||
was rather easy with a declared array, just initialized with unique values for
|
was rather easy with a declared array, just initialized with unique values for
|
||||||
@@ -93,33 +95,33 @@ each.
|
|||||||
This was when the obfuscation took a turn for the better (worse): it was easy
|
This was when the obfuscation took a turn for the better (worse): it was easy
|
||||||
to hide the operations via a simple transformation. The code now dealt with
|
to hide the operations via a simple transformation. The code now dealt with
|
||||||
small integer values instead of direct ASCII representation of the operations.
|
small integer values instead of direct ASCII representation of the operations.
|
||||||
And the strings where collapsed into a single combined error message and
|
And the strings were collapsed into a single combined error message and
|
||||||
operator tokenization table. This was relatively obscure.
|
operator tokenization table. This was relatively obscure.
|
||||||
|
|
||||||
Next, sprintf() was replaced with a custom formatter. This was rather simple,
|
Next, `sprintf(3)` was replaced with a custom formatter. This was rather simple,
|
||||||
but at least the formatting was not obvious. Formatting and printing were
|
but at least the formatting was not obvious. Formatting and printing were
|
||||||
split early. strtol() was replaced with custom logic, limited to 3 practical
|
split early. `strtol(3)` was replaced with custom logic, limited to 3 practical
|
||||||
bases: octal, decimal, and hex. The last result was remembered in a special
|
bases: octal, decimal, and hex. The last result was remembered in a special
|
||||||
variable, dot.
|
variable, dot.
|
||||||
|
|
||||||
By this time, I had a version which I considered obscure, and shared this early
|
By this time, I had a version which I considered obscure, and shared this early
|
||||||
version with a couple of colleagues for comments. I suggested I could improve
|
version with a couple of colleagues for comments. I suggested I could improve
|
||||||
the obfuscation of a term() and factor(), and would like to have assignable
|
the obfuscation of a `term()` and `factor()`, and would like to have assignable
|
||||||
memories.
|
memories.
|
||||||
|
|
||||||
This code was pretty dense, but inspired by previous IOCCC entries, I thought I
|
This code was pretty dense, but inspired by previous IOCCC entries, I thought I
|
||||||
might get rid of all the else's, which showed the structure of the parser
|
might get rid of all the `else`s, which showed the structure of the parser
|
||||||
rather clearly. Investing in the ternary operator, the code got quite a bit
|
rather clearly. Investing in the ternary operator, the code got quite a bit
|
||||||
more difficult to follow, used only for-loops, and I had to start taking notes
|
more difficult to follow, used only `for` loops, and I had to start taking notes
|
||||||
to follow it. This was a good sign! ;-)
|
to follow it. This was a good sign! ;-)
|
||||||
|
|
||||||
I started testing the code. I wrote a test suite to ensure the changes I made
|
I started testing the code. I wrote a test suite to ensure the changes I made
|
||||||
did not corrupt the processing (oh so easy to do with this obfuscated mess).
|
did not corrupt the processing (oh so easy to do with this obfuscated mess).
|
||||||
Why does -1>>32 yield -1? Oh, yeah: signed arithmetic. Enter Java's unsigned
|
Why does `-1>>32` yield `-1`? Oh, yeah: signed arithmetic. Enter Java's
|
||||||
shift: >>>. Better. But with more features, more to get wrong, and harder to
|
`unsigned` shift: `>>>`. Better. But with more features, more to get wrong,
|
||||||
know which expression is causing the error. The easy solution: if the name
|
and harder to know which expression is causing the error. The easy solution: if
|
||||||
begins with 'e', echo the input. Now I can see the failing expression. Wait,
|
the name begins with `e`, echo the input. Now I can see the failing expression.
|
||||||
what is this test expression supposed to be testing?
|
Wait, what is this test expression supposed to be testing?
|
||||||
|
|
||||||
Wouldn't it be nice to have comments?
|
Wouldn't it be nice to have comments?
|
||||||
|
|
||||||
@@ -130,8 +132,8 @@ statements on a single line (read: semicolon expression terminators). This
|
|||||||
involved quite a bit of rework of the input code, but now allowed very
|
involved quite a bit of rework of the input code, but now allowed very
|
||||||
convenient user interaction.
|
convenient user interaction.
|
||||||
|
|
||||||
Better Obscurity is a laborious process
|
|
||||||
--------------------------------------------------
|
## Better Obscurity is a laborious process
|
||||||
|
|
||||||
Satisfied with the functionality, but unsatisfied with the obscurity, I
|
Satisfied with the functionality, but unsatisfied with the obscurity, I
|
||||||
pondered if I could get rid of all the constants? This had not been done
|
pondered if I could get rid of all the constants? This had not been done
|
||||||
@@ -141,45 +143,46 @@ Obscurity now increased!
|
|||||||
|
|
||||||
But wouldn't it be nice to have assignable memories?
|
But wouldn't it be nice to have assignable memories?
|
||||||
|
|
||||||
Around this time, the formatted code no longer fit with the 2053 limit (due to
|
Around this time, the formatted code no longer fit with the 2053 limit (due to a
|
||||||
a lack of comprehension of the significance of the space-after-brace-and-semi
|
lack of comprehension of the significance of the space-after-brace-and-semi
|
||||||
rule, and a layout program that did not know how to do this). Frustrated, I
|
rule, and a layout program that did not know how to do this). Frustrated, I
|
||||||
wrote another program to reformat this code into rule2.c, which was a single C
|
wrote another program to reformat this code into [rule2.c](rule2.c), which was a
|
||||||
token per line. This solved the formatting problem, but intriguingly
|
single C token per line. This solved the formatting problem, but intriguingly
|
||||||
introduced another. Now I was faced with the 4096 rule as well as the 2053
|
introduced another. Now I was faced with the 4096 rule as well as the 2053
|
||||||
rule. But this was a challenge!
|
rule. But this was a challenge!
|
||||||
|
|
||||||
I wrote a de-obfuscator, and worked on both the unobfuscated version and the
|
I wrote a de-obfuscator, and worked on both the unobfuscated version and the
|
||||||
master code. I worked on the template formatter to make this as automated as
|
master code. I worked on the template formatter to make this as automated as
|
||||||
possible, and worked to achieve both a perfectly presented picture AND the
|
possible, and worked to achieve both a perfectly presented picture AND the
|
||||||
single-line exactly-at-4096 rule2.c.
|
single-line exactly-at-4096 [rule2.c](rule2.c).
|
||||||
|
|
||||||
Finally, "perfection", with assignable memories! Where perfection is defined
|
Finally, "perfection", with assignable memories! Where perfection is defined
|
||||||
as a nicely formatted picture at less than 2053, and exactly 4096
|
as a nicely formatted picture at less than 2053, and exactly 4096
|
||||||
token-per-line rule2.c.
|
token-per-line [rule2.c](rule2.c).
|
||||||
|
|
||||||
Testing Testing Testing
|
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
Cross platform, it works on Mac OSX, fails on Linux. It works if #include
|
## Testing Testing Testing
|
||||||
<stdlib.h>, but it dumps core without it (the only change!)?. 64bits without
|
|
||||||
proper declaration of ints in syscalls yields unpredictable results. 32bit
|
Cross platform, it works on Mac OSX, fails on Linux. It works if `#include
|
||||||
compilation showed problems with byte- swapping in using addition versus
|
<stdlib.h>`, but it dumps core without it (the only change!)?. 64-bits without
|
||||||
bitwise-or. In 32bit land, x<<32 == x, so x<<32 + x>>32 does not yield the
|
proper declaration of `int`s in syscalls yields unpredictable results. 32-bit
|
||||||
desired result, but x<<32 | x>>32 does (with appropriate care and feeding).
|
compilation showed problems with byte-swapping in using addition versus
|
||||||
|
bitwise-or. In 32-bit land, `x<<32 == x`, so `x<<32 + x>>32` does not yield the
|
||||||
|
desired result, but `x<<32 | x>>32` does (with appropriate care and feeding).
|
||||||
And the different compilers on different hosts complained loudly and
|
And the different compilers on different hosts complained loudly and
|
||||||
differently, although all produced correct bits in spite of the noise.
|
differently, although all produced correct bits in spite of the noise.
|
||||||
|
|
||||||
Further, the program did not do UNIX pipelines well, due to the unbuffered
|
Further, the program did not do UNIX pipelines well, due to the unbuffered
|
||||||
homage code (byte-at-a-time I/O), of which write() was also the source of the
|
homage code (byte-at-a-time I/O), of which `write(2)` was also the source of the
|
||||||
portability problem. Replace write() with getchar()/putchar(), and it piped
|
portability problem. Replace `write(2)` with `getchar(3)`/`putchar(3)`, and it
|
||||||
well but lost some obscurity and no longer paid (direct) homage to history.
|
piped well but lost some obscurity and no longer paid (direct) homage to
|
||||||
|
history.
|
||||||
|
|
||||||
Another problem: only one expression at a time was allowed on the command line,
|
Another problem: only one expression at a time was allowed on the command line,
|
||||||
no semis, no comments, and it turns out $2, $3, ... were not handled correctly.
|
no semis, no comments, and it turns out `$2`, `$3`, ... were not handled correctly.
|
||||||
Given the new I/O code, it was now possible to copy the command line arguments
|
Given the new I/O code, it was now possible to copy the command line arguments
|
||||||
into a buffer, and write a small routine to retrieve bytes from this buffer
|
into a buffer, and write a small routine to retrieve bytes from this buffer
|
||||||
like getchar(). Voila! Better obfuscation with much better functionality!
|
like `getchar(3)`. Voila! Better obfuscation with much better functionality!
|
||||||
|
|
||||||
But wouldn't it be nice to show what the current variables hold?
|
But wouldn't it be nice to show what the current variables hold?
|
||||||
|
|
||||||
@@ -187,20 +190,20 @@ Bytes are now hard to find to keep exactly at 4096, and adding functionality to
|
|||||||
obscured code is not easy. Restructuring the logic to gain the bytes
|
obscured code is not easy. Restructuring the logic to gain the bytes
|
||||||
necessary, code to display the variables was added.
|
necessary, code to display the variables was added.
|
||||||
|
|
||||||
Test. Test. Test. Oh, noes! More bugs! Typing "1 + 2 5" yields 3, but no
|
Test. Test. Test. Oh, noes! More bugs! Typing "`1 + 2 5`" yields `3`, but
|
||||||
syntax error. "g = 1" yields a syntax error, and "0xg" does not! ")" causes a
|
no syntax error. "`g = 1`" yields a syntax error, and "`0xg`" does not! "`)`"
|
||||||
segfault! Why didn't I test these before I started obfuscating?! By this
|
causes a segfault! Why didn't I test these before I started obfuscating?! By
|
||||||
point, everything I did to the code perturbed the 4096 bytes, all variables
|
this point, everything I did to the code perturbed the 4096 bytes, all variables
|
||||||
were already a single character, and it was really difficult to follow the
|
were already a single character, and it was really difficult to follow the logic
|
||||||
logic in a debugger (single-stepping an expression did the entire line of code,
|
in a debugger (single-stepping an expression did the entire line of code, even
|
||||||
even if it involved multiple statements -- and it did, everywhere, even in the
|
if it involved multiple statements -- and it did, everywhere, even in the
|
||||||
unobfuscated version; think: a complex chain of ternary expressions is a single
|
unobfuscated version; think: a complex chain of ternary expressions is a single
|
||||||
statement). This involved careful examination of all the expressions within
|
statement). This involved careful examination of all the expressions within the
|
||||||
the logic, trying to find a different way of getting the same result, but
|
logic, trying to find a different way of getting the same result, but typically
|
||||||
typically within fewer bytes than before to allow for the added logic to fix
|
within fewer bytes than before to allow for the added logic to fix the bugs.
|
||||||
the bugs.
|
|
||||||
|
|
||||||
The bugs were fixed, the code re-formatted, and rule2.c was again 4096 bytes.
|
The bugs were fixed, the code re-formatted, and [rule2.c](rule2.c) was again
|
||||||
|
4096 bytes.
|
||||||
|
|
||||||
But wouldn't it be nice for unary plus to work?
|
But wouldn't it be nice for unary plus to work?
|
||||||
|
|
||||||
@@ -211,22 +214,22 @@ this, bytes are scarce to come by.
|
|||||||
|
|
||||||
But wouldn't it be nice for assignments to be silent?
|
But wouldn't it be nice for assignments to be silent?
|
||||||
|
|
||||||
Made them silent (easier said than done!). Why does "1 + 2 5" print a syntax
|
Made them silent (easier said than done!). Why does "`1 + 2 5`" print a syntax
|
||||||
error, but "a = 1 + 2 5" does not? Rewrite(!) the assignment logic -- and this
|
error, but "`a = 1 + 2 5`" does not? Rewrite(!) the assignment logic -- and
|
||||||
requires more bytes!
|
this requires more bytes!
|
||||||
|
|
||||||
Better Obscurity Through Simplification
|
|
||||||
--------------------------------------------------
|
|
||||||
|
|
||||||
Except there were not enough bytes to do this, unless #include <unistd.h> is
|
## Better Obscurity Through Simplification
|
||||||
removed. But then sbrk() must be declared, eating into the savings. If only
|
|
||||||
|
Except there were not enough bytes to do this, unless `#include <unistd.h>` is
|
||||||
|
removed. But then `sbrk()` must be declared, eating into the savings. If only
|
||||||
there were 512 bytes of memory already allocated that could be portably touched
|
there were 512 bytes of memory already allocated that could be portably touched
|
||||||
for free....
|
for free....
|
||||||
|
|
||||||
Light bulb! Memory allocation was excised, and the code became more obscure.
|
Light bulb! Memory allocation was excised, and the code became more obscure.
|
||||||
|
|
||||||
It was working again! It formats beautifully, it compiled cleanly (with
|
It was working again! It formats beautifully, it compiled cleanly (with apropos
|
||||||
apropos -Wno-guards), it remains obscure after cpp and clang-format, it
|
`-Wno-guards`), it remains obscure after `cpp(1)` and `clang-format(1)`, it
|
||||||
measures within the limits and exactly at 4096 (this latter was really getting
|
measures within the limits and exactly at 4096 (this latter was really getting
|
||||||
challenging to maintain). No more bugs known, it is feature complete,
|
challenging to maintain). No more bugs known, it is feature complete,
|
||||||
documentation written.
|
documentation written.
|
||||||
@@ -239,12 +242,12 @@ relatively tight. But changing around the stable initialization code -- by now
|
|||||||
the largest piece of logic -- yielded the necessary bytes, and improved
|
the largest piece of logic -- yielded the necessary bytes, and improved
|
||||||
obscurity by double duty encoding of the operations initialization.
|
obscurity by double duty encoding of the operations initialization.
|
||||||
|
|
||||||
The utility is now truly functional, and feature complete, and obscure.
|
The utility is now truly functional, and feature complete, and obscure. Without
|
||||||
Without src.doc (basic documentation of the code structure), and some commented
|
[src.doc.txt](src.doc.txt) (basic documentation of the code structure), and some
|
||||||
expanded logic stashed away, and the familiarity I have with the code having
|
commented expanded logic stashed away, and the familiarity I have with the code
|
||||||
written and revised it repeatedly, I would not even want to attempt to decipher
|
having written and revised it repeatedly, I would not even want to attempt to
|
||||||
the logic. Many times in the debugging, I was thwarted by my own obfuscations,
|
decipher the logic. Many times in the debugging, I was thwarted by my own
|
||||||
and went down blind paths in trying to fix a bug or add functionality. Now I
|
obfuscations, and went down blind paths in trying to fix a bug or add
|
||||||
think I have a good entry.
|
functionality. Now I think I have a good entry.
|
||||||
|
|
||||||
But wouldn't it be nice to import pre-set definitions from a file?
|
But wouldn't it be nice to import pre-set definitions from a file?
|
||||||
+26
-22
@@ -1,34 +1,38 @@
|
|||||||
### Spoilers
|
### Spoilers
|
||||||
|
|
||||||
|
|
||||||
Calc is a recursive-descent parser. It allocates from the stack deep enough
|
`calc` is a recursive-descent parser. It allocates from the stack deep enough
|
||||||
that all expressions are exhausted before the recursion stack depth reaches the
|
that all expressions are exhausted before the recursion stack depth reaches the
|
||||||
allocations. The memory map and source is documented in src.doc.txt. Tokens
|
allocations. The memory map and source is documented in
|
||||||
are encoded using values 0-20, where 0 is an invalid token, 1 is minus, 2 is
|
[src.doc.txt](src.doc.txt). Tokens are encoded using values 0-20, where 0 is an
|
||||||
space, 3 is plus, etc. The necessary constants are derived at the start of the program,
|
invalid token, 1 is minus, 2 is space, 3 is plus, etc. The necessary constants
|
||||||
using the guarantee that globals are initialized to zero; so a correct null pointer
|
are derived at the start of the program, using the guarantee that globals are
|
||||||
is obtained from an uninitialized global pointer variable, and the integers from
|
initialized to zero; so a correct NULL pointer is obtained from an uninitialized
|
||||||
uninitialized global int variables. If x is a global int, then !x is 1, and the
|
global pointer variable, and the integers from uninitialized global `int`
|
||||||
derivation proceeds from there, using some identities that are confusingly similar
|
variables. If `x` is a global `int`, then `!x` is `1`, and the derivation
|
||||||
and interestingly combined (increasing obfuscation):
|
proceeds from there, using some identities that are confusingly similar and
|
||||||
|
interestingly combined (increasing obfuscation):
|
||||||
|
|
||||||
obc clr
|
```
|
||||||
--- ---
|
obc clr
|
||||||
-~x x+1
|
--- ---
|
||||||
~-x x-1
|
-~x x+1
|
||||||
y-~x y+x+1
|
~-x x-1
|
||||||
|
y-~x y+x+1
|
||||||
|
```
|
||||||
|
|
||||||
The derived constants are a compromise between simple expressions, maximum utility,
|
The derived constants are a compromise between simple expressions, maximum utility,
|
||||||
and minimum number of variables used.
|
and minimum number of variables used.
|
||||||
|
|
||||||
Command line arguments are copied into the upper half of the input buffer,
|
Command line arguments are copied into the upper half of the input buffer,
|
||||||
separated by a space. This allows the code to have a single input parser by using
|
separated by a space. This allows the code to have a single input parser by using
|
||||||
a pointer-to-func to either getchar() if live, or a getchar() simulator.
|
a pointer-to-func to either `getchar(3)` if live, or a `getchar(3)` simulator.
|
||||||
|
|
||||||
A small amount of obfuscation comes from void functions declared int, but missing return.
|
A small amount of obfuscation comes from `void` functions declared `int`, but
|
||||||
This is necessary to allow these functions in ternary ?: expressions and paired with && and ||;
|
missing `return`. This is necessary to allow these functions in ternary `?:`
|
||||||
but the requisite returns would add symbol count at no benefit to obscurity or functionality.
|
expressions and paired with `&&` and `||`; but the requisite returns would add
|
||||||
Function decomposition was mostly chosen to avoid curly braces:
|
symbol count at no benefit to obscurity or functionality. Function
|
||||||
if a for-loop required multiple statements, the body became a new function.
|
decomposition was mostly chosen to avoid curly braces: if a `for`-loop required
|
||||||
Last, but not least: sharing state via global variables is an ancient and honored
|
multiple statements, the body became a new function. Last, but not least:
|
||||||
form of obfuscation, and remarkably effective.
|
sharing state via global variables is an ancient and honored form of
|
||||||
|
obfuscation, and remarkably effective.
|
||||||
@@ -20,7 +20,7 @@ td J jmp_buf
|
|||||||
c *K pointer to cmd line args
|
c *K pointer to cmd line args
|
||||||
td L char
|
td L char
|
||||||
v M(u64) detect errors, save answer in dot, format result in decimal and hex
|
v M(u64) detect errors, save answer in dot, format result in decimal and hex
|
||||||
c *N freebie null pointer
|
c *N freebie NULL pointer
|
||||||
td O unsigned char
|
td O unsigned char
|
||||||
td P void
|
td P void
|
||||||
td Q int
|
td Q int
|
||||||
@@ -37,12 +37,12 @@ i64 a derived: 32
|
|||||||
i64 b derived: 1
|
i64 b derived: 1
|
||||||
i64 _b(i64) term: << >> >>>
|
i64 _b(i64) term: << >> >>>
|
||||||
i64 c derived: 10
|
i64 c derived: 10
|
||||||
i64 _c(c*) convert binary global j to ascii decimal
|
i64 _c(c*) convert binary global j to ASCII decimal
|
||||||
i64 d freebie: 0
|
i64 d freebie: 0
|
||||||
v _d(i64) accumulate converted ascii bytes into j in base passed in
|
v _d(i64) accumulate converted ASCII bytes into j in base passed in
|
||||||
i64 e derived: 256
|
i64 e derived: 256
|
||||||
i64 f derived: 2
|
i64 f derived: 2
|
||||||
i64 _f() convert from ascii to binary: selects convert base (10,8,16), result in j
|
i64 _f() convert from ASCII to binary: selects convert base (10,8,16), result in j
|
||||||
u64 g(u16) byte swap 16-bits
|
u64 g(u16) byte swap 16-bits
|
||||||
u64 h(u32) byte swap 32-bits
|
u64 h(u32) byte swap 32-bits
|
||||||
i64 i(u64) select size and byte swap minimal bytes
|
i64 i(u64) select size and byte swap minimal bytes
|
||||||
@@ -54,7 +54,7 @@ jb l for parser error recovery
|
|||||||
c m() scans input for token, ignores whitespace
|
c m() scans input for token, ignores whitespace
|
||||||
c n() get next input character
|
c n() get next input character
|
||||||
i64 *o registers
|
i64 *o registers
|
||||||
u32 p(c*) writes to stdout until null, newline appended intelligently
|
u32 p(c*) writes to stdout until NUL, newline appended intelligently
|
||||||
v q(**) init
|
v q(**) init
|
||||||
i64 r(c) writes c to stdout
|
i64 r(c) writes c to stdout
|
||||||
i64 s derived: 8
|
i64 s derived: 8
|
||||||
|
|||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# try.sh - demonstrate IOCCC winner 2015/burton
|
||||||
|
#
|
||||||
|
|
||||||
|
# make sure CC is set so that when we do make CC="$CC" it isn't empty. Doing it
|
||||||
|
# this way allows us to have the user specify a different compiler in an easy
|
||||||
|
# way.
|
||||||
|
if [[ -z "$CC" ]]; then
|
||||||
|
CC="cc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
make CC="$CC" all >/dev/null || exit 1
|
||||||
|
|
||||||
|
# clear screen after compilation so that only the entry is shown
|
||||||
|
clear
|
||||||
|
|
||||||
|
echo "$ ./prog '?0x3343<<2'" 1>&2
|
||||||
|
./prog '?0x3343<<2'
|
||||||
|
|
||||||
|
echo "$ echo '?0x3343<<2' | ./prog" 1>&2
|
||||||
|
echo '?0x3343<<2' | ./prog
|
||||||
|
|
||||||
|
|
||||||
|
echo "$ grep '[0-9]' prog.c" 1>&2
|
||||||
|
read -r -n 1 -p "Press any key to continue (notice how there are no digits!): "
|
||||||
|
echo 1>&2
|
||||||
|
grep -c '[0-9]' prog.c
|
||||||
|
|
||||||
|
read -r -n 1 -p "Press any key to run: ./prog < test.in: "
|
||||||
|
echo 1>&2
|
||||||
|
./prog < test.in
|
||||||
|
echo 1>&2
|
||||||
|
|
||||||
|
read -r -n 1 -p "Press any key to show try.this.txt: "
|
||||||
|
echo 1>&2
|
||||||
|
cat try.this.txt
|
||||||
|
echo 1>&2
|
||||||
|
|
||||||
|
read -r -n 1 -p "Press any key to run: ./prog < try.this.txt: "
|
||||||
|
echo 1>&2
|
||||||
|
./prog < try.this.txt
|
||||||
|
|
||||||
|
read -r -n 1 -p "Press any key to show the hex value of 42: "
|
||||||
|
echo 1>&2
|
||||||
|
echo "$ ./prog 42" 1>&2
|
||||||
|
./prog 42
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
a=0xfeedface
|
||||||
|
b=0xcafe
|
||||||
|
a << 16 + b
|
||||||
|
a=0xdeadbeef
|
||||||
|
b=0xcafe
|
||||||
|
a << 16 + b
|
||||||
@@ -3796,6 +3796,14 @@ output.
|
|||||||
# <a name="2015"></a>2015
|
# <a name="2015"></a>2015
|
||||||
|
|
||||||
|
|
||||||
|
## <a name="2015_burton"></a>[2015/endoh3](/2015/burton/prog.c) ([README.md](/2015/burton/README.md]))
|
||||||
|
|
||||||
|
[Cody](#cody) added the [try.sh](/2015/burton/try.sh) script. This involved
|
||||||
|
adding a text file, [try.this.txt](/2015/burton/try.this.txt).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## <a name="2015_dogon"></a>[2015/endoh3](/2015/dogon/prog.c) ([README.md](/2015/dogon/README.md]))
|
## <a name="2015_dogon"></a>[2015/endoh3](/2015/dogon/prog.c) ([README.md](/2015/dogon/README.md]))
|
||||||
|
|
||||||
[Cody](#cody) improved the Makefile so that one can easily change the dimensions
|
[Cody](#cody) improved the Makefile so that one can easily change the dimensions
|
||||||
|
|||||||
Reference in new issue
Block a user