Merge pull request #1672 from xexyl/burton2015
Add 2015/burton/try.sh + format/typo checks
This commit is contained in:
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