Merge pull request #155 from xexyl/2015-burton

This commit is contained in:
Landon Curt Noll authored and GitHub committed 2023-03-25 12:58:42 -07:00
commit 7b37df5e0d
3 files changed
+339 -175

No files matched your search

+76 -61
View File
@@ -46,13 +46,14 @@ grep '[0-9]' prog.c
./prog < test.in ./prog < test.in
man ./calc.man 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`, is provided.
This alternate code is discussed in the "prog.c vs prog.alt.c" section of the author's comments below. This alternate code is discussed in the "prog.c vs prog.alt.c" section of the
author's comments below.
To compile this alternate version: To compile this alternate version:
@@ -66,10 +67,11 @@ Use `prog.alt` as you would `prog` above.
"Look, Ma, no ~~hands~~ digits!" "Look, Ma, no ~~hands~~ digits!"
The place where some arithmetic operations are done, for example, The place where some arithmetic operations are done, for example, division or
division or modulo, are easy to find. Where are addition and subtraction? modulo, are easy to find. Where are addition and subtraction?
We were impressed with the level of detail the author "blogged" the obfuscation process. We were impressed with the level of detail the author "blogged" the obfuscation
process.
## Author's comments: ## Author's comments:
@@ -80,10 +82,10 @@ See the included man page. ;-)
### Notes ### Notes
This program is named calc. Onomatopoeia of sorts, in the visual realm. This program is named calc. Onomatopoeia of sorts, in the visual realm. The
The formatting was easier: Form Follows Function (although [Hou][1] did this better). formatting was easier: Form Follows Function (although [Hou][1] did this
It was tempting to call it iCalc, with i for integer (and it was written on an iDevice), better). It was tempting to call it iCalc, with i for integer (and it was
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.
@@ -91,12 +93,13 @@ 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 using an Dominik Muth observed a syntax error on `1+1`, `1-1`, `1|1`, and `1^1` when
ARM based computer, although `1*1` and others worked correctly. using an ARM based computer, although `1*1` and others worked correctly. This
This occurs because some platforms by default treat `char` as `unsigned char`. occurs because some platforms by default treat `char` as `unsigned char`. The
The original code has this weakness, which can be corrected by adding `-fsigned-char` original code has this weakness, which can be corrected by adding
to the compile line. `-fsigned-char` to the compile line.
The modified code is agnostic to `char` signed-ness.
The modified code is agnostic to `char` signedness.
### Why is this entry obfuscated/interesting? ### Why is this entry obfuscated/interesting?
@@ -105,21 +108,24 @@ Yes, this is another calculator program.
However, unlike any other calculator source code the author is aware of, However, unlike any other calculator source code the author is aware of,
this one contains no digits. At all. Anywhere. As in this one contains no digits. At all. Anywhere. As in
grep '[0-9]' prog.c || echo no digits! ```sh
grep '[0-9]' prog.c || echo no digits!
```
Nor are the digits simply obfuscated. There are no character constants. Nor are the digits simply obfuscated. There are no character constants. There
There is only one single, short string, and it does not contain any obfuscated digits. 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 hex input. And yet the output is in decimal and hexadecimal, and it accepts octal, decimal,
and hex input.
Flow control is by `for` and `return`. It is If-less. Switch-less. While-less. Hope-less. Flow control is by `for` and `return`. It is if-less, switch-less,
Literally, if not figuratively. ;-) while-less, and hopeless. Literally, if not figuratively. ;-)
The code size is intentionally 2015, as stated by iocccsize -i, The code size is intentionally 2015, as stated by iocccsize -i, and can be
and can be reformatted without changes (as in rule2.c) to exactly 4096 octets. reformatted without changes (as in rule2.c) to exactly 4096 octets.
Finally, calc is interesting, if for no other reason than it is practically useful: Finally, calc is interesting, if for no other reason than it is practically
Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and logical operations with memory, useful: Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and
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?
@@ -130,52 +136,57 @@ The code follows many industry best-practices:
- Numbers and digits have often been used for obfuscation, - Numbers and digits have often been used for obfuscation,
so in a bid for better clarity, this code has no digits at all. so in a bid for better clarity, this code has no digits at all.
- In the spirit of clarity, most of the variables are declared before use, so they - In the spirit of clarity, most of the variables are declared before use, so
are readily visible, with single letter names that don't clutter the screen, they are readily visible, with single letter names that don't clutter the
in a tautological arrangement. screen, in a tautological arrangement.
- More helpful is the use of `typedef`s instead of `#define`, so the types remain useful - More helpful is the use of `typedef`s instead of `#define`, so the types
even in the debugger, and source code beautifiers will not undo careful presentation. remain useful even in the debugger, and source code beautifiers will not undo
careful presentation.
- `#defines` are used here in the best spirit of the preprocessor: to shorten - `#defines` are used here in the best spirit of the preprocessor: to shorten
repetitive constructs, not for obscurity. repetitive constructs, not for obscurity.
- All functions are short (and therefore easy to understand?) and do only one thing, well, .... - All functions are short (and therefore easy to understand?) and do only one
thing, well, ....
- Curly braces invite endless holy wars over readability. This code only uses them - Curly braces invite endless holy wars over readability. This code only uses
for function definitions, thereby greatly improving readability, and reducing arguments them for function definitions, thereby greatly improving readability, and
about said readability. reducing arguments about said readability.
- One of C's many blessings (until recently) is a modicum of keywords, - One of C's many blessings (until recently) is a modicum of keywords, of which
of which only 8 are used: only 8 are used: `for`, `return`, `typedef`, `void`, `char`, `short`, `unsigned`
`for`, `return`, `typedef`, `void`, `char`, `short`, `unsigned` and `long`; and `long`; 10 if `#include` and `#define` are counted.
10 if `#include` and `#define` are counted.
- The code helpfully declares "syntax_error!" right up front, filling the reader with confidence. - The code helpfully declares `"syntax_error!"` right up front, filling the
reader with confidence.
- In spite of its inherent clarity, the code was presented to both *indent*(1) and - In spite of its inherent clarity, the code was presented to both *indent*(1)
*clang-format*(1), after running it through the preprocessor, sustaining its better qualities. and *clang-format*(1), after running it through the preprocessor, sustaining its
better qualities.
- That the code is obfuscated is spelled out two different ways in the source; it helps - That the code is obfuscated is spelled out two different ways in the source;
to reformat the code to see the second spelling. it helps to reformat the code to see the second spelling.
- The author is overly fond of the code that handles input. - The author is overly fond of the code that handles input. There is no memory
There is no memory allocation, nor pre-allocation of arrays, so where does the allocation, nor pre-allocation of arrays, so where does the memory come from?
memory come from? The multi-base strtol() code is pretty trick. The multi-base strtol() code is pretty tricky. No special attempt was made to
No special attempt was made to replace operations with obscure mathematical relations, replace operations with obscure mathematical relations, as the (lack of) beauty
as the (lack of) beauty is in the parsing -- ask not where is the modulus operation, is in the parsing -- ask not where is the modulus operation, but how does it
but how does it know when to mod -- okay, but where is add and subtract? know when to mod -- okay, but where is add and subtract? And really, who
And really, who doesn't enjoy a setjmp() in the argument list? doesn't enjoy a setjmp() in the argument list?
- Finally, documentation of the source code structure is helpfully provided in rot13 format. - Finally, documentation of the source code structure is helpfully provided in
But isn't it unsportsmanlike to rely upon it? However, even with this helpful guide, rot13 format. But isn't it unsportsmanlike to rely upon it? However, even with
things like reading input lines remain as tricky to read as they are to write. this helpful guide, things like reading input lines remain as tricky to read as
they are to write.
### Compiler warnings ### Compiler warnings
Calc is C99 compliant, and is ANSI/C90 compliant except for wanting 64-bit `long long`. Calc is C99 compliant, and is ANSI/C90 compliant except for wanting 64-bit `long
Changing the two `long long` typedefs to `long`, and compiling with `gcc -m32` long`. Changing the two `long long` typedefs to `long`, and compiling with `gcc
yields a program that operates on 32-bit values, with wrap-around of shifts mod 32: -m32` yields a program that operates on 32-bit values, with wrap-around of
shifts mod 32:
1<<48 1<<48
65536 0x00010000 65536 0x00010000
@@ -185,8 +196,10 @@ 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:
-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'`
@@ -202,12 +215,14 @@ With -Wall -std=c99, both are quite noisy, and all can be ignored:
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:
ln -s prog eprog ```sh
PATH=. eprog < test.in | diff - test.out ln -s prog eprog
PATH=. eprog < test.in | diff - test.out
```
### Spoilers ### Spoilers
If you do not want to puzzle out how it works, see spoilers.markdown. If you do not want to puzzle out how it works, see `spoilers.markdown`.
## Copyright: ## Copyright:
+130
View File
@@ -0,0 +1,130 @@
.TH calc 1 "25 March 2023" "calc" "IOCCC 2015"
.SH NAME
calc \- an integer expression calculator that outputs in both hex and decimal
.SH SYNOPSIS
\fBcalc\fP \fI[expr]\fP
.SH DESCRIPTION
.BR calc
presents a simple signed integer expression calculator.
The result is printed on
.BR stdout
as both a decimal value and its hex equivalent.
Results are presented as 32-bit values, displaying as 64-bits only if necessary.
If the program name begins with
.I e
the input is also copied to
.BR stdout .
.BR stdin
is read if there are no arguments.
.PP
Constants may be decimal, octal or hex.
There are 26 registers, initialized to zero, named A-Z; the most recent result is always remembered in the register
.IR .
(dot).
Assignments are silent; the character
.IR $
will print a table of the non-zero registers.
.PP
All input is case-insensitive.
Semicolons and/or newlines separate statements.
Whitespace is ignored, except newlines cannot occur within expressions.
Comments are introduced with
.I #
and extend through the following newline.
.PP
Operator precedence is as found in Go:
OR and XOR are treated as additive, SHIFTing and AND are multiplicative.
This allows a more natural expression without parentheses,
as in
.BR a&0xff\ |\ b<<8 \c
\&.
.PP
Supported operations in decreasing precedence:
.TP
.B ()
forced precedence
.TQ
.B \- + ! ~ ?
unary plus/minus, not, compl, byteswap
.TQ
.B * / % & << >> >>>
mul, div, mod, bitand, shift: left, right, unsigned right
.TQ
.B + \- | ^
add, sub, bitor, xor
.TQ
.B =
assignment ([a-z] = expr)
.SH EXAMPLES
.PP
Display the decimal value of 0x42:
.sp
.RS
.ft B
$ calc 0x42
.br
66 0x00000042
.ft R
.RE
.PP
What is the mask value for 22 bits?
.sp
.RS
.ft B
$ calc '1<<22-1'
.br
4194303 0x003fffff
.ft R
.RE
.PP
What is the integer value of byte-swapped 0x11223344?
.RS
.ft B
$ calc ?0x11223344
.br
1144201745 0x44332211
.ft R
.RE
.PP
Some canned expressions:
.RS
.ft B
cat <<! | ecalc
.br
# multiple assignments
.br
a=32;n=10;c=50;m=100;$
.br
7+7/7+7*7-7 # pedmas
.br
m|a-a*3 # convenient precedence
.br
?(1<<14|1<<9)-1<<4-1<<3
.br
57005<<16+48879
.br
-1 >> 32 # signed shift
.br
-1 >>> 32 # unsigned shift
.br
!
.ft R
.RE
.SH BUGS
An assignment with certain types of syntax error will replace the registers with a partial evaluation:
.sp
.RS
.ft B
calc 'a = 1; a; a = 3 4; a'
.br
1 0x00000001
.br
syntax error
.br
3 0x00000003
.ft R
.RE
.PP
Command line expressions longer than 64 characters will give a syntax error.
.SH SEE ALSO
\<https://golang.org/\>
+133 -114
View File
@@ -3,18 +3,19 @@ Development of the obfuscated calculator.
I wanted to enter the IOCCC.... I wanted to enter the IOCCC....
I think I can write something clever. Possibly even obscure. The competition is fierce, I think I can write something clever. Possibly even obscure. The competition
but not a closed door. is fierce, but not a closed door.
I needed an interesting program, that had a reasonable chance of begin shrunk within 4096 bytes, I needed an interesting program, that had a reasonable chance of being shrunk
and lent itself to obfuscation. I had been revisiting utilities I've written over within 4096 bytes, and lent itself to obfuscation. I had been revisiting
the years. One useful program was calc, a simple recursive descent signed integer utilities I've written over the years. One useful program was calc, a simple
expression parser I developed from first principles as a demonstration of how computers recursive descent signed integer expression parser I developed from first
work for my son. That program did the basic four operations, but as a programmer, principles as a demonstration of how computers work for my son. That program
I needed more, so those were added over time. did the basic four operations, but as a programmer, I needed more, so those
were added over time.
Calc is a useful, functioning utility. The operators before the obfuscation dance began Calc is a useful, functioning utility. The operators before the obfuscation
(this is called fore-shadowing): dance began (this is called fore-shadowing):
() forced precedence () forced precedence
- ~ ? unary minus, compl, byteswap - ~ ? unary minus, compl, byteswap
@@ -22,8 +23,8 @@ Calc is a useful, functioning utility. The operators before the obfuscation dan
+ - | ^ add, sub, bitor, xor + - | ^ add, sub, bitor, xor
NUM 0-9 NUM 0-9
This seemed like a reasonable candidate to experiment with obfuscation; perhaps it might This seemed like a reasonable candidate to experiment with obfuscation; perhaps
yield good results. it might yield good results.
Principle of operation Principle of operation
-------------------------------------------------- --------------------------------------------------
@@ -69,7 +70,8 @@ The algorithm was and is rather straightforward:
Obfuscation is a process Obfuscation is a process
-------------------------------------------------- --------------------------------------------------
Within the above algorithm, there are many details unmentioned, all ripe for obfuscation: Within the above algorithm, there are many details unmentioned, all ripe for
obfuscation:
- input - input
- tokenization - tokenization
@@ -79,153 +81,170 @@ Within the above algorithm, there are many details unmentioned, all ripe for obf
- 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 everything Uncertain as to how this would progress, I started obfuscating/condensing
except the conversion operations, leaving those to sprintf() and strtol(). The first everything except the conversion operations, leaving those to sprintf() and
version of the obfuscated code used an homage to 1984 anon for I/O, and it featured a strtol(). The first version of the obfuscated code used an homage to 1984 anon
#define for else's and the types, to help minimize the repetition. And of course, most for I/O, and it featured a #define for else's and the types, to help minimize
functions were renamed with one or two letters, and most of the variables became single the repetition. And of course, most functions were renamed with one or two
letter globals. Tokenization was rather easy with a declared array, just initialized with letters, and most of the variables became single letter globals. Tokenization
unique values for each. was rather easy with a declared array, just initialized with unique values for
each.
This was when the obfuscation took a turn for the better (worse): it was easy to hide the This was when the obfuscation took a turn for the better (worse): it was easy
operations via a simple transformation. The code now dealt with small integer values to hide the operations via a simple transformation. The code now dealt with
instead of direct ASCII representation of the operations. And the strings where collapsed small integer values instead of direct ASCII representation of the operations.
into a single combined error message and operator tokenization table. This was relatively And the strings where collapsed into a single combined error message and
obscure. operator tokenization table. This was relatively obscure.
Next, sprintf() was replaced with a custom formatter. This was rather simple, but at least Next, sprintf() was replaced with a custom formatter. This was rather simple,
the formatting was not obvious. Formatting and printing were split early. strtol() was but at least the formatting was not obvious. Formatting and printing were
replaced with custom logic, limited to 3 practical bases: octal, decimal, and hex. The split early. strtol() was replaced with custom logic, limited to 3 practical
last result was remembered in a special variable, dot. bases: octal, decimal, and hex. The last result was remembered in a special
variable, dot.
By this time, I had a version which I considered obscure, and shared this early version By this time, I had a version which I considered obscure, and shared this early
with a couple of colleagues for comments. I suggested I could improve the obfuscation of version with a couple of colleagues for comments. I suggested I could improve
a term() and factor(), and would like to have assignable memories. the obfuscation of a term() and factor(), and would like to have assignable
memories.
This code was pretty dense, but inspired by previous IOCCC entries, I thought I might get This code was pretty dense, but inspired by previous IOCCC entries, I thought I
rid of all the else's, which showed the structure of the parser rather clearly. Investing might get rid of all the else's, which showed the structure of the parser
in the ternary operator, the code got quite a bit more difficult to follow, used only rather clearly. Investing in the ternary operator, the code got quite a bit
for-loops, and I had to start taking notes to follow it. This was a good sign! ;-) more difficult to follow, used only for-loops, and I had to start taking notes
to follow it. This was a good sign! ;-)
I started testing the code. I wrote a test suite to ensure the changes I made did not I started testing the code. I wrote a test suite to ensure the changes I made
corrupt the processing (oh so easy to do with this obfuscated mess). Why does -1>>32 did not corrupt the processing (oh so easy to do with this obfuscated mess).
yield -1? Oh, yeah: signed arithmetic. Enter Java's unsigned shift: >>>. Better. But Why does -1>>32 yield -1? Oh, yeah: signed arithmetic. Enter Java's unsigned
with more features, more to get wrong, and harder to know which expression is causing the shift: >>>. Better. But with more features, more to get wrong, and harder to
error. The easy solution: if the name begins with 'e', echo the input. Now I can see the know which expression is causing the error. The easy solution: if the name
failing expression. Wait, what is this test expression supposed to be testing? begins with 'e', echo the input. Now I can see the failing expression. 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?
Adding new functionality to an obscure program is ... going about it backwards. Get the Adding new functionality to an obscure program is ... going about it backwards.
functionality correct and complete, THEN obscure. Sounds obvious, but that did not Get the functionality correct and complete, THEN obscure. Sounds obvious, but
happen. I got comments working, then desired to have multiple statements on a single that did not happen. I got comments working, then desired to have multiple
line (read: semicolon expression terminators). This involved quite a bit of rework of statements on a single line (read: semicolon expression terminators). This
the input code, but now allowed very convenient user interaction. involved quite a bit of rework of the input code, but now allowed very
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 pondered if I Satisfied with the functionality, but unsatisfied with the obscurity, I
could get rid of all the constants? This had not been done before (to my knowledge), and pondered if I could get rid of all the constants? This had not been done
I was able to work out the how-tos and the compromises. Now I can no longer declare my before (to my knowledge), and I was able to work out the how-tos and the
arrays, I have to allocate them. Obscurity now increased! compromises. Now I can no longer declare my arrays, I have to allocate them.
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 a lack of Around this time, the formatted code no longer fit with the 2053 limit (due to
comprehension of the significance of the space-after-brace-and-semi rule, and a layout a lack of comprehension of the significance of the space-after-brace-and-semi
program that did not know how to do this). Frustrated, I wrote another program to rule, and a layout program that did not know how to do this). Frustrated, I
reformat this code into rule2.c, which was a single C token per line. This solved the wrote another program to reformat this code into rule2.c, which was a single C
formatting problem, but intriguingly introduced another. Now I was faced with the 4096 token per line. This solved the formatting problem, but intriguingly
rule as well as the 2053 rule. But this was a challenge! introduced another. Now I was faced with the 4096 rule as well as the 2053
rule. But this was a challenge!
I wrote a de-obfuscator, and worked on both the unobfuscated version and the master code. I wrote a de-obfuscator, and worked on both the unobfuscated version and the
I worked on the template formatter to make this as automated as possible, and worked to master code. I worked on the template formatter to make this as automated as
achieve both a perfectly presented picture AND the single-line exactly-at-4096 rule2.c. possible, and worked to achieve both a perfectly presented picture AND the
single-line exactly-at-4096 rule2.c.
Finally, "perfection", with assignable memories! Where perfection is defined as a nicely Finally, "perfection", with assignable memories! Where perfection is defined
formatted picture at less than 2053, and exactly 4096 token-per-line rule2.c. as a nicely formatted picture at less than 2053, and exactly 4096
token-per-line rule2.c.
Testing Testing Testing Testing Testing Testing
-------------------------------------------------- --------------------------------------------------
Cross platform, it works on Mac OSX, fails on Linux. It works if #include <stdlib.h>, but Cross platform, it works on Mac OSX, fails on Linux. It works if #include
it dumps core without it (the only change!)?. 64bits without proper declaration of ints <stdlib.h>, but it dumps core without it (the only change!)?. 64bits without
in syscalls yields unpredictable results. 32bit compilation showed problems with byte- proper declaration of ints in syscalls yields unpredictable results. 32bit
swapping in using addition versus bitwise-or. In 32bit land, x<<32 == x, so x<<32 + x>>32 compilation showed problems with byte- swapping in using addition versus
does not yield the desired result, but x<<32 | x>>32 does (with appropriate care and bitwise-or. In 32bit land, x<<32 == x, so x<<32 + x>>32 does not yield the
feeding). And the different compilers on different hosts complained loudly and desired result, but x<<32 | x>>32 does (with appropriate care and feeding).
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 homage code Further, the program did not do UNIX pipelines well, due to the unbuffered
(byte-at-a-time I/O), of which write() was also the source of the portability problem. homage code (byte-at-a-time I/O), of which write() was also the source of the
Replace write() with getchar()/putchar(), and it piped well but lost some obscurity and portability problem. Replace write() with getchar()/putchar(), and it piped
no longer paid (direct) homage to history. 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, no semis, Another problem: only one expression at a time was allowed on the command line,
no comments, and it turns out $2, $3, ... were not handled correctly. Given the new I/O no semis, no comments, and it turns out $2, $3, ... were not handled correctly.
code, it was now possible to copy the command line arguments into a buffer, and write a Given the new I/O code, it was now possible to copy the command line arguments
small routine to retrieve bytes from this buffer like getchar(). Voila! Better obfuscation into a buffer, and write a small routine to retrieve bytes from this buffer
with much better functionality! like getchar(). 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?
Bytes are now hard to find to keep exactly at 4096, and adding functionality to obscured Bytes are now hard to find to keep exactly at 4096, and adding functionality to
code is not easy. Restructuring the logic to gain the bytes necessary, code to display obscured code is not easy. Restructuring the logic to gain the bytes
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 syntax error. Test. Test. Test. Oh, noes! More bugs! Typing "1 + 2 5" yields 3, but no
"g = 1" yields a syntax error, and "0xg" does not! ")" causes a seg fault! Why didn't I syntax error. "g = 1" yields a syntax error, and "0xg" does not! ")" causes a
test these before I started obfuscating?! By this point, everything I did to the code segfault! Why didn't I test these before I started obfuscating?! By this
perturbed the 4096 bytes, all variables were already a single character, and it was really point, everything I did to the code perturbed the 4096 bytes, all variables
difficult to follow the logic in a debugger (single-stepping an expression did the entire were already a single character, and it was really difficult to follow the
line of code, even if it involved multiple statements -- and it did, everywhere, even in logic in a debugger (single-stepping an expression did the entire line of code,
the unobfuscated version; think: a complex chain of ternary expressions is a single even if it involved multiple statements -- and it did, everywhere, even in the
statement). This involved careful examination of all the expressions within the logic, unobfuscated version; think: a complex chain of ternary expressions is a single
trying to find a different way of getting the same result, but typically within fewer statement). This involved careful examination of all the expressions within
bytes than before to allow for the added logic to fix the bugs. the logic, trying to find a different way of getting the same result, but
typically within fewer bytes than before to allow for the added logic to fix
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 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?
Add unary plus, re-balance. Little things now take a fair bit of work. Adding anything Add unary plus, re-balance. Little things now take a fair bit of work. Adding
requires taking away in others, but since all the logic is functional, this means anything requires taking away in others, but since all the logic is functional,
rewriting the logic to be more concise. After several rounds of this, bytes are scarce to this means rewriting the logic to be more concise. After several rounds of
come by. 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 error, but Made them silent (easier said than done!). Why does "1 + 2 5" print a syntax
"a = 1 + 2 5" does not? Rewrite(!) the assignment logic -- and this requires more bytes! error, but "a = 1 + 2 5" does not? Rewrite(!) the assignment logic -- and this
requires more bytes!
Better Obscurity Through Simplification Better Obscurity Through Simplification
-------------------------------------------------- --------------------------------------------------
Except there were not enough bytes to do this, unless #include <unistd.h> is removed. But Except there were not enough bytes to do this, unless #include <unistd.h> is
then sbrk() must be declared, eating into the savings. If only there were 512 bytes of removed. But then sbrk() must be declared, eating into the savings. If only
memory already allocated that could be portably touched for free.... there were 512 bytes of memory already allocated that could be portably touched
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 apropos It was working again! It formats beautifully, it compiled cleanly (with
-Wno-guards), it remains obscure after cpp and clang-format, it measures within the limits apropos -Wno-guards), it remains obscure after cpp and clang-format, it
and exactly at 4096 (this latter was really getting challenging to maintain). No more bugs measures within the limits and exactly at 4096 (this latter was really getting
known, it is feature complete, documentation written. challenging to maintain). No more bugs known, it is feature complete,
documentation written.
But wouldn't it be nice to have a logical not? But wouldn't it be nice to have a logical not?
Sigh. This was the last operation added. Another round of code churn, expressions are Sigh. This was the last operation added. Another round of code churn,
difficult to rewrite because they are both obscure and relatively tight. But changing expressions are difficult to rewrite because they are both obscure and
around the stable initialization code -- by now the largest piece of logic -- yielded the relatively tight. But changing around the stable initialization code -- by now
necessary bytes, and improved obscurity by double duty encoding of the operations the largest piece of logic -- yielded the necessary bytes, and improved
initialization. obscurity by double duty encoding of the operations initialization.
The utility is now truly functional, and feature complete, and obscure. Without src.doc The utility is now truly functional, and feature complete, and obscure.
(basic documentation of the code structure), and some commented expanded logic stashed away, Without src.doc (basic documentation of the code structure), and some commented
and the familiarity I have with the code having written and revised it repeatedly, I would expanded logic stashed away, and the familiarity I have with the code having
not even want to attempt to decipher the logic. Many times in the debugging, I was thwarted written and revised it repeatedly, I would not even want to attempt to decipher
by my own obfuscations, and went down blind paths in trying to fix a bug or add functionality. the logic. Many times in the debugging, I was thwarted by my own obfuscations,
Now I think I have a good entry. and went down blind paths in trying to fix a bug or add 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?