244 lines
7.4 KiB
Markdown
244 lines
7.4 KiB
Markdown
## To build:
|
|
|
|
``` <!---sh-->
|
|
make
|
|
```
|
|
|
|
There are two additional programs based on the author's remarks and the original
|
|
version, provided as alternate code as it might be less portable. See [sorted
|
|
code](#sorted-code), [punch card code](#punch-card-code) and [alternate
|
|
code](#alternate-code) below.
|
|
|
|
|
|
### Bugs and (Mis)features:
|
|
|
|
The current status of this entry is:
|
|
|
|
> **STATUS: uses gets() - change to fgets() if possible**<br>
|
|
> **STATUS: main() has only one arg - try and make it have 2 or 3**
|
|
|
|
For more detailed information see [2001/westley in bugs.html](../../bugs.html#2001_westley).
|
|
|
|
|
|
## To use:
|
|
|
|
``` <!---sh-->
|
|
./westley 2>/dev/null
|
|
# enter some input, terminate with EOF
|
|
|
|
echo foo | ./westley 2>/dev/null
|
|
|
|
./westley < westley.c 2>/dev/null
|
|
```
|
|
|
|
|
|
## Try:
|
|
|
|
``` <!---sh-->
|
|
./try.sh
|
|
```
|
|
|
|
|
|
## Sorted code:
|
|
|
|
The author stated that one can sort (ignoring leading blanks i.e. `sort -b`) the
|
|
code by line and it will always sort the order of the input.
|
|
|
|
This is generated and compiled by default; use `westley.sort` as you would
|
|
`westley` above.
|
|
|
|
|
|
## Punch card code:
|
|
|
|
The author also stated that if you just sort the code (`sort westley.c`) you'll
|
|
get a version that just prints out the punch card. The `westley.punch.c`
|
|
(generated by `make` via `sort westley.c`) code is specifically so that Westley
|
|
can punch everyone in the face! :-) This is also compiled by default.
|
|
|
|
|
|
### Punch card try:
|
|
|
|
``` <!---sh-->
|
|
echo 'Brian Westley does it again!' | ./westley.punch 2>/dev/null
|
|
```
|
|
|
|
|
|
## Alternate code:
|
|
|
|
An alternate version of this entry, [westley.alt.c](%%REPO_URL%%/2001/westley/westley.alt.c), is provided.
|
|
This alternate code might be less portable.
|
|
|
|
|
|
### Alternate build:
|
|
|
|
``` <!---sh-->
|
|
make alt
|
|
```
|
|
|
|
|
|
### Alternate use:
|
|
|
|
Use `westley.alt` as you would `westley` above.
|
|
|
|
|
|
## Judges' remarks:
|
|
|
|
Try copying the source to a new file and changing around the
|
|
order of the lines. Does it produce the exact same output?
|
|
|
|
By changing the order of the source, figure out in how many different
|
|
ways this program can transform input to output.
|
|
|
|
This assortment of obfuscated lines takes [position-independent
|
|
code](https://en.wikipedia.org/wiki/Position-independent_code) to a new level! :-)
|
|
|
|
We find it amazing that with just 28 lines of code (not counting comments and
|
|
blank lines) there are `28!` or `304,888,344,611,713,860,501,504,000,000`
|
|
versions of the program, all valid C!
|
|
|
|
|
|
## Author's remarks:
|
|
|
|
[Punch cards](https://en.wikipedia.org/wiki/Punched_card), or Hollerith cards,
|
|
were becoming obsolete just as C was becoming more popular[^1]; this is rather
|
|
unfortunate, because C has an advantage over many other languages when it comes
|
|
to punch cards - C programs can be written to be "drop proof".
|
|
|
|
Consider the following C program:
|
|
|
|
``` <!---c-->
|
|
main(){a();b();c();}
|
|
a(){printf("A");}
|
|
b(){printf("B");}
|
|
c(){printf("C");}
|
|
```
|
|
|
|
This program will compile and run no matter what order the
|
|
lines are in. If you punch it on cards (one line of code per
|
|
card, naturally) and drop them, you don't need to put them in
|
|
any particular order[^2]; it will always produce `ABC` as output.
|
|
|
|
You can also use global variables:
|
|
|
|
``` <!---c-->
|
|
char *greet = "Hello, world!";
|
|
char *greet; main(){puts(greet);}
|
|
```
|
|
|
|
It would be trivial to write an entire C program that is
|
|
"drop proof" using the above techniques; surprisingly, it's
|
|
possible to write a program that will always compile but
|
|
behave differently depending on the line order:
|
|
|
|
``` <!---c-->
|
|
test(a,b){return a;}
|
|
#define test(a,b) test(b,a)
|
|
main(){if (test(0,1)) printf("T"); else printf("F");}
|
|
```
|
|
|
|
This program will print out `T` if the `#define` statement
|
|
falls between the declaration of `test()` and its use in
|
|
`main()`, otherwise it will print out `F`. Furthermore,
|
|
you can use the same call to `test()` repeatedly in
|
|
different lines to return different values:
|
|
|
|
``` <!---c-->
|
|
#define test(x,y) test(y,x)
|
|
main(){int i=0;a(&i);b(&i);c(&i);d(&i);printf("%d\n",i);}
|
|
a(i)int*i;{if (test(0,1)) *i += 1;}
|
|
b(i)int*i;{if (test(0,1)) *i += 2;}
|
|
c(i)int*i;{if (test(0,1)) *i += 4;}
|
|
d(i)int*i;{if (test(0,1)) *i += 8;}
|
|
test(x,y){return x;}
|
|
```
|
|
|
|
If you enter the above program on punch cards, throw them
|
|
up in the air, gather them up, and run it, you will get
|
|
a number from 0 to 15 printed out. Now *that's* a random
|
|
number generator...
|
|
|
|
Of course, there are drawbacks to C programming on punch
|
|
cards; an 80-character line limit can be restricting, and
|
|
(to be maximally portable) you need to use trigraphs for
|
|
the characters `^[]{}\` (these aren't found on every
|
|
keypunch, and have various encodings).
|
|
|
|
My C program entry does the following:
|
|
|
|
1. Reads in lines.
|
|
|
|
2. Rearranges the lines, or not.
|
|
- if it rearranges the lines, they are sorted or scrambled.
|
|
|
|
3. Reverses the order of the lines, or not.
|
|
|
|
4. Prints out the lines as either text or
|
|
[EBCDIC](https://en.wikipedia.org/wiki/EBCDIC) punch cards.
|
|
|
|
The code, as written, scrambles the input and prints it out
|
|
as normal text, so you can simulate 'dropping' the cards.
|
|
You can repeatedly scramble the code to get all 12 variants
|
|
(but the scrambling isn't very random; it's just a bubble
|
|
sort with a random number, so some variants are more common
|
|
than others). You can also just swap the arguments in the
|
|
calls to `t()` in all combinations to get all the variations.
|
|
|
|
If you sort the lines, you will get the version that just
|
|
prints out the input as punch cards (you will need to display
|
|
at least 82 columns of text, or clip the last few chars by
|
|
piping it through `sed s/...$//`). If you use this on the
|
|
original code, you will see that it spells out `HOLLERITH`
|
|
when punched on cards (you can see the tall, thin letters
|
|
spelling out `HOLLERITH` if you turn the C code sideways).
|
|
You will also see why `/KC 0000 K` is a `Hollerith emoticon`.
|
|
Unrecognized characters are punched as a lace column.
|
|
(You can see an ASCII simulation of the code as punch cards
|
|
by looking at `card.gif`.)
|
|
|
|
For a nice test pattern, try
|
|
|
|
```
|
|
&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ:#@'=".<(+|!$*);,%_>?abcmnoxyz^[]{}\
|
|
```
|
|
|
|
If you want to analyze the program, sorting the lines while
|
|
ignoring leading blanks works best (`sort -b`). This will
|
|
produce the version that just reverses the line order, and
|
|
the routines in the code are in a sensible order.
|
|
|
|
This program should work on EBCDIC computers. It assumes
|
|
that the `time_t` ptr param to `time(3)` will work with an `int`
|
|
ptr, and that "passing through" a `char[][]` array as a simple
|
|
`char *` is OK. Your compiler MUST understand trigraphs, or it
|
|
will miss the trigraph backslash before the double quote in
|
|
the `K` string and complain about a malformed string; for GNU
|
|
C, use `-ansi` or `-trigraphs`. Entering a line of more than 80
|
|
characters, or more than 81 lines, will jam the card reader.
|
|
|
|
I've used the `a[b]` ==> `*(a+b)` trick, because this is
|
|
actually shorter than the trigraph version of `a??(b??)`.
|
|
|
|
With 28 lines of code (not counting the blank lines or the
|
|
comment), there are technically `28!` different programs, or
|
|
`304,888,344,611,713,860,501,504,000,000` different versions,
|
|
all legal C.
|
|
|
|
[^1]: except in Florida voting machines
|
|
|
|
[^2]: all cards must be face down, nine-edge first, of course.
|
|
|
|
|
|
<!--
|
|
|
|
Copyright © 1984-2024 by Landon Curt Noll. All Rights Reserved.
|
|
|
|
You are free to share and adapt this file under the terms of this license:
|
|
|
|
Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
|
|
|
|
For more information, see:
|
|
|
|
https://creativecommons.org/licenses/by-sa/4.0/
|
|
|
|
-->
|