Merge pull request #1396 from xexyl/fanf-98

This commit is contained in:
Landon Curt Noll authored and GitHub committed 2023-11-12 09:26:10 -07:00
commit b1e339cdbe
26 files changed
+623 -295

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
#include <ctype.h>
#include <curses.h>
#define T isspace(*(t=Z(p)))&&
#define V return
#define _ while
int d,i,j,m,n,p,q,x,y;char*c,b[BUF],*f,*g=b,*h,k[]="hjklbJKw0$tbixWRq",*t;
char*Z(a){if(a<0)V b;V b+a+(b+a<g?0:h-g);}P(a)char*a;{V
a-b-(a<h?0:h-g);}S(){p=0;}bf(){n=p=P(c);}Q(){q=1;}C(){clear();Y();}
G(){t=Z(p);_(t<g)*--h= *--g;_(h<t)*g++= *h++;p=P(h);}B(){_(!T b<t)--p;_(T
b<t)--p;}M(a){_(b<(t=Z(--a))&&*t-'\n');V
b<t?++a:0;}N(a){_((t=Z(a++))<c&&*t-'\n');V
t<c?a:P(c);}A(a,j){i=0;_((t=Z(a))<c&&*t-'\n'&&i<j){i+= *t-'\t'?1:8-(i&7);++a;}V
a;}L(){0<p&&--p;}R(){p<P(c)&&++p;}U(){p=A(M(M(p)-1),x);}
D(){p=A(N(p),x);}H(){p=M(p);}E(){p=N(p);L();}
J(){m=p=M(n-1);_(0<y--)D();n=P(c);}K(){j=d;_(0<--j)m=M(m-1),U();}
I(){G();_((j=getch())-'\x1b'){if(j-'\b')g-h&&(*g++=j-'\r'?j:'\n');else
b<g&&--g;p=P(h);Y();}}X(){G();p=h<c?P(++h):p;}
F(){j=p;p=0;G();write(i=creat(f,MODE),h,(int)(c-h));close(i);p=j;}W(){_(!T
t<c)++p;_(T
t<c)++p;}int(*z[])()={L,D,U,R,B,J,K,W,H,E,S,bf,I,X,F,C,Q,G};
Y(){m=p<m?M(p):m;if(n<=p){m=N(p);i=m-P(c)?d:d-2;_(0<i--)m=M(m-1);}
move(0,0);i=j=0;n=m;_(1){p-n||(y=i,x=j);t=Z(n);if(d<=i||c<=t)break;
if(*t-'\r')addch(*t),j+= *t-'\t'?1:8-(j&7);if(*t=='\n'||COLS<=j)
++i,j=0;++n;}clrtobot();++i<d&&mvaddstr(i,0,"<< EOF >>");move(y,x);
refresh();}main(u,v)char**v;{h=c=b+BUF;if(u<2)V
2;initscr();d=LINES;raw();noecho();idlok(stdscr,1);if(0<(i=open(f= *++v,0))){
g+=read(i,b,BUF);g=g<b?b:g;close(i);}S();_(!q){Y();i=0;j=getch();
_(k[i]&&j-k[i])++i;(*z[i])();}endwin();V 0;}
+1
View File
@@ -1,3 +1,4 @@
kivinen kivinen
kivinen.alt
kivinen.orig kivinen.orig
prog.orig prog.orig
+3 -3
View File
@@ -58,7 +58,7 @@ ARCH=
# Defines that are needed to compile # Defines that are needed to compile
# #
CDEFINE= -DZ=15000 CDEFINE= -DZ=20000
# Include files that are needed to compile # Include files that are needed to compile
# #
@@ -113,8 +113,8 @@ OBJ= ${PROG}.o
DATA= DATA=
TARGET= ${PROG} TARGET= ${PROG}
# #
ALT_OBJ= ALT_OBJ= ${PROG}.alt.o
ALT_TARGET= ALT_TARGET= ${PROG}.alt
################# #################
+38 -8
View File
@@ -3,14 +3,14 @@
If your machine support the X Window System, Version 11: If your machine support the X Window System, Version 11:
```sh ```sh
make all make alt
``` ```
NOTE: this entry requires `X11/Xlib.h` header file and the X11 library to We recommend the alt version first as the original goes too fast on modern
compile. macOS users running Mountain Lion and later will need to download and systems. See [original code](#original-code) for the original should you wish to
install [XQuartz](https://www.xquartz.org) in order to compile and run this see what we mean.
entry.
You can reconfigure the value to `usleep(3)`; see [try](#try) section below.
### Bugs and (Mis)features: ### Bugs and (Mis)features:
@@ -26,15 +26,45 @@ For more detailed information see [1992 kivinen in bugs.md](/bugs.md#1992-kivine
### Try: ### Try:
```sh ```sh
./kivinen ./kivinen.alt
./kivinen a ./kivinen.alt a
./kivinen a b ./kivinen.alt a b
``` ```
See also the author's remarks for other variations. See also the author's remarks for other variations.
Also try changing the speed this game moves. For instance if you wish to change
the `usleep(3)` value to `30000` from `20000`, try:
```sh
make clobber CDEFINE="-DZ=30000" alt # make it slower
make clobber CDEFINE="-DZ=10000" alt # make it faster
```
Then use the same syntax as above and described by the author.
## Original code:
As noted above, the alt version is recommended because the code goes too fast in
modern systems. If you wish to see the original you may do so with the original
code.
### Original build:
```sh
make all
```
### Original use:
Use `kivinen` as you would `kivinen.alt`.
## Judges' remarks: ## Judges' remarks:
+39
View File
@@ -0,0 +1,39 @@
#include <X11/Xlib.h>
#define exit(a) (exit(a),0)
# define X( x,v,y) XMoveWindow(d,s[x],(v)*z,(y)*z)
# define _ ButtonPressMask|ButtonReleaseMask
# define I( x,y) XSelectInput(d,s[x],y)
# define N( v) (XMapWindow(d,s[v]),v)
# define V VisibilityChangeMask
# define A XCreateSimpleWindow
# define D DefaultScreen(d)
# define E r.xbutton.button
# define R BlackPixel(d,D)
# define S r.xvisibility
int main(a)int a;{Window s[53];int w,u,i,c,y,l=0
,q,e=32,t,k,j=~0,z=(a+1)/2,x=a&1,v=z&1;XEvent r;Display*
d=XOpenDisplay("");s[0]=A(d,DefaultRootWindow(d),200,200,(x&
v?330:120)*z,215*z,2,R,R);I(N(c=0),_);for(;c<(x?32:52);c++){s[
c+1]=A(d,s[0],x ?c*20*z:(c-1)%10 *12*z,x?-20*z:(
c-1)/10*12*z,x ?z*(c^31?10: 2):z*10*(c?1:2
), x& v?z*10: (c &&!x?10:1+ x) *z,k =q=1, R,
WhitePixel(d ,D));I (N(c+1), V);}X( N(1),y=(x&v)
*115+50,x*70+ 60 ); X(N(c-- )+ 1,x?-10:(i=y)
,u=!x*130-20); while(c &&e< 160){if(x){if(
v){l=!(e &32)*9; for(w=30; w--;){ if(s[w+2]){ k=w%
10;l=e&32?k>l?k:l:k<l?k:l;X(w+2,10+k *20+q,(w/10)*20+e);}}l*=20;
e&31?e++:e&32?(q++,(q+l>300?e++:0)):(q--,q+l<10?e++:0); } u>140?
exit(c):u>-20?X(32,i,u-=(v?5:-1 )):(!v?(u =5,srandom(time(0)),i=
random()%88+6):0); } else{k>0?i+=(i>105?k= -k:k):(i+=(i<1?k= -k:
k)); j>0?u +=(u>140?exit(++c):j):(u+=(u <1?j= -j:j
)); X( 52,i,u);}XSync(d,0); while(
XCheckMaskEvent(d ,_|V,&r))if(!(r. type==ButtonPress
?(E&1||!x||!v?t=E*4-8 :u<0?i=y+4,u=122:1):r
.type==ButtonRelease&&E?t=0,1:r.type^VisibilityNotify|S.state^1)
)for(x&v?(u= -15),w++:(w=0);++w <52-x*20;)if
(s[w] ==S.window)x&&v|| w ^1?
XUnmapWindow (d,s[w]) ,s[w]=0,c--:
0,l=1; if(!x&&l)j= -j,l=0; if(l&x&&!v
)u=~19,c--,l=0;t=(!x||!v) &&(y<5&&t<0||y>95&&t>0)?0:t
;s[1]?X(1,y+=x&v?t:t/(x+1),130):exit(++c);usleep(Z);};return c;}
+4 -4
View File
@@ -1,5 +1,5 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#define ext(a) (exit(a),0) #define exit(a) (exit(a),0)
# define X( x,v,y) XMoveWindow(d,s[x],(v)*z,(y)*z) # define X( x,v,y) XMoveWindow(d,s[x],(v)*z,(y)*z)
# define _ ButtonPressMask|ButtonReleaseMask # define _ ButtonPressMask|ButtonReleaseMask
# define I( x,y) XSelectInput(d,s[x],y) # define I( x,y) XSelectInput(d,s[x],y)
@@ -24,9 +24,9 @@
v){l=!(e &32)*9; for(w=30; w--;){ if(s[w+2]){ k=w% v){l=!(e &32)*9; for(w=30; w--;){ if(s[w+2]){ k=w%
10;l=e&32?k>l?k:l:k<l?k:l;X(w+2,10+k *20+q,(w/10)*20+e);}}l*=20; 10;l=e&32?k>l?k:l:k<l?k:l;X(w+2,10+k *20+q,(w/10)*20+e);}}l*=20;
e&31?e++:e&32?(q++,(q+l>300?e++:0)):(q--,q+l<10?e++:0); } u>140? e&31?e++:e&32?(q++,(q+l>300?e++:0)):(q--,q+l<10?e++:0); } u>140?
ext(c):u>-20?X(32,i,u-=(v?5:-1 )) :(!v?(u =5,srandom(time(0)),i= exit(c):u>-20?X(32,i,u-=(v?5:-1 )):(!v?(u =5,srandom(time(0)),i=
random()%88+6):0); } else{k>0?i+=(i>105?k= -k:k):(i+=(i<1?k= -k: random()%88+6):0); } else{k>0?i+=(i>105?k= -k:k):(i+=(i<1?k= -k:
k)); j>0?u +=(u>140?ext(++c):j):(u+=(u <1?j= -j:j k)); j>0?u +=(u>140?exit(++c):j):(u+=(u <1?j= -j:j
)); X( 52,i,u);}XSync(d,0); while( )); X( 52,i,u);}XSync(d,0); while(
XCheckMaskEvent(d ,_|V,&r))if(!(r. type==ButtonPress XCheckMaskEvent(d ,_|V,&r))if(!(r. type==ButtonPress
?(E&1||!x||!v?t=E*4-8 :u<0?i=y+4,u=122:1):r ?(E&1||!x||!v?t=E*4-8 :u<0?i=y+4,u=122:1):r
@@ -36,4 +36,4 @@
XUnmapWindow (d,s[w]) ,s[w]=0,c--: XUnmapWindow (d,s[w]) ,s[w]=0,c--:
0,l=1; if(!x&&l)j= -j,l=0; if(l&x&&!v 0,l=1; if(!x&&l)j= -j,l=0; if(l&x&&!v
)u=~19,c--,l=0;t=(!x||!v) &&(y<5&&t<0||y>95&&t>0)?0 )u=~19,c--,l=0;t=(!x||!v) &&(y<5&&t<0||y>95&&t>0)?0
:t;s[1]?X(1,y+=x&v?t:t/(x+1),130):ext(++c);usleep(Z);};return(c);} :t;s[1]?X(1,y+=x&v?t:t/(x+1),130):exit(++c);};return(c);}
+2
View File
@@ -1,5 +1,7 @@
fanf fanf
fanftmp1.c fanftmp1.c
fanftmp1
fanftmp2.c fanftmp2.c
fanftmp2
fanf.orig fanf.orig
prog.orig prog.orig
+4 -2
View File
@@ -130,8 +130,10 @@ all: data ${TARGET}
sandwich supernova deep_magic magic charon pluto sandwich supernova deep_magic magic charon pluto
${PROG}: ${PROG}.c ${PROG}: ${PROG}.c
@echo "This next step may take some time to complete:" @echo "This may take some time to complete:"
${CC} ${CFLAGS} ${PROG}.c ${LDFLAGS} -o $@ ${CC} ${PROG}.c -E > ${PROG}tmp1.c
${CC} ${PROG}tmp1.c -E > ${PROG}tmp2.c
${CC} ${CFLAGS} ${PROG}tmp2.c ${LDFLAGS} -o $@
# alternative executable # alternative executable
# #
+46 -51
View File
@@ -4,7 +4,7 @@
make all make all
``` ```
NOTE: This may take a while. Some systems may have problems building\ NOTE: this may take a while. Some systems may have problems building
this entry because of the system resources it requires. this entry because of the system resources it requires.
@@ -15,36 +15,26 @@ this entry because of the system resources it requires.
``` ```
### To use: ### Try:
Enter an expression on standard input. Here are some Enter an expression on standard input. To try some we have selected:
sample expressions that you can use:
``` ```sh
((\a(\b(\c(d)))) e) ./try.sh
(\f\g\x
(
(g x) (f x)
) K K z
)
(\f(\f\g\x( (f((\a(g(b))) e)) (g x) ) K K z))
(Y\f\n
((= n 0)
1
(* n (f (- n 1)))
)
)
``` ```
## Judges' remarks: ## Judges' remarks:
### Historical aside:
At a time the code in [fanf.c](fanf.c) was that of [fanf.orig.c](fanf.orig.c)
but to get this to compile in modern systems it had to be translated to what you
now see. The intermediate steps can still be performed but they might be
different from the past. This should be kept in mind as you read the below
remarks.
This program translates lambda expressions into combinator This program translates lambda expressions into combinator
expressions. But you do not need to know Lambda Calculus to be expressions. But you do not need to know Lambda Calculus to be
impressed by this program! impressed by this program!
@@ -56,20 +46,21 @@ Notice how large the code grows from the [fanf.c](fanf.c) into the final
`fanftmp2.c` C program. Take a look at that final C program again, `fanftmp2.c` C program. Take a look at that final C program again,
can you begin to understand what it is doing? can you begin to understand what it is doing?
Look at the first stage of the C pre-processing: Look at the first stage of the C pre-processing which was done like:
```sh ```sh
cc fanf.c -E > fanftmp1.c cc fanf.c -E > fanftmp1.c
``` ```
Skip to the bottom of `fanftmp1.c` (after all of the `#include` header Skip to the bottom of `fanftmp1.c` (after all of the C pre-processor header
stuff ... look for a line of the form: `# 2 "fanf.c"` near the bottom) stuff ... look for a line of the form: `# 38 "fanf.c"` near the bottom)
and look at the resulting code. This code, when C pre-processed and look at the resulting code. This code, when C pre-processed
will expand into over 342 times the original code (ignoring `#include` will expand into over 342 times the original code (ignoring `#include`
headers) to produce `fanftmp2.c`. That program in turn, when compiled headers) to produce `fanftmp2.c`. That program in turn, when compiled
translates a single lambda expression on standard input into translates a single lambda expression on standard input into
combinator expressions on standard output. combinator expressions on standard output.
### Extra credit question: ### Extra credit question:
What do you have to do to make this program work with an old non-ANSI C What do you have to do to make this program work with an old non-ANSI C
@@ -88,7 +79,7 @@ for translating lambda expressions into combinator expressions.
The syntax of lambda expressions recognised by the program is as The syntax of lambda expressions recognised by the program is as
follows: The basic atoms are variables which are single characters other follows: The basic atoms are variables which are single characters other
than backslash or parentheses, e.g. `a` or `b` or `@`. Variables are than backslash or parentheses, e.g. `a` or `b` or `@`. Variables are
combined by function application which is written e.g. `(f a)` which is combined by function application which is written as e.g. `(f a)` which is
the function `f` applied to the argument `a`. Application groups to the the function `f` applied to the argument `a`. Application groups to the
left, so `(f a b c)` is equivalent to `(((f a) b) c)`. Functions are left, so `(f a b c)` is equivalent to `(((f a) b) c)`. Functions are
created from lambda terms which are written in the form `\a(expr)` which created from lambda terms which are written in the form `\a(expr)` which
@@ -131,30 +122,34 @@ The algorithm for translating lambda expressions into combinator
expressions works as follows. There are three forms of lambda expression expressions works as follows. There are three forms of lambda expression
to consider: variables, applications, and abstractions. to consider: variables, applications, and abstractions.
trans `v` -> `v` (variable) ```
trans `a b` -> (trans `a`) (trans `b`) (application) trans v -> v (variable)
trans `\ab` -> abs a (trans `b`) (abstraction) trans a b -> (trans a) (trans b ) (application)
trans \ab -> abs a (trans b) (abstraction)
```
There are a further three cases to consider for the body of lambda There are a further three cases to consider for the body of lambda
expressions, where we need to do the magic that transforms away the expressions, where we need to do the magic that transforms away the
variables. variables.
abs a `f x` -> `S` (abs a `f`) (abs a `x`) ```
abs a `b` -> `K b` (b != a) abs a f x -> S (abs a f) (abs a x)
abs a `a` -> `I` abs a b -> K b (b != a)
abs a a -> I
```
E.g. suppose we had combinator expressions for `+` and `3` and we wanted E.g. suppose we had combinator expressions for `+` and `3` and we wanted
to see what the combinator expression for doubling 3 looked like: to see what the combinator expression for doubling 3 looked like:
``` ```
trans `\x(+ x x) 3` trans \x(+ x x) 3
-> (trans `\x(+ x x)`) (trans `3`) -> (trans \x(+ x x)) (trans 3)
-> (abs x (trans `+ x x`)) `3` -> (abs x (trans + x x)) 3
-> (abs x `+ x x`) `3` -> (abs x + x x) 3
-> `S` (abs x `+ x`) (abs x `x`) `3` -> S (abs x + x) (abs x x) 3
-> `S` (`S` (abs x `+`) (abs x `x`)) `I` `3` -> S (S (abs x +) (abs x x)) I 3
-> `S` (`S` (`K +`) `I`) `I` `3` -> S (S (K +) I) I 3
-> `S (S (K +) I) I 3` -> S (S (K +) I) I 3
``` ```
We can then check that this evaluates to the expected result: We can then check that this evaluates to the expected result:
@@ -176,7 +171,7 @@ same as `S (K a) (K b)`, because
-> a b -> a b
``` ```
and and
``` ```
S (K a) (K b) x S (K a) (K b) x
@@ -220,7 +215,7 @@ It is fairly well known that the lambda calculus (and hence SK
combinators) can compute anything, but mere computation is no use if you combinators) can compute anything, but mere computation is no use if you
cannot communicate with the world. This is why `OFL` includes a few cannot communicate with the world. This is why `OFL` includes a few
concessions to reality: a combinator `E` for representing characters and concessions to reality: a combinator `E` for representing characters and
testing them for equality, two IO combinators, `G` and `P`, for reading testing them for equality, two I/O combinators, `G` and `P`, for reading
and writing characters respectively, and a combinator `J` for and writing characters respectively, and a combinator `J` for
representing `false`. I also implemented the `Y` combinator directly representing `false`. I also implemented the `Y` combinator directly
rather than in terms of the primitive combinators, since it only rather than in terms of the primitive combinators, since it only
@@ -239,7 +234,7 @@ the expression evaluates to `J`.
`K` and `J` are used to represent `true` and `false` respectively; they `K` and `J` are used to represent `true` and `false` respectively; they
correspond exactly to the standard lambda calculus representations of correspond exactly to the standard lambda calculus representations of
true and false, viz. `\t\f(t)` and `\t\f(f)` respectively. In this way a `true` and `false`, viz. `\t\f(t)` and `\t\f(f)` respectively. In this way a
conditional expression can just be written `cond then else`, which is conditional expression can just be written `cond then else`, which is
even more terse than C `cond ? then : else`. An expression that even more terse than C `cond ? then : else`. An expression that
compares a character with `a`, say, can be written in the rather compares a character with `a`, say, can be written in the rather
@@ -247,7 +242,7 @@ obfuscated form `E('a') char then else`.
The `P` combinator, like the `E` combinator, starts off by evaluating The `P` combinator, like the `E` combinator, starts off by evaluating
its first argument and checking that it is some form of `E`. It then its first argument and checking that it is some form of `E`. It then
writes that character on stdout, and finally it calls its second writes that character on `stdout`, and finally it calls its second
argument as a function with the argument `I`, i.e. argument as a function with the argument `I`, i.e.
``` ```
@@ -256,7 +251,7 @@ P x f -> f I
with a side-effect. with a side-effect.
The `G` combinator just reads a character from stdin then calls its The `G` combinator just reads a character from `stdin` then calls its
argument as a function with the appropriate `E` combinator as an argument as a function with the appropriate `E` combinator as an
argument, i.e. argument, i.e.
@@ -268,12 +263,12 @@ with a side-effect.
One of the problems with non-strict languages is that it is hard to One of the problems with non-strict languages is that it is hard to
predict in advance in which order side-effects will occur. In my program predict in advance in which order side-effects will occur. In my program
I used a monadic IO structure like that used by the programming language I used a monadic I/O structure like that used by the programming language
Haskell; this allows one to write programs that manipulate external Haskell; this allows one to write programs that manipulate external
state in a manner remarkably similar to imperative programming languages state in a manner remarkably similar to imperative programming languages
like C. An examination of my program should provide an illustration of like C. An examination of my program should provide an illustration of
how well this technique works in practice. (In particular, I used the how well this technique works in practice. (In particular, I used the
CPS form of the IO monad from page 6 of "Imperative Functional CPS form of the I/O monad from page 6 of "Imperative Functional
Programming".) Programming".)
The `Y` combinator is used for implementing recursive functions. It has The `Y` combinator is used for implementing recursive functions. It has
@@ -342,8 +337,8 @@ of the program that is then interpreted.
The first pass of the compiler is implemented via the C preprocessor. It The first pass of the compiler is implemented via the C preprocessor. It
implements one optimisation, namely function in-lining. Unfortunately, implements one optimisation, namely function in-lining. Unfortunately,
this optimisation always increases object code size and execution time. this optimisation always increases object code size and execution time.
The opposite optimisation, common sub-expression elimination, (which The opposite optimisation, common sub-expression elimination (which
would offer improvements in code size and execution time) has not been would offer improvements in code size and execution time), has not been
implemented. The output of this pass is a C program that may be compiled implemented. The output of this pass is a C program that may be compiled
by a normal C compiler. by a normal C compiler.
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
make all || exit 1
echo "$ echo '((\a(\b(\c(d)))) e' | ./fanf" 1>&2
echo '((\a(\b(\c(d)))) e)' | ./fanf
echo 1>&2
echo "$ echo '
(\f\g\x
(
(g x) (f x)
) K K z
)'|./fanf" 1>&2
echo '
(\f\g\x
(
(g x) (f x)
) K K z
)'|./fanf
echo 1>&2
echo "$ echo '(\f(\f\g\x( (f((\a(g(b))) e)) (g x) ) K K z))' | ./fanf" 1>&2
echo '(\f(\f\g\x( (f((\a(g(b))) e)) (g x) ) K K z))' | ./fanf
echo 1>&2
echo "$ echo '
(Y\f\n
((= n 0)
1
(* n (f (- n 1)))
)
)' | ./fanf" 1>&2
echo '(Y\f\n
((= n 0)
1
(* n (f (- n 1)))
)
)' | ./fanf
+6 -6
View File
@@ -56,7 +56,7 @@ This is a beautiful program. How can a program with no conditional (except for
the fixes for modern systems) behavior at all have output which the fixes for modern systems) behavior at all have output which
depends on anything? depends on anything?
For hints on deciphering this, see below past the Author's Comments; For hints on deciphering this, see below past the Author's remarks;
they're a nice summary, but they leave the mystery intact. they're a nice summary, but they leave the mystery intact.
BTW: the author notes an incredible lipogram story called BTW: the author notes an incredible lipogram story called
@@ -71,9 +71,9 @@ In literary circles, there is a poetic form called a "lipogram",
which is a poem in which a specific letter has been distinctly which is a poem in which a specific letter has been distinctly
avoided. This principle can be applied in other ways. For avoided. This principle can be applied in other ways. For
instance, in this paragraph, the vowel which immediately follows instance, in this paragraph, the vowel which immediately follows
't' in the alphabet is nowhere to be seen. There is even an `t` in the alphabet is nowhere to be seen. There is even an
entire novel (E. V. Wright's "Gadsby") in which no word contains entire novel (E. V. Wright's "Gadsby") in which no word contains
the letter 'e'. the letter `e`.
The IOCCC has been no stranger to this concept. Several winning entries from The IOCCC has been no stranger to this concept. Several winning entries from
years past have accomplished interesting feats while completely avoiding the use years past have accomplished interesting feats while completely avoiding the use
@@ -85,12 +85,12 @@ In continuing with this fine tradition, this program suggests to the ANSI
committee some new practical simplifications for the C language. For instance, committee some new practical simplifications for the C language. For instance,
it appears that CONDITIONAL BRANCHING isn't really necessary. So, we can do it appears that CONDITIONAL BRANCHING isn't really necessary. So, we can do
away with `while`, `do...while`, `for`, `if`/`else`, `switch`, `?:`, or anything away with `while`, `do...while`, `for`, `if`/`else`, `switch`, `?:`, or anything
else that might cause any kind of nondeterministic jump in the code. There's else that might cause any kind of non-deterministic jump in the code. There's
too much uncertainty in the world anyway. too much uncertainty in the world anyway.
Function parameters and return types just confuse people anyway, so Function parameters and return types just confuse people anyway, so
get rid of them. Same with local variables. In fact, all you really get rid of them. Same with local variables. In fact, all you really
need are assignment statements, and maybe one `printf()` and one `goto`. need are assignment statements, and maybe one `printf(3)` and one `goto`.
This style of programming is also ideal for systems with limited This style of programming is also ideal for systems with limited
memory capacity, since the function call stack never goes beyond memory capacity, since the function call stack never goes beyond
@@ -103,7 +103,7 @@ line, such as
./schnitzi 5 ./schnitzi 5
``` ```
The output will itself be a lipographic program, one which inputs The output will itself be a lipographic program, one which expects input of
(in this case) five numbers and prints them out sorted. It sorts (in this case) five numbers and prints them out sorted. It sorts
them, however, using only `if/else` statements, without arrays or them, however, using only `if/else` statements, without arrays or
looping. To see the resulting program run, redirect the output from looping. To see the resulting program run, redirect the output from
+48 -96
View File
@@ -5,8 +5,9 @@ make all
``` ```
There is an alternate version of this entry that will work with macOS. See the There is an alternate version of this entry that will work with macOS. See the
[Alternate code](#alternate-code) section below for details on how it works and [Alternate code](#alternate-code) section below. We recommend you look at it
how to use it. even if you don't have a Mac, unless you want to figure it out yourself, as it
includes some interesting details about the entry.
## To use: ## To use:
@@ -15,9 +16,6 @@ how to use it.
./schweikh1 ./schweikh1
``` ```
NOTE: a limitation with the entry was that it required `gcc` but this limitation
has been removed to make it more portable, requiring only `cc`.
## Alternate code: ## Alternate code:
@@ -25,83 +23,25 @@ As noted above this entry will not work as it stands for macOS and there are
some important notes as well as a description of how the fixed version works some important notes as well as a description of how the fixed version works
(the details of which are relevant to the original entry that no longer works as (the details of which are relevant to the original entry that no longer works as
well as the fixed version but are described in the context of the macOS well as the fixed version but are described in the context of the macOS
adjustments). adjustments). For details of what had to change, see [macos.md](macos.md).
Unless you wish to figure it out yourself, we recommend that you read this even
if you don't have a Mac as it has some interesting details about the entry.
#### macOS changes:
As an aside: if you have a Mac with the Apple chip, some of the header files ### Alternate build:
will report an unsupported architecture and unsupported compiler (error,
warning). This will not make the program abort, however, but even with an Intel
CPU though, the original entry will not work in macOS as it stands because
`/usr/include` is hard-coded in the code and macOS does not have it.
Nevertheless, although some of it would not work in other systems where
`/usr/include` exists, the `#define`s would still be found okay, at least until
a file was not found (this limitation was removed in both versions, see
[thanks-for-fixes.md](/thanks-for-fixes.md) for details).
However, as noted, macOS does **not** have `/usr/include` so this would not ever
work for macOS without some changes, described next. For macOS you will need the
command line tools installed which can be done like:
```sh
sudo xcode-select --install
```
An important point is that the original version hard-coded `gcc` but just like
the entry, the alternate version has also removed this limitation despite `gcc`
existing (though it's `clang`) in macOS.
Now as far as how this works if you look at the code of
[schweikh1.c](schweikh1.c), on line 55 you'll see a funny command (this goes for
the alternate version as well but a bit different).
Now the key is two magic numbers, one (two of this exist) on a different line shortly below the
string and the other a couple lines further below.
In the original the numbers are, respectively, 44 and 46, but with the gcc
limitation removed the numbers now are 43 and 45. But how does this work? Well
the command is:
```
"0gcc -ansi -E -dM -undef %s /usr/include/%s>r\0 ("
```
which has been changed to be:
```
"0cc -ansi -E -dM -undef %s /usr/include/%s>r\0 ("
```
Shortly below that you will see, as noted above, the numbers:
```c
if ((H = fopen (__FILE__+43, 43+__FILE__)))
while ((fgets (L, (int)sizeof L, H)) != 0) {
I[strcspn (I, 45+__FILE__)] = O = 0;
```
The first number in the above C means the length starting from 0 up through the
`>`. The second number is the same starting point but up through the `\0` which
is why it's `+2`. But what happens if only the first number is updated (from the
original)? Most of the output will be just `#define` by itself; in the cases
where there was text after that it was macros that certainly were not defined.
There might have been other errors as well.
Changing this for macOS allows for opening the right files; the problem with the
macOS is `/usr/include` does not exist: instead it's
`/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include` which is why
the different command line and the different numbers. The change of the numbers
can be found in the alternate code and the explanation is the same.
To use this version use:
```sh ```sh
make alt make alt
``` ```
### Alternate build:
Use `schweikh1.alt` as you would `schweikh1` above. Use `schweikh1.alt` as you would `schweikh1` above.
### Alternate try:
Bonus exercise: modify the code to provide a different compiler. Bonus exercise: modify the code to provide a different compiler.
@@ -123,6 +63,7 @@ Do not read them if you want to figure this out yourself. "Amendment
One" refers to "NA1", the add-on to C89 which added some fairly One" refers to "NA1", the add-on to C89 which added some fairly
crufty internationalization support. crufty internationalization support.
#### Historical note: #### Historical note:
Some non-gcc compilers that were not fully ANSI standard did not Some non-gcc compilers that were not fully ANSI standard did not
@@ -130,7 +71,8 @@ compile this entry correctly. Using cc by default was not helpful
most of the time on this entry, because the program had a hardcoded most of the time on this entry, because the program had a hardcoded
gcc invocation anyway. Anyone who uses egcs and has no plain gcc gcc invocation anyway. Anyone who uses egcs and has no plain gcc
will need to frob the source anyway and can be expected to do the will need to frob the source anyway and can be expected to do the
right thing with `${CC}`. So one should have used gcc. right thing with `${CC}`. This limitation was removed in 2023 but in the past
one should have used gcc.
## Author's remarks: ## Author's remarks:
@@ -162,16 +104,17 @@ Bug your vendor for an upgrade.
The format of the info file is straightforward and described in the file The format of the info file is straightforward and described in the file
itself. Just load it in your favorite editor. itself. Just load it in your favorite editor.
### Requirements ### Requirements
`gcc` (the GNU C compiler) must be available at runtime. It is assumed that your `gcc` (the GNU C compiler) must be available at runtime. It is assumed that your
C implementation keeps headers as files in the /usr/include directory. The info C implementation keeps headers as files in the `/usr/include` directory. The
file must be readable and reside in the current working directory. The current [info](info) file must be readable and reside in the current working directory.
working directory must be writable in order to create a temporary file (which is The current working directory must be writable in order to create a temporary
removed upon program termination). In case you don't have gcc at runtime, not file (which is removed upon program termination). In case you don't have gcc at
all is lost if your compiler or preprocessor can produce a list of defined runtime, not all is lost if your compiler or preprocessor can produce a list of
macros in the format output by `gcc -dM`, i.e. lines of the form `#define MACRO defined macros in the format output by `gcc -dM`, i.e. lines of the form
value`. `#define MACRO value`.
Edit the source at line 55 in this case. Edit the source at line 55 in this case.
@@ -180,7 +123,7 @@ Edit the source at line 55 in this case.
ISO C has a very strict idea of visibility of identifiers. All possible ISO C has a very strict idea of visibility of identifiers. All possible
macros are explicitly enumerated. In a compliant implementation no other macros are explicitly enumerated. In a compliant implementation no other
macros can be defined, because you could write strictly conforming macros can be defined, because you could write strictly conforming
programs that may fail to compile due to syntax errors: suppose programs that may fail to compile due to syntax errors: supposing that
`<stdio.h>` defines `PIPE_BUF`, then the conforming `<stdio.h>` defines `PIPE_BUF`, then the conforming
```c ```c
@@ -200,6 +143,7 @@ int main (void)
is expected to compile and meet the assertion. If it does not, your is expected to compile and meet the assertion. If it does not, your
compiler compiles some other language than ISO C. compiler compiles some other language than ISO C.
### Why I think my program is obfuscated ### Why I think my program is obfuscated
Not only is the program's purpose a thorough standard conformance Not only is the program's purpose a thorough standard conformance
@@ -232,7 +176,7 @@ C compilers.
``` ```
To be honest, I don't know if the rules for pp-tokens and token To be honest, I don't know if the rules for pp-tokens and token
pasting don't forbid what I do (and tcc is correct in rejecting it). pasting don't forbid what I do (and thus tcc is correct in rejecting it).
In this case, all other compilers I tried are buggy, or the Standard In this case, all other compilers I tried are buggy, or the Standard
itself :-) itself :-)
@@ -263,21 +207,21 @@ foo\0bar 9 tcc 4.1.2, Sunsoft cc turn "\0" into "\\0", ugh!
3. The `%:` and `%:%:` digraphs test for conformance to Amendment One. 3. The `%:` and `%:%:` digraphs test for conformance to Amendment One.
Ask your local guru if C allows the same case label in the same switch Ask your local guru if C allows the same case label in the same switch
statement to appear more than once. Ask him/her to think real hard. statement to appear more than once. Ask him/her to think real hard.
The answer will be "no". The true guru will cite a constraint in The answer will be "no". The true guru will cite a constraint in
ISO 6.6.4.2. Then make fun of the guru's answer by waving `case __LINE__:` ISO 6.6.4.2. Then make fun of the guru's answer by waving `case __LINE__:`
under his/her nose. Easy money from a bet! Make sure your guru has under his/her nose. Easy money from a bet! Make sure your guru has
no chance to use Standardese weasel words as an escape: can I have no chance to use Standardese weasel words as an escape: can I have
```c ```c
case <some_token>: case <some_token>:
case <some_token>: case <some_token>:
``` ```
in the same switch? (Note that you need the [invisible] newline. You in the same switch? (Note that you need the [invisible] newline. You
can generously allow the additional constraint that no `#undef`s or can generously allow the additional constraint that no `#undef`s or
`#define`s are allowed between the two cases.) `#define`s are allowed between the two cases.)
Lots of integer constants and string literals come into the source Lots of integer constants and string literals come into the source
via `__LINE__` and `__FILE__` which are redefined at various places. via `__LINE__` and `__FILE__` which are redefined at various places.
@@ -289,7 +233,7 @@ This makes the source and header input depend on ASCII.
A few old obfuscations, like one character identifiers, not too A few old obfuscations, like one character identifiers, not too
many macros, a `goto O`, "needless" assignments to satisfy lint many macros, a `goto O`, "needless" assignments to satisfy lint
with its "function value ignored" warnings. My lint has nothing with its "function value ignored" warnings. My lint has nothing
to complain. to complain about.
There's more whitespace in the Standard than just space, tab and newline. In There's more whitespace in the Standard than just space, tab and newline. In
particular, there are vertical tab and form-feed that can be used in certain particular, there are vertical tab and form-feed that can be used in certain
@@ -303,13 +247,14 @@ printer when you make a hard copy of the source...
While I'm at it, the rules state that only space, tab and newline While I'm at it, the rules state that only space, tab and newline
are ignored for the count (plus '`{`', '`}`', '`;`' followed by are ignored for the count (plus '`{`', '`}`', '`;`' followed by
whitespace). The mkentry.c program, however, uses `isspace()` which whitespace). The [mkentry.c](../mkentry.c) program, however, uses `isspace(3)` which
returns nonzero for vtab and form-feed and other characters as well. returns nonzero for `\v` and `\f` and other characters as well.
I could have used a lot more ^K and ^L probably undetected by your I could have used a lot more `^K` and `^L` probably undetected by your
counter but decided to err on the side of safety. I use a perl counter but decided to err on the side of safety. I use a perl
script to compute the character count according to the rules. script to compute the character count according to the rules.
The advantages of an independent clean room approach... The advantages of an independent clean room approach...
```perl ```perl
#!/usr/bin/perl -w #!/usr/bin/perl -w
$/ = "\0"; $/ = "\0";
@@ -320,10 +265,17 @@ print length ($_),"\n";
``` ```
To use, try:
```sh
perl ./charcount.pl schweikh1.c
```
I have tried, as suggested in the guidelines, to let the code look I have tried, as suggested in the guidelines, to let the code look
like ordinary C code. Apart from a few long lines I think I left like ordinary C code. Apart from a few long lines I think I left
the indentation like I would in RL. You should however not try to the indentation like I would in RL. You should however not try to
use indent on the source. The code is extremely fragile because of the use `indent` on the source. The code is extremely fragile because of the
myriads of `__LINE__` macros -- indenting is a sure way to break myriads of `__LINE__` macros -- indenting is a sure way to break
the program. Don't even think of maintaining that beast; I've the program. Don't even think of maintaining that beast; I've
had my share of core dumps during development :-) A test suite had my share of core dumps during development :-) A test suite
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/perl -w
$/ = "\0";
$_ = <>;
s/[;{}][ \t\n]/\n/g;
s/[ \t\n]+//g;
print length ($_),"\n";
+70
View File
@@ -0,0 +1,70 @@
This document describes how this was fixed to work with macOS as well as some of
the magic in the entry. Even if you don't have a Mac it might be worth your time
reading especially as some of it details some important fixes and changes in the
original entry as well.
# macOS changes
As an aside: if you have a Mac with the Apple chip, some of the header files
will report an unsupported architecture and unsupported compiler (error,
warning). This will not make the program abort, however, but even with an Intel
CPU though, the original entry will not work in macOS as it stands because
`/usr/include` is hard-coded in the code and macOS does not have it.
Nevertheless, although some of it would not work in other systems where
`/usr/include` exists, the `#define`s would still be found okay, at least until
a file was not found (this limitation was removed in both versions in 2023). see
However, as noted, macOS does **not** have `/usr/include` so this would never
work for macOS without some changes, described next. For macOS you will need the
command line tools installed which can be done like:
```sh
sudo xcode-select --install
```
An important point is that the original version hard-coded `gcc` but just like
the entry, the alternate version has also removed this limitation despite `gcc`
existing (though it's `clang`) in macOS.
Now as far as how this works if you look at the code of
[schweikh1.c](schweikh1.c), on line 55 you'll see a funny command (this goes for
the alternate version as well but a bit different).
Now the key is two magic numbers, one (two times) on a different line shortly below the
string and the other a couple lines further below.
In the original the numbers are, respectively, `44` and `46`, but with the gcc
limitation removed the numbers now are `43` and `45`. But how does this work? Well
the command is:
```
"0gcc -ansi -E -dM -undef %s /usr/include/%s>r\0 ("
```
which has been changed to be:
```
"0cc -ansi -E -dM -undef %s /usr/include/%s>r\0 ("
```
Shortly below that you will see, as noted above, the numbers:
```c
if ((H = fopen (__FILE__+43, 43+__FILE__)))
while ((fgets (L, (int)sizeof L, H)) != 0) {
I[strcspn (I, 45+__FILE__)] = O = 0;
```
The first number in the above C means the length starting from 0 up through the
`>`. The second number is the same starting point but up through the `\0` which
is why it's `+2`. But what happens if only the first number is updated (from the
original)? Most of the output will be just `#define` by itself; in the cases
where there was text after that it was macros that certainly were not defined.
There might have been other errors as well.
Changing this for macOS allows for opening the right files; the problem with
macOS is `/usr/include` does not exist: instead it's
`/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include` which is why
the different command line and the different numbers. The change of the numbers
can be found in the alternate code and the explanation is the same.
+10 -7
View File
@@ -89,6 +89,7 @@ to trigger this error.
With the proper text beyond the `\012`, it was even possible to have all kinds With the proper text beyond the `\012`, it was even possible to have all kinds
of fun adding inline assembly via `#line` directives. :-) of fun adding inline assembly via `#line` directives. :-)
#### For extra fun and credit: #### For extra fun and credit:
If your compiler had this bug, you could transform the line in the C program to If your compiler had this bug, you could transform the line in the C program to
@@ -136,6 +137,7 @@ and now it works with both clang and gcc.
## Author's remarks: ## Author's remarks:
### What this program does ### What this program does
My entry is a `yarng`, which stands for, you guessed it, yet another My entry is a `yarng`, which stands for, you guessed it, yet another
@@ -147,7 +149,7 @@ non-deterministic factors. Here's how it works:
1. an alarm is scheduled to be delivered in a second 1. an alarm is scheduled to be delivered in a second
2. an infinite loop is entered that just increments a counter 2. an infinite loop is entered that just increments a counter
3. on delivery of the alarm signal, the counter's LSB is printed. 3. on delivery of the alarm signal, the counter's LSB is printed.
4. while some other counter is not zero, go to 1) 4. while some other counter is not zero, go to step (1)
5. exit 5. exit
The `yarng` therefore produces output at a rate of 1 baud. The number of The `yarng` therefore produces output at a rate of 1 baud. The number of
@@ -175,7 +177,7 @@ impossible, like in the `int main` declaration I have `int` split across two
lines. All indent programs I use insert space at the beginning of the lines. All indent programs I use insert space at the beginning of the
continuation line and thereby introduce a syntax error. continuation line and thereby introduce a syntax error.
- `main` (ab)used as a signal handler. Shocking. Yeah, this leads to undefined - `main()` (ab)used as a signal handler. Shocking. Yeah, this leads to undefined
behaviour. I have yet to see a system where it does not work. If it doesn't work behaviour. I have yet to see a system where it does not work. If it doesn't work
for you, I'd appreciate if you mail me about it. Uh-oh, Schweikh did it, for you, I'd appreciate if you mail me about it. Uh-oh, Schweikh did it,
uttered "Works for me". Don't quote me on that! uttered "Works for me". Don't quote me on that!
@@ -190,7 +192,7 @@ lame and I came up with `??=line 10 ONE(O(1,1,2,6,0,6))`.
- `__LINE__` is used as the signal number argument to `signal()`. If indenting or - `__LINE__` is used as the signal number argument to `signal()`. If indenting or
other editing changes the number of lines the program is likely to be killed other editing changes the number of lines the program is likely to be killed
after one second... Thanks to POSIX for specifying SIGALRM as 14. You're SOL if after one second... Thanks to POSIX for specifying `SIGALRM` as `14`. You're SOL if
your system does not conform. Get a real OS. your system does not conform. Get a real OS.
- All strings (or characters) needed are crammed into `__FILE__` using octal - All strings (or characters) needed are crammed into `__FILE__` using octal
@@ -205,14 +207,15 @@ and as a plain identifier in `main`'s parameters. The rule is that whenever
`ONE` is followed by `(` it must be the macro. `ONE` is followed by `(` it must be the macro.
- A quote from Kernighan and Plauger, _The Elements of Programming Style_, 2nd - A quote from Kernighan and Plauger, _The Elements of Programming Style_, 2nd
Ed., page 21: "A useful way to decide if some piece of code is clear or not is Ed., page 21: `"A useful way to decide if some piece of code is clear or not is
the 'telephone test.' If someone could understand your code when read aloud the 'telephone test.' If someone could understand your code when read aloud
over the telephone, it's clear enough. If not then it needs rewriting." Now try over the telephone, it's clear enough. If not then it needs rewriting."` Now try
the telephone test for 'one = zero = atoi (ONE[zero-1])'. the telephone test for 'one = zero = atoi (ONE[zero-1])'.
- Don't you love the aesthetics of symmetry: `write (1, 1+__FILE__+1, 1)` - Don't you love the aesthetics of symmetry: `write (1, 1+__FILE__+1, 1)`
(telephone test this as well; is it 1, One, ONE, one? Maybe won?). (telephone test this as well; is it 1, One, ONE, one? Maybe won?).
### Where does the randomness come from? ### Where does the randomness come from?
Each bit obviously depends on the number of times the loop body is Each bit obviously depends on the number of times the loop body is
@@ -221,7 +224,7 @@ this number?
- The accuracy of the clock driving the CPU. - The accuracy of the clock driving the CPU.
- The accuracy of the clock driving the timer responsible for alarm signals. - The accuracy of the clock driving the timer responsible for alarm signals.
- On multitasking OS's: The total time the process is scheduled to run between - On multitasking OSes: The total time the process is scheduled to run between
successive invocations of the signal handler. successive invocations of the signal handler.
How about the randomness of the `yarng`? To measure these properties I How about the randomness of the `yarng`? To measure these properties I
@@ -230,7 +233,7 @@ expect from a generator with uniform distribution (all sequences
equally likely to occur). equally likely to occur).
It is interesting to note that `yarng`'s maximum deviations are less It is interesting to note that `yarng`'s maximum deviations are less
than or equal to `rand()`'s. Randomness however tends to degrade under than or equal to `rand(3)`'s. Randomness however tends to degrade under
high load averages on my system (Linux). This could be avoided by high load averages on my system (Linux). This could be avoided by
allowing the process one second of execution time instead of wall allowing the process one second of execution time instead of wall
clock time (using the `SIGVTALRM` sent by the virtual timer clock time (using the `SIGVTALRM` sent by the virtual timer
+1 -1
View File
@@ -58,7 +58,7 @@ ARCH=
# Defines that are needed to compile # Defines that are needed to compile
# #
CDEFINE= -DM0=sizeof -DM1=long -DM2=void \ CDEFINE= -DM0=sizeof -DM1=long -DM2=void \
-DM3=realloc -DM4=calloc -DM5=free -DM3=realloc -DM4=calloc -DM5=free -DSZ=32767
# Include files that are needed to compile # Include files that are needed to compile
# #
+60 -46
View File
@@ -4,10 +4,29 @@
make all make all
``` ```
The author stated:
```
In the remote event that the input has more than `8192` files with
the same size (on systems where `sizeof (char *) == 4`, or `4096` when
`sizeof (char *) == 8`), increase the manifest constant 32767 on line
31.
```
so we changed the constant to be `SZ` defined in the Makefile. To change the
value you can do something like:
```sh
make clobber CDEFINE="-DM0=sizeof -DM1=long -DM2=void \
-DM3=realloc -DM4=calloc -DM5=free -DSZ=55555" all
```
or whatever you wish to redefine it to.
## To use: ## To use:
If "dir" is the directory (or a directory tree) where you keep If `dir` is the directory (or a directory tree) where you keep
all your favorite pictures off the Internet or from all your favorite pictures off the Internet or from
`alt.binaries.pictures.*`, do `alt.binaries.pictures.*`, do
@@ -15,7 +34,7 @@ all your favorite pictures off the Internet or from
find dir -type f -print | ./samefile find dir -type f -print | ./samefile
``` ```
Maybe you will not need to buy another 10 Gb disk to store them. :-) Maybe you will not need to buy another 10 GB disk to store them. :-)
### Try: ### Try:
@@ -23,20 +42,14 @@ Maybe you will not need to buy another 10 Gb disk to store them. :-)
If you're in this winning entry's directory: If you're in this winning entry's directory:
```sh ```sh
find . -type f -print | ./samefile ./try.sh
``` ```
Notice that the tool finds a duplicate file, that of the man page. Notice that the tool finds some files that are duplicates.
## Judges' remarks: ## Judges' remarks:
Try this:
```sh
./try.sh
```
This program is not as smart as to detect identity of JPEG files with the same This program is not as smart as to detect identity of JPEG files with the same
picture but with different compression levels. On the other hand compression can picture but with different compression levels. On the other hand compression can
make a thing look/sound/feel different so these probably aren't duplicates make a thing look/sound/feel different so these probably aren't duplicates
@@ -56,7 +69,7 @@ Thank God the IEEE does not standardize a coding style...
### What this program does ### What this program does
This is best explained in the man page. The troff source for this This is best explained in the man page. The troff source for this
man page can be found in the file samefile.1. To render try: man page can be found in the file [samefile.1](samefile.1). To render try:
```sh ```sh
`man ./samefile.1 `man ./samefile.1
@@ -65,23 +78,24 @@ man page can be found in the file samefile.1. To render try:
Otherwise: Otherwise:
**SAMEFILE(1) User Manuals SAMEFILE(1)** ### SAMEFILE(1) User Manuals SAMEFILE(1)
**NAME** #### NAME
**samefile** - find identical files `samefile` - find identical files
**SYNOPSIS** #### SYNOPSIS
./samefile ```sh
./samefile
```
**DESCRIPTION** #### DESCRIPTION
`samefile` reads a list of file names (one file name per `samefile` reads a list of file names (one file name per
line) from `stdin`. For each file name pair with identical line) from `stdin`. For each file name pair with identical
contents, a line consisting of six tab separated fields is contents, a line consisting of six tab separated fields is
output: The size in bytes, two file names, the character output: The size in bytes, two file names, the character
`=` if the two files are on the same device, `X` otherwise, `=` if the two files are on the same device, `X` otherwise,
@@ -104,10 +118,10 @@ binary tree.
In the second stage all files having the same size are In the second stage all files having the same size are
compared against each other. The rules of mathematical compared against each other. The rules of mathematical
logic are applied to reduce work and output noise: if logic are applied to reduce work and output noise: if
files A, B, and C have the same size and samefile finds files `A`, `B`, and `C` have the same size and samefile finds
that A = B and A = C then it will not compare B against C that `A = B` and `A = C` then it will not compare `B` against `C`
(and will not output a line for B and C) but only for A = (and will not output a line for `B` and `C`) but only for `A =
B and A = C. The algorithm will detect equality across B` and `A = C`. The algorithm will detect equality across
arbitrarily long chains. Note however, that because only arbitrarily long chains. Note however, that because only
the first filename per inode gets into the first stage, the first filename per inode gets into the first stage,
the output for a group of identical files with different the output for a group of identical files with different
@@ -116,13 +130,13 @@ identical files of size 100 in an inode group consisting
of the three inodes with numbers 10, 20 and 30: of the three inodes with numbers 10, 20 and 30:
```sh ```sh
$ ls -i # output edited for readability: $ ls -i # output edited for readability:
10 file1 20 file4 30 file6 10 file1 20 file4 30 file6
10 file2 20 file5 10 file2 20 file5
10 file3 10 file3
$ ls | ./samefile $ ls | ./samefile
100 file1 file4 = 3 2 100 file1 file4 = 3 2
100 file1 file6 = 3 1 100 file1 file6 = 3 1
``` ```
The sum of the sizes in the first column is the amount of The sum of the sizes in the first column is the amount of
@@ -135,24 +149,24 @@ Note that it is not enough to just remove `file4` and `file6`
exists.) exists.)
**LIMITATIONS** #### LIMITATIONS
`samefile` was written with no limits in mind. The number of `samefile` was written with no limits in mind. The number of
input lines is unlimited. The size of the actual files is input lines is unlimited. The size of the actual files is
only limited by available virtual memory needed to compare only limited by available virtual memory needed to compare
one pair of files. The only hard limit is the constraint one pair of files. The only hard limit is the constraint
that there should not be more than about 8192 files having that there should not be more than about `8192` files having
the same size. Experience has shown that there are rarely the same size. Experience has shown that there are rarely
more than a couple dozen files of the same size. more than a couple dozen files of the same size.
**EXAMPLES** #### EXAMPLES
For everybody: For everybody:
What are the duplicates under my home directory? What are the duplicates under my home directory?
```sh ```sh
$ find $HOME | ./samefile find $HOME | ./samefile
``` ```
For the sysadmin folks: For the sysadmin folks:
@@ -160,23 +174,23 @@ For the sysadmin folks:
Report all duplicate files under /usr larger than 16k: Report all duplicate files under /usr larger than 16k:
```sh ```sh
$ find /usr -size +16384c -a -type f | ./samefile find /usr -size +16384c -a -type f | ./samefile
``` ```
For the ftp and WWW admins: For the ftp and WWW admins:
How much space is wasted below our site's /pub directory? How much space is wasted below our site's /pub directory?
```sh ```sh
$ find /pub -type f | samefile | awk '{sum += $1} END {print sum}' find /pub -type f | samefile | awk '{sum += $1} END {print sum}'
``` ```
**EXIT STATUS** #### EXIT STATUS
If `samefile` runs out of memory the exit status is 1, zero If `samefile` runs out of memory the exit status is 1, zero
otherwise. otherwise.
**SEE ALSO** #### SEE ALSO
`find(1)`, `ln(1)`, `rm(1)` `find(1)`, `ln(1)`, `rm(1)`
@@ -194,19 +208,19 @@ otherwise.
of files across arbitrarily long chains? In my private unobfuscated of files across arbitrarily long chains? In my private unobfuscated
source there are 17 lines describing just that. A classical case for source there are 17 lines describing just that. A classical case for
the well known `/* You're not supposed to understand this */` comment. the well known `/* You're not supposed to understand this */` comment.
- Would Dijkstra consider this goto harmful? - Would Dijkstra consider this `goto` harmful?
- The only string literal is split in more than a dozen substrings - The only string literal is split in more than a dozen substrings.
- For the task at hand the code is pretty terse. Some dynamic memory - For the task at hand the code is pretty terse. Some dynamic memory
is even freed as soon as it's no longer needed. I have tried to is even freed as soon as it's no longer needed. I have tried to
make the preprocessor directives as short as possible. This means make the preprocessor directives as short as possible. This means
no spaces after #include and no spaces after some of the macro no spaces after `#include` and no spaces after some of the macro
names. names.
Are you an algorithms guru? If so you should know why the output Are you an algorithms guru? If so you should know why the output
appears sorted without any use of `qsort()` or `system("sort")`. appears sorted without any use of `qsort(3)` or `system("sort")`.
Rewrite (hah!) the program to use memory mapping of files instead Rewrite (hah!) the program to use memory mapping of files instead
of `read()`. Does this affect performance noticeably? of `read(2)`. Does this affect performance noticeably?
It's easy to write a script that takes `samefile`'s output and It's easy to write a script that takes `samefile`'s output and
hard links (or symlinks) identical files. Writing such a script is hard links (or symlinks) identical files. Writing such a script is
@@ -214,11 +228,11 @@ left as an exercise for the deobfuscator.
When you run `samefile` on some directory tree, try to estimate When you run `samefile` on some directory tree, try to estimate
how much you think it will find. Is the actual result anywhere how much you think it will find. Is the actual result anywhere
near your estimate? How many copies of the GPL COPYING file near your estimate? How many copies of the GPL `COPYING` file
are there on your filesystems? :-) are there on your filesystems? :-)
In the remote event that the input has more than 8192 files with In the remote event that the input has more than `8192` files with
the same size (on systems where `sizeof (char *) == 4`, or 4096 when the same size (on systems where `sizeof (char *) == 4`, or `4096` when
`sizeof (char *) == 8`), increase the manifest constant 32767 on line `sizeof (char *) == 8`), increase the manifest constant 32767 on line
31. 31.
+1 -1
View File
@@ -28,7 +28,7 @@
/* HE WHO SAYS */ /* HE WHO SAYS */
Z I A<:32767/ M0(I )]; Z g stat S,T; Z size_t y B f{ Z I A<: SZ/ M0(I )]; Z g stat S,T; Z size_t y B f{
I n ; g f * x ; dev_t d ; ino_t i; } f B t{ M1 s,c; f I n ; g f * x ; dev_t d ; ino_t i; } f B t{ M1 s,c; f
*l; g t*L,*R; } t; D(a,t)D(E ,f)Z t*J(t*p,I n){ H! p){ p= *l; g t*L,*R; } t; D(a,t)D(E ,f)Z t*J(t*p,I n){ H! p){ p=
a(); p _ s =S O; p _ c=1; p _ L=p _ R=0; a(); p _ s =S O; p _ c=1; p _ L=p _ R=0;
+3
View File
@@ -12,3 +12,6 @@ echo "$ ln -f file3 file4"
ln -f file3 file4 ln -f file3 file4
echo "$ find . -type f -name 'file?' | ./samefile" echo "$ find . -type f -name 'file?' | ./samefile"
find . -type f -name 'file?' | ./samefile find . -type f -name 'file?' | ./samefile
echo "find . -type f -print | ./samefile" 1>&2
find . -type f -print | ./samefile
+34 -19
View File
@@ -8,26 +8,31 @@ make all
## To use: ## To use:
```sh ```sh
./tomtorfs tomtorfs.c 32 04C11DB7 1 FFFFFFFF FFFFFFFF ./tomtorfs filename bitwidth polynom reflected init xor
``` ```
where:
```
filename : file to calculate the CRC of
bitwidth : width of the CRC in bits (decimal)
polynom : polynomial used for the CRC (hexadecimal)
reflected : 0 if the CRC is not reflected, 1 if it is
init : initial value for the CRC (hexadecimal)
xor : value to xor the final CRC with (hexadecimal)
```
### Try: ### Try:
```sh ```sh
echo "abscond conferrable" > file.A ./try.sh
echo "adorn condolence" > file.B
echo "adorn condolence too" > file.C
./tomtorfs file.A 32 04C11DB7 1 FFFFFFFF FFFFFFFF
./tomtorfs file.B 32 04C11DB7 1 FFFFFFFF FFFFFFFF
./tomtorfs file.C 32 04C11DB7 1 FFFFFFFF FFFFFFFF
``` ```
## Judges' remarks: ## Judges' remarks:
If you don't believe the program is correct, try and compare the output If you don't believe the program is correct, try and compare the output
of the program with the value of CRC computed by PKZIP or any other of the program with the value of CRCs computed by PKZIP or any other
archiver that uses the CRC-32 algorithm. archiver that uses the CRC-32 algorithm.
@@ -47,12 +52,14 @@ xor : value to xor the final CRC with (hexadecimal)
### Examples ### Examples
#### CRC-16: #### CRC-16:
```sh ```sh
./tomtorfs filename 16 1021 0 FFFF 0000 ./tomtorfs filename 16 1021 0 FFFF 0000
``` ```
#### CRC-32: #### CRC-32:
@@ -60,6 +67,7 @@ xor : value to xor the final CRC with (hexadecimal)
./tomtorfs filename 32 04C11DB7 1 FFFFFFFF FFFFFFFF ./tomtorfs filename 32 04C11DB7 1 FFFFFFFF FFFFFFFF
``` ```
#### XMODEM: #### XMODEM:
```sh ```sh
@@ -67,12 +75,14 @@ xor : value to xor the final CRC with (hexadecimal)
``` ```
#### ARC: #### ARC:
```sh ```sh
./tomtorfs filename 16 8005 1 0000 0000 ./tomtorfs filename 16 8005 1 0000 0000
``` ```
### Portability notes ### Portability notes
The program assumes `CHAR_BIT==8`. Results are undefined if `bitwidth > The program assumes `CHAR_BIT==8`. Results are undefined if `bitwidth >
@@ -82,11 +92,13 @@ valid exit value to indicate failure (replace them with `EOF` resp.
No special build options required. No special build options required.
### Special notes ### Special notes
Count the number of non-whitespace characters in the source Count the number of non-whitespace characters in the source
file to find out what dark force drove me to write this ;-). file to find out what dark force drove me to write this ;-).
### Spoiler ### Spoiler
First of all, if you run a C beautifier on the program (thereby of course losing First of all, if you run a C beautifier on the program (thereby of course losing
@@ -99,26 +111,29 @@ following meanings:
``` ```
a = argc a = argc
A = argv A = argv
b = array of unsigned long variables b = array of unsigned long variables
b[0] = bit-width of the CRC (argv[2]) b[0] = bit-width of the CRC (argv[2])
b[1] = CRC polynomial (argv[3]) b[1] = CRC polynomial (argv[3])
b[2] = nonzero if CRC is reflected (argv[4]) b[2] = nonzero if CRC is reflected (argv[4])
b[3] = initial value of CRC (argv[5]) b[3] = initial value of CRC (argv[5])
b[4] = value final CRC is XORed with (argv[6]) b[4] = value final CRC is XORed with (argv[6])
b[5] = CRC value b[5] = CRC value
b[6] = byte from file b[6] = byte from file
b[7] = counter b[7] = counter
B = FILE * through which the file argv[1] is opened B = FILE * through which the file argv[1] is opened
C = typedef for unsigned long C = typedef for unsigned long
``` ```
The first for loop simply reads the command-line arguments The first `for` loop simply reads the command-line arguments
in the `b[]` array. The third parameter to `strtoul()` causes in the `b[]` array. The third parameter to `strtoul(3)` causes
all arguments to be in base-16 (hex) except the first one, all arguments to be in base-16 (hex) except the first one,
which will be in base-10 (decimal). which will be in base-10 (decimal).
The rest of the program "simply" calculates the CRC in `b[5]` The rest of the program "simply" calculates the CRC in `b[5]`
and then prints it to stdout. The bit reflection loops may and then prints it to `stdout`. The bit reflection loops may
appear a bit tricky at first, but if you have a good look appear a bit tricky at first, but if you have a good look
at them they should become obvious also. at them they should become obvious also.
+3 -2
View File
@@ -1,11 +1,12 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#define exit(a) return EXIT_FAILURE
int main(int a,char **A){FILE*B;typedef unsigned long C;C b int main(int a,char **A){FILE*B;typedef unsigned long C;C b
[8]; if(!(a==7&&(B= fopen(1[A],"rb")))) return 1;for(7[b]=0 [8]; if(!(a==7&&(B= fopen(1[A],"rb")))) exit(1); for(7[b]=0
;7[b]<5;7[b]++)b[7[ b]]=strtoul(A[2+7[b ]],0,16-!7[b]*6);5[ ;7[b]<5;7[b]++)b[7[ b]]=strtoul(A[2+7[b ]],0,16-!7[b]*6);5[
b]=3[b] ; while ((6[b]= getc(B) b]=3[b] ; while ((6[b]= getc(B)
)!=(C)- 1){if(2 [b])for (7[b]=0 ) !=EOF ){if(2 [b])for (7[b]=0
;7[b]<4 ;7[b]++ )if(((6 [b]>>7[ ;7[b]<4 ;7[b]++ )if(((6 [b]>>7[
b])^(6[ b]>>(7-7[b])))&1)6[ b] ^=(1 b])^(6[ b]>>(7-7[b])))&1)6[ b] ^=(1
<<7[b]) ^(1<<(7-7[b]));5[b] ^= 6[b] <<7[b]) ^(1<<(7-7[b]));5[b] ^= 6[b]
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
#
# script to demonstrate 1998/tomtorfs
#
rm -f file.A file.B file.C
echo "$ echo abscond conferrable > file.A" 1>&2
echo "abscond conferrable" > file.A
echo "$ ./tomtorfs file.A 32 04C11DB7 1 FFFFFFFF FFFFFFFF" 1>&2
./tomtorfs file.A 32 04C11DB7 1 FFFFFFFF FFFFFFFF
echo "$ echo adorn condolence > file.B" 1>&2
echo "adorn condolence" > file.B
echo "$ ./tomtorfs file.B 32 04C11DB7 1 FFFFFFFF FFFFFFFF" 1>&2
./tomtorfs file.B 32 04C11DB7 1 FFFFFFFF FFFFFFFF
echo "$ echo adorn condolence too > file.C" 1>&2
echo "adorn condolence too" > file.C
echo "$ ./tomtorfs file.C 32 04C11DB7 1 FFFFFFFF FFFFFFFF" 1>&2
./tomtorfs file.C 32 04C11DB7 1 FFFFFFFF FFFFFFFF
rm -f file.A file.B file.C
+62
View File
@@ -0,0 +1,62 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <conio.h>
#include <signal.h>
#define n(o,p,e)=y=(z=a(e)%16 p x%16 p o,a(e)p x p o),h(
#define s 6[o]
#define p z=l[d(9)]|l[d(9)+1]<<8,1<(9[o]+=2)||++8[o]
#define Q a(7)
#define w 254>(9[o]-=2)||--8[o],l[d(9)]=z,l[1+d(9)]=z>>8
#define O )):((
#define b (y&1?~s:s)>>"\6\0\2\7"[y/2]&1?0:(
#define S )?(z-=
#define a(f)*((7&f)-6?&o[f&7]:&l[d(5)])
#define C S 5 S 3
#define D(E)x/8!=16+E&198+E*8!=x?
#define B(C)fclose((C))
#define q (c+=2,0[c-2]|1[c-2]<<8)
#define m x=64&x?*c++:a(x),
#define A(F)=fopen((F),"rb+")
unsigned char o[10],l[78114],*c=l,*k=l
#define d(e)o[e]+256*o[e-1]
#define h(l)s=l>>8&1|128&y|!(y&255)*64|16&z|2,y^=y>>4,y^=y<<2,y^=~y>>1,s|=y&4
+64506; FILE *u, *v, *e, *V; int x,y,z,Z; main(r,U)char**U;{
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { ; } } { { { } } } { { ; } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
signal(SIGINT, SIG_IGN); for(v A((u A((e A((r-2?0:(V A(1[U])),"C")
),fread(l,78114,1,e),B(e),"B")),"A")); 118-(x
=*c++); (y=x/8%8,z=(x&199)-4 S 1 S 1 S 186 S 2 S 2 S 3 S 0,r=(y>5)*2+y,z=(x&
207)-1 S 2 S 6 S 2 S 182 S 4)?D(0)D(1)D(2)D(3)D(4)D(5)D(6)D(7)(z=x-2 C C C C
C C C C+129 S 6 S 4 S 6 S 8 S 8 S 6 S 2 S 2 S 12)?x/64-1?((0 O a(y)=a(x) O 9
[o]=a(5),8[o]=a(4) O 237==*c++?((int (*)())(2-*c++?fwrite:fread))(l+*k+1[k]*
256,128,1,(fseek(y=5[k]-1?u:v,((3[k]|4[k]<<8)<<7|2[k])<<7,Q=0),y)):0 O y=a(5
),z=a(4),a(5)=a(3),a(4)=a(2),a(3)=y,a(2)=z O c=l+d(5) O y=l[x=d(9)],z=l[++x]
,x[l]=a(4),l[--x]=a(5),a(5)=y,a(4)=z O 2-*c?Z||Z=kbhit()?getch():0,1&*c++?Q=Z,Z=0:(
Q=!!Z):(c++,Q=r=V?fgetc(V):-1,s=s&~1|r<0) O++c,putchar(7[o]) O z=c+2-l,w,
c=l+q O p,c=l+z O c=l+q O s^=1 O Q=q[l] O s|=1 O q[l]=Q O Q=~Q O a(5)=l[x=q]
,a(4)=l[++x] O s|=s&16|9<Q%16?Q+=6,16:0,z=s|=1&s|Q>159?Q+=96,1:0,y=Q,h(s<<8)
O l[x=q]=a(5),l[++x]=a(4) O x=Q%2,Q=Q/2+s%2*128,s=s&~1|x O Q=l[d(3)]O x=Q /
128,Q=Q*2+s%2,s=s&~1|x O l[d(3)]=Q O s=s&~1|1&Q,Q=Q/2|Q<<7 O Q=l[d(1)]O s=~1
&s|Q>>7,Q=Q*2|Q>>7 O l[d(1)]=Q O m y n(0,-,7)y) O m z=0,y=Q|=x,h(y) O m z=0,
y=Q^=x,h(y) O m z=Q*2|2*x,y=Q&=x,h(y) O m Q n(s%2,-,7)y) O m Q n(0,-,7)y) O
m Q n(s%2,+,7)y) O m Q n(0,+,7)y) O z=r-8?d(r+1):s|Q<<8,w O p,r-8?o[r+1]=z,r
[o]=z>>8:(s=~40&z|2,Q=z>>8) O r[o]--||--o[r-1]O a(5)=z=a(5)+r[o],a(4)=z=a(4)
+o[r-1]+z/256,s=~1&s|z>>8 O ++o[r+1]||r[o]++O o[r+1]=*c++,r[o]=*c++O z=c-l,w
,c=y*8+l O x=q,b z=c-l,w,c=l+x) O x=q,b c=l+x) O b p,c=l+z) O a(y)=*c++O r=y
,x=0,a(r)n(1,-,y)s<<8) O r=y,x=0,a(r)n(1,+,y)s<<8))));
system("stty cooked echo"); B((B((V?B(V):0,u)),v)); }
+4 -3
View File
@@ -1026,8 +1026,8 @@ not exist in the archive. Do you have it? Please provide it!
### Source code: [1993/cmills/cmills.c](1993/cmills/cmills.c) ### Source code: [1993/cmills/cmills.c](1993/cmills/cmills.c)
### Information: [1993/cmills/README.md](1993/cmills/README.md) ### Information: [1993/cmills/README.md](1993/cmills/README.md)
We are unsure if this is a problem with multiple platforms but in macOS at least In multiple platforms, both macOS and also linux (in particular a RHEL 9.3
the program appears to just shows a black screen. system), this entry just shows a blank screen.
Can you fix it? We welcome your help. Can you fix it? We welcome your help.
@@ -1041,7 +1041,8 @@ Can you fix it? We welcome your help.
This entry relied on a bug in gcc that was fixed with gcc version 2.3.3. This This entry relied on a bug in gcc that was fixed with gcc version 2.3.3. This
cannot be fixed for modern systems as the bug is long gone. cannot be fixed for modern systems as the bug is long gone.
An alternate version, however, does exist. See the README.md file for details. An alternate version that will work for modern systems, however, does exist. See
the README.md file for details.
### STATUS: INABIAF - please **DO NOT** fix ### STATUS: INABIAF - please **DO NOT** fix
+1 -1
View File
@@ -361,7 +361,7 @@ usually away with `stty echo`. Sometimes you can also get away with `stty sane`.
will too but it won't reset the terminal). will too but it won't reset the terminal).
<a name="#X11macos"></a> <a name="X11macos"></a>
## Q: How do I get X11 entries to work with macOS? ## Q: How do I get X11 entries to work with macOS?
As an example we will use [1993/jonth](1993/jonth/README.md) which works well As an example we will use [1993/jonth](1993/jonth/README.md) which works well
+83 -45
View File
@@ -1391,15 +1391,16 @@ should have been removed.
## [1992/adrian](1992/adrian/adrian.c) ([README.md](1992/adrian/README.md])) ## [1992/adrian](1992/adrian/adrian.c) ([README.md](1992/adrian/README.md]))
Cody fixed the code so that it will try opening the file the code was compiled Cody fixed the code so that it will try opening the file the code was compiled
from (`__FILE__`), not `adgrep.c`, as from (`__FILE__`), not `adgrep.c`, as the latter does not exist: `adgrep` is
the latter does not exist: `adgrep` is simply a link to `adrian` as `adgrep` is simply a link to `adrian` as `adgrep` is what the program was submitted as but
what the program was submitted as but the winner is `adrian.c`. the winner is `adrian.c`.
Not fixing this would cause the program to crash if no arg was specified as the Not fixing the above problem would have also cause the program to crash if no
file did not exist. In doing this, at first the change to check for a NULL file arg was specified as the file doesn't exist. In making the above fix, at first
was added. Then it was noticed that the problem is that `adgrep.c` was an the change to check for a `NULL` file was added. Then it was noticed that the
incorrect reference that was never fixed in any of the files, not the code or problem is that `adgrep.c` was an incorrect reference that never existed and it
the documentation. A fun fact is that one can do: was never fixed in any of the files, not the code or the documentation, and thus
was entirely incorrect code. A fun fact is that one can do:
```c ```c
W= fopen(wc>= 2 ? V[1] : __FILE__,"rt");if(!W)exit(1); W= fopen(wc>= 2 ? V[1] : __FILE__,"rt");if(!W)exit(1);
@@ -1423,24 +1424,24 @@ Cody also restored a slightly more obscure line of code that had been changed:
+ putc(i["}|uutsrq`_^bji`[Zkediml[PO]a_M__]ISOYIRGTNR"]+i-9,stderr); + putc(i["}|uutsrq`_^bji`[Zkediml[PO]a_M__]ISOYIRGTNR"]+i-9,stderr);
``` ```
though it's questionable how much more (if at all) obscure that is. though it's questionable how much more (if at all) obscure that is :-)
Cody also changed the location that it used `gets()` to be `fgets()` instead to Cody also changed the location that it used `gets()` to be `fgets()` instead to
make it safer and to prevent annoying warnings during compiling, linking or make it safer and to prevent annoying warnings during compiling, linking or
runtime (interspersed with the program's output). This was complicated because runtime (interspersed with the program's output). This was complicated because
of how the other source files are generated, as noted above; simply changing the of how the other source files are generated (as above); simply changing the code
code could cause invalid output in the program which made other files fail to could cause invalid output in the program which made other files fail to compile
compile (for this example specifically, see below). (for this example specifically, see below).
One might think that simply changing the `gets()` to `fgets()` (with `stdin`) One might think that simply changing the `gets()` to `fgets()` (with `stdin`)
would work but it did not because `fgets()` stores the newline and `gets()` does would work but it did not because `fgets()` stores the newline and `gets()` does
not. That is well known but this code was relying on not having this newline not. That is well known but this code was relying on not having this newline in
(see also above). a different way (see also above).
With `fgets()` the code `if(A(Y)) puts(Y);` ended up printing an extra line With `fgets()` the code `if(A(Y)) puts(Y);` ended up printing an extra line
which made the generation of some files (like `adhead.c`) fail to compile. Why? which made the generation of some files (like `adhead.c`) fail to compile. Why?
There was a blank line after a `\` at the end of the first line of a macro There was a blank line after a `\` at the end of the first line of a _macro
definition! Thus the code now first trims off the last character of the buffer definition_! Thus the code now first trims off the last character of the buffer
read to get the same correct functionality but in a safe and non obnoxious way. read to get the same correct functionality but in a safe and non obnoxious way.
Later Cody improved the change to `fgets()` to make it slightly more like the Later Cody improved the change to `fgets()` to make it slightly more like the
@@ -1467,9 +1468,10 @@ the additional tools.
Cody fixed this to compile with modern systems. Note that in 1996 a bug fix was Cody fixed this to compile with modern systems. Note that in 1996 a bug fix was
applied to the code, provided as the alt code as that version is not obfuscated. applied to the code, provided as the alt code as that version is not obfuscated.
Thus Cody's fixed applies to the original entry. The problems were that Thus Cody's fix applies to the original entry. The problems were that `malloc.h`
`malloc.h` is not the correct header file now and a non-void (implicit int) is not the correct header file now (at least in some systems?) and a non-void
function returning without a value. The function was changed to return void. (implicit int) function returning without a value. That function was changed to
return void.
## [1992/ant](1992/ant/ant.c) ([README.md](1992/ant/README.md])) ## [1992/ant](1992/ant/ant.c) ([README.md](1992/ant/README.md]))
@@ -1495,7 +1497,8 @@ necessary to include `time.h` so Cody did this as well.
Cody added a check for the right number of args, exiting 1 if not enough (2) Cody added a check for the right number of args, exiting 1 if not enough (2)
used. This was not originally done but at a time it was changed to be considered used. This was not originally done but at a time it was changed to be considered
a bug so it was fixed at that point as it only took a few seconds. a bug so it was fixed at that point as it only took a few seconds and had to be
verified that it was consistent with the [bugs.md](/bugs.md) file.
He also added the [try.sh](1991/buzzard.1/try.sh) script to try out some He also added the [try.sh](1991/buzzard.1/try.sh) script to try out some
commands that we suggested and some additional ones that provide for some fun. commands that we suggested and some additional ones that provide for some fun.
@@ -1522,14 +1525,15 @@ loop).
Cody also added the [mkdict.sh](1992/gson/mkdict.sh) script that the author Cody also added the [mkdict.sh](1992/gson/mkdict.sh) script that the author
included in their remarks. See the README.md for its purpose. It was NOT fixed included in their remarks. See the README.md for its purpose. It was NOT fixed
for ShellCheck because the author deliberately obfuscated it. for ShellCheck because the author deliberately obfuscated it so **PLEASE DO NOT
FIX THIS**.
## [1992/imc](1992/imc/imc.c) ([README.md](1992/imc/README.md])) ## [1992/imc](1992/imc/imc.c) ([README.md](1992/imc/README.md]))
The original code, [imc.orig.c](1992/imc/imc.orig.c), assumed that `exit(3)` The original code, [imc.orig.c](1992/imc/imc.orig.c), assumed that `exit(3)`
returned a value but this will cause problems where `exit(3)` returns void. The returned a value but this will cause problems where `exit(3)` returns void. The
source code was modified to avoid this problem but like Cody did with otherfixes source code was modified to avoid this problem but like Cody did with other fixes
he made this more like the original by redefining `exit` to use the comma he made this more like the original by redefining `exit` to use the comma
operator so that it could be used in binary expressions. operator so that it could be used in binary expressions.
@@ -1537,16 +1541,17 @@ operator so that it could be used in binary expressions.
## [1992/kivinen](1992/kivinen/kivinen.c) ([README.md](1992/kivinen/README.md])) ## [1992/kivinen](1992/kivinen/kivinen.c) ([README.md](1992/kivinen/README.md]))
It was observed that on modern systems this goes much too quick. Yusuke created It was observed that on modern systems this goes much too quick. Yusuke created
a patch that calls `usleep()` but Cody thought the value was too slow so he made a patch that calls `usleep(3)` but Cody thought the value was too slow so he
it a macro in the Makefile `Z`, defaulting at 15000. You can reconfigure it made it a macro in the Makefile `Z`, defaulting at 15000. This was made an [alt
like: version](1992/kivinen/kivinen.alt.c) and it is recommended one use the alt
version first. See the README.md file to see how to reconfigure it.
```sh Cody also made the fixed version (the code relied on `exit(3)` returning to use
make clobber Z=1000 all in binary expressions) (and alt code from it) more like the original by renaming
``` the `ext` macro to be `exit` which uses the comma operator. He made it so the
line lengths match the original code, at least as best as possible (if not
This was not made an alternate version because it moves so fast that it's nigh perfectly), including start and end columns, often (if not all) with the same
impossible to use otherwise. start and end character.
Yusuke also noted that there is a bug in the program where right after starting Yusuke also noted that there is a bug in the program where right after starting
it moves towards the right but if you click the mouse it goes back. it moves towards the right but if you click the mouse it goes back.
@@ -2045,9 +2050,13 @@ exiting the program.
## [1998/fanf](1998/fanf/fanf.c) ([README.md](1998/fanf/README.md)) ## [1998/fanf](1998/fanf/fanf.c) ([README.md](1998/fanf/README.md))
Cody fixed this for Linux. The problem was the intermediate steps to get to the Cody fixed this to compile. The problem was the intermediate steps to get to the
final code that is compiled. The entry itself is what was essentially what used final code that is compiled. The code is now what it essentially becomes when
to be compiled. Now it works with both Linux and macOS (and BSD?). processed completely. The intermediate steps can now be performed to see how it
expands but it can still compile and be used.
Cody also added the [try.sh](1998/fanf/try.sh) script to show the output of some
of the expressions that we selected.
## [1998/schnitzi](1998/schnitzi/schnitzi.c) ([README.md](1998/schnitzi/README.md])) ## [1998/schnitzi](1998/schnitzi/schnitzi.c) ([README.md](1998/schnitzi/README.md]))
@@ -2055,8 +2064,9 @@ to be compiled. Now it works with both Linux and macOS (and BSD?).
Cody fixed invalid data types which prevented this entry from working, causing a Cody fixed invalid data types which prevented this entry from working, causing a
segfault. This showed itself in two parts which required two fixes, one for segfault. This showed itself in two parts which required two fixes, one for
linux and further changes for macOS. He also fixed a segfault (after printing linux and further changes for macOS. He also fixed a segfault (after printing
garbage) when the arg specified evaluated to 0. It was decided that these garbage) when the arg specified evaluated to 0. It was decided by us, the
segfault fixes should be made because the program is so beautiful. judges, that these segfault fixes should be made because the program is so
beautiful.
Later on Cody improved the fixes by checking that the arg is a number `>0 && Later on Cody improved the fixes by checking that the arg is a number `>0 &&
<27` as that was noted by the author as a requirement and again since it's such <27` as that was noted by the author as a requirement and again since it's such
@@ -2081,16 +2091,16 @@ alternate version that works with macOS. Cody also made it ever so slightly more
portable by removing the hard-coding of `gcc`, instead hard-coding it `cc`. Doing portable by removing the hard-coding of `gcc`, instead hard-coding it `cc`. Doing
this not only lets it work with systems without `gcc` (though in macOS it does this not only lets it work with systems without `gcc` (though in macOS it does
exist but is actually `clang`) as `cc` always should. exist but is actually `clang`) as `cc` always should.
Getting this entry to work was quite complicated but is also very interesting. Getting this entry to work was quite complicated but is also very interesting.
To see how the macOS fixes works, see the README.md but do note that this
includes spoilers for both versions! The fixes to get it to work at all are To see how the macOS fixes works, see the [macos.md](1998/schweikh1/macos.md)
described next. file but do note that this includes spoilers for both versions! The fixes to get
it to work at all are described next.
So what was wrong with the original? So what was wrong with the original?
The call to `freopen()` was incorrect with the second arg (the mode) being The call to `freopen(3)` was incorrect with the second arg (the mode) being
`5+__FILE__`; it is now `"r"`. (Observe that the mode to the `fopen()` `5+__FILE__`; it is now `"r"`. (Observe that the mode to the `fopen(3)`
call is: `43+__FILE__`. This might seem incorrect and indeed it can be changed call is: `43+__FILE__`. This might seem incorrect and indeed it can be changed
to `"r"` as well but this was not actually necessary so once this was noticed it to `"r"` as well but this was not actually necessary so once this was noticed it
was changed back to the original.) was changed back to the original.)
@@ -2127,9 +2137,9 @@ as the author stated: as long as the options `-E -dM` of the compiler prints out
the macros in the form of `gcc -dM` i.e. the lines are in the form `#define the macros in the form of `gcc -dM` i.e. the lines are in the form `#define
MACRO value` it will work, assuming that compiler can run, of course. MACRO value` it will work, assuming that compiler can run, of course.
As for the version for macOS, the even more complicated details are described in Cody also added the perl script that the author provided that they used to
the [Alternate code](1998/schweikh1/README#alternate-code) section of the compute the character count in the code according to the contest rules of 1998
README.md file as these changes pertain to the described version therein. in the file [charcount.pl](1998/schweikh1/charcount.pl).
## [1998/schweikh2](1998/schweikh2/schweikh2.c) ([README.md](1998/schweikh2/README.md])) ## [1998/schweikh2](1998/schweikh2/schweikh2.c) ([README.md](1998/schweikh2/README.md]))
@@ -2158,10 +2168,38 @@ command.
Cody also made the Makefile rule `all` symlink the entry to `samefile` as that Cody also made the Makefile rule `all` symlink the entry to `samefile` as that
is the name of the program. is the name of the program.
The author stated that:
```
In the remote event that the input has more than `8192` files with
the same size (on systems where `sizeof (char *) == 4`, or `4096` when
`sizeof (char *) == 8`), increase the manifest constant 32767 on line
31.
```
so Cody changed the constant to a macro in the Makefile called `SZ` so one can
more easily do this (though it indeed seems highly unlikely). See the README.md
for more details.
There actually is a web page for the tool and this was added to the author There actually is a web page for the tool and this was added to the author
information for the entry. It has not been added to any JSON file. information for the entry. It has not been added to any JSON file.
## [1998/tomtorfs](1998/tomtorfs/tomtorfs.c) ([README.md](1998/tomtorfs/README.md]))
Cody fixed the assumption that `EOF` is `-1` (the author noted that it assumes
it is `-1` and it's indeed a valid concern as the standard only guarantees that
it's a value `< 0`) and that `1` is a valid code to return failure (it seems
unlikely that it wouldn't be but since the author suggested it it was changed to
`EXIT_FAILURE`). To make it so the lines end at the same columns as the original
the `EXIT_FAILURE` change was done by redefining `exit(3)` to be `exit(a) return
EXIT_FAILURE` and `1` was passed to it (`return` was used because the original
program had `return 1`).
Cody also added the [try.sh](1998/tomtorfs/try.sh) script to try out a few
commands that we recommended.
## [2000/anderson](2000/anderson/anderson.c) ([README.md](2000/anderson/README.md])) ## [2000/anderson](2000/anderson/anderson.c) ([README.md](2000/anderson/README.md]))
Cody changed this entry to use `fgets()` instead of `gets()` to make it safer Cody changed this entry to use `fgets()` instead of `gets()` to make it safer