Fixes in 2015/burton
Format fixes (maybe typos?) in README.md. Added calc.1 which is essentially what was calc.man but so one can actually get expected formatting when using man ./calc.1. If I fixed any typos they are not fixed in calc.man. I kept the commands the same so one would have to change calc to ./prog. Formatting and maybe typos in road.to.obscurity.
This commit is contained in:
1 parent
dbc6a3b812
commit
fa31f80b39
3 files changed
+339
-175
No files matched your search
+76
-61
@@ -46,13 +46,14 @@ grep '[0-9]' prog.c
|
||||
|
||||
./prog < test.in
|
||||
|
||||
man ./calc.man
|
||||
man ./calc.1
|
||||
```
|
||||
|
||||
## Alternate code
|
||||
|
||||
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:
|
||||
|
||||
@@ -66,10 +67,11 @@ Use `prog.alt` as you would `prog` above.
|
||||
|
||||
"Look, Ma, no ~~hands~~ digits!"
|
||||
|
||||
The place where some arithmetic operations are done, for example,
|
||||
division or modulo, are easy to find. Where are addition and subtraction?
|
||||
The place where some arithmetic operations are done, for example, division or
|
||||
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:
|
||||
|
||||
@@ -80,10 +82,10 @@ See the included man page. ;-)
|
||||
|
||||
### Notes
|
||||
|
||||
This program is named calc. Onomatopoeia of sorts, in the visual realm.
|
||||
The 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 written on an iDevice),
|
||||
but sanity prevailed.
|
||||
This program is named calc. Onomatopoeia of sorts, in the visual realm. The
|
||||
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
|
||||
written on an iDevice), but sanity prevailed.
|
||||
|
||||
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
|
||||
|
||||
Dominik Muth observed a syntax error on `1+1`, `1-1`, `1|1`, and `1^1` when using an
|
||||
ARM based computer, although `1*1` and others worked correctly.
|
||||
This occurs because some platforms by default treat `char` as `unsigned char`.
|
||||
The original code has this weakness, which can be corrected by adding `-fsigned-char`
|
||||
to the compile line.
|
||||
The modified code is agnostic to `char` signed-ness.
|
||||
Dominik Muth observed a syntax error on `1+1`, `1-1`, `1|1`, and `1^1` when
|
||||
using an ARM based computer, although `1*1` and others worked correctly. This
|
||||
occurs because some platforms by default treat `char` as `unsigned char`. The
|
||||
original code has this weakness, which can be corrected by adding
|
||||
`-fsigned-char` to the compile line.
|
||||
|
||||
The modified code is agnostic to `char` signedness.
|
||||
|
||||
### 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,
|
||||
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.
|
||||
There 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.
|
||||
Nor are the digits simply obfuscated. There are no character constants. There
|
||||
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.
|
||||
|
||||
Flow control is by `for` and `return`. It is If-less. Switch-less. While-less. Hope-less.
|
||||
Literally, if not figuratively. ;-)
|
||||
Flow control is by `for` and `return`. It is if-less, switch-less,
|
||||
while-less, and hopeless. Literally, if not figuratively. ;-)
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Finally, calc is interesting, if for no other reason than it is practically useful:
|
||||
Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and logical operations with memory,
|
||||
on the command line, or interactively.
|
||||
Finally, calc is interesting, if for no other reason than it is practically
|
||||
useful: Hex/decimal conversion, arithmetic, byte-swapping, bit-shifting, and
|
||||
logical operations with memory, on the command line, or interactively.
|
||||
|
||||
### 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,
|
||||
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
|
||||
are readily visible, with single letter names that don't clutter the screen,
|
||||
in a tautological arrangement.
|
||||
- In the spirit of clarity, most of the variables are declared before use, so
|
||||
they are readily visible, with single letter names that don't clutter the
|
||||
screen, in a tautological arrangement.
|
||||
|
||||
- More helpful is the use of `typedef`s instead of `#define`, so the types remain useful
|
||||
even in the debugger, and source code beautifiers will not undo careful presentation.
|
||||
- More helpful is the use of `typedef`s instead of `#define`, so the types
|
||||
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
|
||||
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
|
||||
for function definitions, thereby greatly improving readability, and reducing arguments
|
||||
about said readability.
|
||||
- Curly braces invite endless holy wars over readability. This code only uses
|
||||
them for function definitions, thereby greatly improving readability, and
|
||||
reducing arguments about said readability.
|
||||
|
||||
- One of C's many blessings (until recently) is a modicum of keywords,
|
||||
of which only 8 are used:
|
||||
`for`, `return`, `typedef`, `void`, `char`, `short`, `unsigned` and `long`;
|
||||
10 if `#include` and `#define` are counted.
|
||||
- One of C's many blessings (until recently) is a modicum of keywords, of which
|
||||
only 8 are used: `for`, `return`, `typedef`, `void`, `char`, `short`, `unsigned`
|
||||
and `long`; 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
|
||||
*clang-format*(1), after running it through the preprocessor, sustaining its better qualities.
|
||||
- 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
|
||||
better qualities.
|
||||
|
||||
- That the code is obfuscated is spelled out two different ways in the source; it helps
|
||||
to reformat the code to see the second spelling.
|
||||
- That the code is obfuscated is spelled out two different ways in the source;
|
||||
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 allocation, nor pre-allocation of arrays, so where does the
|
||||
memory come from? The multi-base strtol() code is pretty trick.
|
||||
No special attempt was made to 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 know when to mod -- okay, but where is add and subtract?
|
||||
And really, who doesn't enjoy a setjmp() in the argument list?
|
||||
- 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?
|
||||
The multi-base strtol() code is pretty tricky. No special attempt was made to
|
||||
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
|
||||
know when to mod -- okay, but where is add and subtract? And really, who
|
||||
doesn't enjoy a setjmp() in the argument list?
|
||||
|
||||
- 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 this helpful guide,
|
||||
things like reading input lines remain as tricky to read as they are to write.
|
||||
- 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
|
||||
this helpful guide, things like reading input lines remain as tricky to read as
|
||||
they are to write.
|
||||
|
||||
### Compiler warnings
|
||||
|
||||
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 -m32`
|
||||
yields a program that operates on 32-bit values, with wrap-around of shifts mod 32:
|
||||
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
|
||||
-m32` yields a program that operates on 32-bit values, with wrap-around of
|
||||
shifts mod 32:
|
||||
|
||||
1<<48
|
||||
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.
|
||||
To stifle these complaints:
|
||||
|
||||
-Wno-return-type -Wno-parentheses -Wno-empty-body
|
||||
|
||||
|
||||
When invoked with -ansi -pedantic, there are two warnings, which can be ignored:
|
||||
|
||||
* `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.
|
||||
This allows for a convenient test suite:
|
||||
|
||||
ln -s prog eprog
|
||||
PATH=. eprog < test.in | diff - test.out
|
||||
```sh
|
||||
ln -s prog eprog
|
||||
PATH=. eprog < test.in | diff - test.out
|
||||
```
|
||||
|
||||
### 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:
|
||||
|
||||
|
||||
@@ -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
@@ -3,18 +3,19 @@ Development of the obfuscated calculator.
|
||||
|
||||
I wanted to enter the IOCCC....
|
||||
|
||||
I think I can write something clever. Possibly even obscure. The competition is fierce,
|
||||
but not a closed door.
|
||||
I think I can write something clever. Possibly even obscure. The competition
|
||||
is fierce, but not a closed door.
|
||||
|
||||
I needed an interesting program, that had a reasonable chance of begin shrunk 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 recursive descent signed integer
|
||||
expression parser I developed from first 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 were added over time.
|
||||
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
|
||||
utilities I've written over the years. One useful program was calc, a simple
|
||||
recursive descent signed integer expression parser I developed from first
|
||||
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
|
||||
were added over time.
|
||||
|
||||
Calc is a useful, functioning utility. The operators before the obfuscation dance began
|
||||
(this is called fore-shadowing):
|
||||
Calc is a useful, functioning utility. The operators before the obfuscation
|
||||
dance began (this is called fore-shadowing):
|
||||
|
||||
() forced precedence
|
||||
- ~ ? unary minus, compl, byteswap
|
||||
@@ -22,8 +23,8 @@ Calc is a useful, functioning utility. The operators before the obfuscation dan
|
||||
+ - | ^ add, sub, bitor, xor
|
||||
NUM 0-9
|
||||
|
||||
This seemed like a reasonable candidate to experiment with obfuscation; perhaps it might
|
||||
yield good results.
|
||||
This seemed like a reasonable candidate to experiment with obfuscation; perhaps
|
||||
it might yield good results.
|
||||
|
||||
Principle of operation
|
||||
--------------------------------------------------
|
||||
@@ -69,7 +70,8 @@ The algorithm was and is rather straightforward:
|
||||
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
|
||||
- tokenization
|
||||
@@ -79,153 +81,170 @@ Within the above algorithm, there are many details unmentioned, all ripe for obf
|
||||
- swapping bytes
|
||||
- user interface (command line vs stdin)
|
||||
|
||||
Uncertain as to how this would progress, I started obfuscating/condensing everything
|
||||
except the conversion operations, leaving those to sprintf() and strtol(). 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 the repetition. And of course, most
|
||||
functions were renamed with one or two letters, and most of the variables became single
|
||||
letter globals. Tokenization was rather easy with a declared array, just initialized with
|
||||
unique values for each.
|
||||
Uncertain as to how this would progress, I started obfuscating/condensing
|
||||
everything except the conversion operations, leaving those to sprintf() and
|
||||
strtol(). 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
|
||||
the repetition. And of course, most functions were renamed with one or two
|
||||
letters, and most of the variables became single letter globals. Tokenization
|
||||
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
|
||||
operations via a simple transformation. The code now dealt with small integer values
|
||||
instead of direct ASCII representation of the operations. And the strings where collapsed
|
||||
into a single combined error message and operator tokenization table. This was relatively
|
||||
obscure.
|
||||
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
|
||||
small integer values instead of direct ASCII representation of the operations.
|
||||
And the strings where collapsed into a single combined error message and
|
||||
operator tokenization table. This was relatively obscure.
|
||||
|
||||
Next, sprintf() was replaced with a custom formatter. This was rather simple, but at least
|
||||
the formatting was not obvious. Formatting and printing were split early. strtol() was
|
||||
replaced with custom logic, limited to 3 practical bases: octal, decimal, and hex. The
|
||||
last result was remembered in a special variable, dot.
|
||||
Next, sprintf() was replaced with a custom formatter. This was rather simple,
|
||||
but at least the formatting was not obvious. Formatting and printing were
|
||||
split early. strtol() was replaced with custom logic, limited to 3 practical
|
||||
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
|
||||
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 memories.
|
||||
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
|
||||
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
|
||||
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 more difficult to follow, used only
|
||||
for-loops, and I had to start taking notes to follow it. This was a good sign! ;-)
|
||||
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
|
||||
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
|
||||
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
|
||||
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 shift: >>>. Better. But
|
||||
with more features, more to get wrong, and harder to know which expression is causing the
|
||||
error. The easy solution: if the name begins with 'e', echo the input. Now I can see the
|
||||
failing expression. Wait, what is this test expression supposed to be testing?
|
||||
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).
|
||||
Why does -1>>32 yield -1? Oh, yeah: signed arithmetic. Enter Java's unsigned
|
||||
shift: >>>. Better. But with more features, more to get wrong, and harder to
|
||||
know which expression is causing the error. The easy solution: if the name
|
||||
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?
|
||||
|
||||
Adding new functionality to an obscure program is ... going about it backwards. Get the
|
||||
functionality correct and complete, THEN obscure. Sounds obvious, but that did not
|
||||
happen. I got comments working, then desired to have multiple statements on a single
|
||||
line (read: semicolon expression terminators). This involved quite a bit of rework of
|
||||
the input code, but now allowed very convenient user interaction.
|
||||
Adding new functionality to an obscure program is ... going about it backwards.
|
||||
Get the functionality correct and complete, THEN obscure. Sounds obvious, but
|
||||
that did not happen. I got comments working, then desired to have multiple
|
||||
statements on a single line (read: semicolon expression terminators). This
|
||||
involved quite a bit of rework of the input code, but now allowed very
|
||||
convenient user interaction.
|
||||
|
||||
Better Obscurity is a laborious process
|
||||
--------------------------------------------------
|
||||
|
||||
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 before (to my knowledge), and
|
||||
I was able to work out the how-tos and the compromises. Now I can no longer declare my
|
||||
arrays, I have to allocate them. Obscurity now increased!
|
||||
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
|
||||
before (to my knowledge), and I was able to work out the how-tos and the
|
||||
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?
|
||||
|
||||
Around this time, the formatted code no longer fit with the 2053 limit (due to a 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 wrote another program to
|
||||
reformat this code into rule2.c, which was a 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 rule. But this was a challenge!
|
||||
Around this time, the formatted code no longer fit with the 2053 limit (due to
|
||||
a 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
|
||||
wrote another program to reformat this code into rule2.c, which was a 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
|
||||
rule. But this was a challenge!
|
||||
|
||||
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 possible, and worked to
|
||||
achieve both a perfectly presented picture AND the single-line exactly-at-4096 rule2.c.
|
||||
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
|
||||
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
|
||||
formatted picture at less than 2053, and exactly 4096 token-per-line rule2.c.
|
||||
Finally, "perfection", with assignable memories! Where perfection is defined
|
||||
as a nicely formatted picture at less than 2053, and exactly 4096
|
||||
token-per-line rule2.c.
|
||||
|
||||
Testing Testing Testing
|
||||
--------------------------------------------------
|
||||
|
||||
Cross platform, it works on Mac OSX, fails on Linux. It works if #include <stdlib.h>, but
|
||||
it dumps core without it (the only change!)?. 64bits without proper declaration of ints
|
||||
in syscalls yields unpredictable results. 32bit compilation showed problems with byte-
|
||||
swapping in using addition versus bitwise-or. In 32bit 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
|
||||
Cross platform, it works on Mac OSX, fails on Linux. It works if #include
|
||||
<stdlib.h>, but it dumps core without it (the only change!)?. 64bits without
|
||||
proper declaration of ints in syscalls yields unpredictable results. 32bit
|
||||
compilation showed problems with byte- swapping in using addition versus
|
||||
bitwise-or. In 32bit 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
|
||||
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
|
||||
(byte-at-a-time I/O), of which write() was also the source of the portability problem.
|
||||
Replace write() with getchar()/putchar(), and it piped well but lost some obscurity and
|
||||
no longer paid (direct) homage to history.
|
||||
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
|
||||
portability problem. Replace write() with getchar()/putchar(), and it 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, 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 into a buffer, and write a
|
||||
small routine to retrieve bytes from this buffer like getchar(). Voila! Better obfuscation
|
||||
with much better functionality!
|
||||
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.
|
||||
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
|
||||
like getchar(). Voila! Better obfuscation with much better functionality!
|
||||
|
||||
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
|
||||
code is not easy. Restructuring the logic to gain the bytes necessary, code to display
|
||||
the variables was added.
|
||||
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
|
||||
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.
|
||||
"g = 1" yields a syntax error, and "0xg" does not! ")" causes a seg fault! Why didn't I
|
||||
test these before I started obfuscating?! By 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 logic in a debugger (single-stepping an expression did the entire
|
||||
line of code, even if it involved multiple statements -- and it did, everywhere, even in
|
||||
the unobfuscated version; think: a complex chain of ternary expressions is a single
|
||||
statement). This involved careful examination of all the expressions within 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.
|
||||
Test. Test. Test. Oh, noes! More bugs! Typing "1 + 2 5" yields 3, but no
|
||||
syntax error. "g = 1" yields a syntax error, and "0xg" does not! ")" causes a
|
||||
segfault! Why didn't I test these before I started obfuscating?! By 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
|
||||
logic in a debugger (single-stepping an expression did the entire line of code,
|
||||
even if it involved multiple statements -- and it did, everywhere, even in the
|
||||
unobfuscated version; think: a complex chain of ternary expressions is a single
|
||||
statement). This involved careful examination of all the expressions within
|
||||
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.
|
||||
|
||||
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
|
||||
requires taking away in others, but since all the logic is functional, this means
|
||||
rewriting the logic to be more concise. After several rounds of this, bytes are scarce to
|
||||
come by.
|
||||
Add unary plus, re-balance. Little things now take a fair bit of work. Adding
|
||||
anything requires taking away in others, but since all the logic is functional,
|
||||
this means rewriting the logic to be more concise. After several rounds of
|
||||
this, bytes are scarce to come by.
|
||||
|
||||
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
|
||||
"a = 1 + 2 5" does not? Rewrite(!) the assignment logic -- and this requires more bytes!
|
||||
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
|
||||
requires more bytes!
|
||||
|
||||
Better Obscurity Through Simplification
|
||||
--------------------------------------------------
|
||||
|
||||
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 for free....
|
||||
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
|
||||
for free....
|
||||
|
||||
Light bulb! Memory allocation was excised, and the code became more obscure.
|
||||
|
||||
It was working again! It formats beautifully, it compiled cleanly (with apropos
|
||||
-Wno-guards), it remains obscure after cpp and clang-format, it measures within the limits
|
||||
and exactly at 4096 (this latter was really getting challenging to maintain). No more bugs
|
||||
known, it is feature complete, documentation written.
|
||||
It was working again! It formats beautifully, it compiled cleanly (with
|
||||
apropos -Wno-guards), it remains obscure after cpp and clang-format, it
|
||||
measures within the limits and exactly at 4096 (this latter was really getting
|
||||
challenging to maintain). No more bugs known, it is feature complete,
|
||||
documentation written.
|
||||
|
||||
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
|
||||
difficult to rewrite because they are both obscure and relatively tight. But changing
|
||||
around the stable initialization code -- by now the largest piece of logic -- yielded the
|
||||
necessary bytes, and improved obscurity by double duty encoding of the operations
|
||||
initialization.
|
||||
Sigh. This was the last operation added. Another round of code churn,
|
||||
expressions are difficult to rewrite because they are both obscure and
|
||||
relatively tight. But changing around the stable initialization code -- by now
|
||||
the largest piece of logic -- yielded the necessary bytes, and improved
|
||||
obscurity by double duty encoding of the operations initialization.
|
||||
|
||||
The utility is now truly functional, and feature complete, and obscure. Without src.doc
|
||||
(basic documentation of the code structure), and some commented expanded logic stashed away,
|
||||
and the familiarity I have with the code having written and revised it repeatedly, I would
|
||||
not even want to attempt to decipher the logic. Many times in the debugging, I was thwarted
|
||||
by my own obfuscations, and went down blind paths in trying to fix a bug or add functionality.
|
||||
Now I think I have a good entry.
|
||||
The utility is now truly functional, and feature complete, and obscure.
|
||||
Without src.doc (basic documentation of the code structure), and some commented
|
||||
expanded logic stashed away, and the familiarity I have with the code having
|
||||
written and revised it repeatedly, I would not even want to attempt to decipher
|
||||
the logic. Many times in the debugging, I was thwarted by my own obfuscations,
|
||||
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?
|
||||
Reference in new issue
Block a user