#!/bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'encode.ANSWER' <<'END_OF_FILE' XAllen writes: X XThis is an eexec encoder written in PostScript. It will encode text Xpresented to it in the same way fonts are encrypted. There's no Xgraphic output. You can run the output of X X abdict begin X (encode.ps) (r) file encodefile X Xjust like the original! This file is encoded.ps, which was originally Xsubmitted for the contest, but discounted because it was machine Xgenerated. X XWhen I first heard of the contest, this (encoded.ps) was my immediate Xidea for an entry. The original code was pretty straightforward, once Xthe number promotion problem was solved (large intermediate results Xwould go to floating point and the bit operations would fail), but the Xencoded version was nearly unreadable without mechanical assistance. X XThe text version (encode.ps) is a fairer submission since it is not X"machine-generated in any way". I have a strong hunch that many of the Xsubmitted programs will have machine-generated strings of data, and Xwhile that's certainly not good, I'd accept it if the program that Xgenerated the data was also written in PostScript and included. X XCuriously, encoded.ps is the program that generated itself- sort of. XThe result is reproduceable by encoding encode.ps. You can also encode Xencoded.ps if you want. Would that program work? X Xencode.ps is block formatted not to obscure its meaning, but to make it Xa little shorter because the encoding process expands it. You Xshouldn't consider its lexical presentation part of the obscurity. X XNotice that considerable dictionary dancing happens at the top. Why is Xthat? Is it necessary? Would it be if this program were eexec Xencoded? X XThe encode function encodes a single character. You shouldn't find Xthat too difficult if you've read the description of eexec in The Black XBook. The two ifs make sure the printed output comes out as we'd Xexpect. The second isn't needed, but long lines are annoying even in Xan obfuscated program. :-) X XThe next function, encodestring, is very straightforward. What would Xyou do with an encoded string if you had one? Notice that since this Xis in sync with encode, you can call encode and encodestring Xalternatively at your convenience. X XThe last function, encodefile, is the real meat of the program. It Xinitializes the names used in the encode engine, prints out enough code Xto get things rolling, eexec encodes all the characters in the file, Xand then adds some convenience code to the end of the encoded program. X XNotice also that instead of randomly generating four inital bytes, I Xstuck a vanity line in. This was to make the output reproduceable. In Xgeneral use, you'd probably take that out and use the random number Xgenerator. X XSeveral tricks were necessary to get around PostScript's implicit Xinteger to floating point conversion. If you try to write it directly Xfrom the C version, you will fail. X XI've found this program rather useful, but there is a hitch: eexec Xdoesn't work the same on all non-Adobe PostScript clones. Play with it Xand see. What should happen when the eexec is encountered: X X A new file object is pushed on the exec stack. This is the X unencypted stream. X X systemdict is pushed on the dict stack. Note that this means X you can't def anything because you can't write on systemdict! X (Post 1.7 on the Amiga doesn't do this!) X X The text is decrypted and executed (see page 63 in The Black X Book). X X If the currentfile is closed within the encrypted portion, X execution resumes after the encrypted text. My program X automatically adds this at the end of the program text. X XMy program should leave the new dictionary containing the encryption Xroutines on the dict stack (and the encrypted version should also leave Xsystemdict there). GhostScript 2.3 pops it, Adobe PostScripts don't. XThis might be fixed in newer versions of gs. To be safe, I defined the Xdictionary in userdict so you could begin it if it wasn't on the dict Xstack. It'd be a little shorter if you didn't have to. X XBefore and after you run encode.ps, do a countdictstack X== to see how many dictionaries are on the dict stack. Try it on Xdifferent PostScript interpreters. Report bugs as needed. :-) X Xeexec encryption is usually only used for fonts, but it can be used (as Xwe've seen) on nearly anything. Just be careful with your Xdictionaries. If all you do is draw, you should be OK! Try writing a Xsimple program that does some graphics and no defs and encodefile it. XAdd code to an encoded file that interacts with the encrypted code. X X24#Allen is a base 24 radix number. Mostly a red herring to throw the Xdogs off the scent. Kinky way to put in vanity info as well. :-) X XThe lines to calculate the encoding (in /encode): X Xr (the "random" number used by the encryption) starts at 55665. The Xjob of encode is to take a character from the op stack, leave the Xencoded character on the op stack when it's done, and update r. X XLet's say the incoming character is a space (actually, r would be Xsomething else probably because of the ignored bytes up front): X Xon op stack operator why X-- -- ----- -------- --- X32 55665 -8 bitshift We want to mask the incoming X32 217 xor character with 8 bits of r. X249 255 and Make sure just 8 bits * X249 dup Eventually leave the crypted byte X249 249 55665 add Update r as: X249 55914 10569 mul r = ((cipher+r) * C1 + C2) & 16#ffff X249 590955066 65535 and This and X249 16954 5 mul and this mul are to keep it from X249 84770 22719 add going to floating point. X249 107489 65535 and Get new r back in range X249 41953 /r exch Update r X249 /r 41953 def X249 Encrypted character (plain^(r>>8)) X XC1 is 52485 and C2 is 22719 (mandated by the Black Magic hoodoo). If Xthe intermediate result ever gets to 2^30, the number will be promoted Xto a real and you'll lose bits (so the xor won't work). The 65535 and Xis used to keep results lower. C1 was factored to keep the result from Xexploding for values of r greater than 20458. X XThe first 65535 and (*) is in case some joker set r to something Xunreasonable. Obviously, this function never will. X XIt's a lot clearer if you keep the integer limitations in mind and then Xgo read the C example in The Black Book. Why you can do the and in the X"middle" of the multiplication might puzzle you. Think about how r is Xused. X XLeft as an exercise to the student is the function cexee that takes Xeither a file or a string as an argument and leaves the encoding in an Xobject on the stack for eexec to decode and execute. END_OF_FILE if test 6763 -ne `wc -c <'encode.ANSWER'`; then echo shar: \"'encode.ANSWER'\" unpacked with wrong size! fi # end of 'encode.ANSWER' fi if test -f 'encode.HINT' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'encode.HINT'\" else echo shar: Extracting \"'encode.HINT'\" \(464 characters\) sed "s/^X//" >'encode.HINT' <<'END_OF_FILE' Xencode.ps, by Allen Braunsdorf (ab@mentor.cc.purdue.edu) is a utility. XYou must run it interactive through a previewer. X X MOST USEFUL -- 1st prize X -- The most useful, yet complex and twisted, program. X XThe program is pretty straightforward, here's how to use it: X X abdict begin X (encode.ps) (r) file encodefile X XCompare the output you get with encoded.ps. X XYou can replace encode.ps with any simple PostScript program. XTry lots of simple moveto's and lineto's. END_OF_FILE if test 464 -ne `wc -c <'encode.HINT'`; then echo shar: \"'encode.HINT'\" unpacked with wrong size! fi # end of 'encode.HINT' fi if test -f 'encode.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'encode.ps'\" else echo shar: Extracting \"'encode.ps'\" \(675 characters\) sed "s/^X//" >'encode.ps' <<'END_OF_FILE' Xuserdict begin 8 dict dup /abdict exch def end begin /hex 2 string def /zero X(0) def /encode { r -8 bitshift xor 255 and dup r add 10569 mul 65535 and 5 Xmul 22719 add 65535 and /r exch def 16 hex cvrs dup length 1 eq { zero print X} if print /c c 1 add def 35 c lt { (\n) print /c 0 def } if } def X/encodestring { { encode } forall } def /encodefile { /plaintext exch def /c X0 def /r 55665 def (%!PS-Adobe-2.0\n%%Title: Obfuscated Program\n) print X(%%Creator: ab@nova.cc.purdue.edu\n\ncurrentfile eexec\n) print 24#Allen Xencode 12#B encode 21#did encode 30#this encode { plaintext read { encode } { Xexit } ifelse } loop (currentfile closefile\n) encodestring (\n) print } def END_OF_FILE if test 675 -ne `wc -c <'encode.ps'`; then echo shar: \"'encode.ps'\" unpacked with wrong size! fi # end of 'encode.ps' fi if test -f 'encode.unobfs.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'encode.unobfs.ps'\" else echo shar: Extracting \"'encode.unobfs.ps'\" \(1578 characters\) sed "s/^X//" >'encode.unobfs.ps' <<'END_OF_FILE' X%! X%%Title: Commented and formatted version of encode.ps X%%Creator: Allen B (ab@nova.cc.purdue.edu) X X% if this program were encoded, systemdict would be TOS right now Xuserdict begin % systemdict is not writable X8 dict % so we need our own Xdup /abdict exch def % and we should leave a hook for it in userdict Xend X Xbegin X X/hex 2 string def % string to put hex numbers in X/zero (0) def % for padding small numbers X X% int encode - X/encode X{ X % encode the byte X r -8 bitshift xor X 255 and X X % update the state of the encoder X dup r add X 10569 mul 65535 and 5 mul % this mul is split to avoid overflow X 22719 add 65535 and X /r exch def X X % encoded byte is on the stack, print it out with a zero if needed X 16 hex cvrs X dup length 1 eq { X zero print X } if X print X X % drop a newline every 35 X /c c 1 add def X 35 c lt { X (\n) print X /c 0 def X } if X} def X X% string | array encodestring - X/encodestring X{ X { X encode X } forall X} def X X% file encodefile - X/encodefile X{ X /plaintext exch def % file to encode X /c 0 def % column to start in X /r 55665 def % random number generator seed X X % print a header and an eexec to undo what we're going to do X (%!PS-Adobe-2.0\n%%Title: Obfuscated Program\n) print X (%%Creator: ab@nova.cc.purdue.edu\n\ncurrentfile eexec\n) print X X % encode four unimportant bytes X 24#Allen encode X 12#B encode X 21#did encode X 30#this encode X X % encode the given file X { X plaintext read { X encode X } { X exit X } ifelse X } loop X X % add some code so execution will resume after the encoded part X (currentfile closefile\n) encodestring X (\n) print X} def END_OF_FILE if test 1578 -ne `wc -c <'encode.unobfs.ps'`; then echo shar: \"'encode.unobfs.ps'\" unpacked with wrong size! fi # end of 'encode.unobfs.ps' fi if test -f 'encoded.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'encoded.ps'\" else echo shar: Extracting \"'encoded.ps'\" \(1517 characters\) sed "s/^X//" >'encoded.ps' <<'END_OF_FILE' X%!PS-Adobe-2.0 X%%Title: Obfuscated Program X%%Creator: ab@nova.cc.purdue.edu X Xcurrentfile eexec X7E7E7A0E1BEDCE30FAAC1DD1FB30D25F2AF71C3F146B6CE254B80A9FA279B00E0E424FE5 X64536ADCC2CE52B3D61FEB35131B1B8618DE8BAB3AC01F885FDB5F672A6DD4A8D388FDC7 X3107AF68F59BA583FF664B27DEDCB5671DDBA42305AA15753FEA76E080EBDD5553A837E9 XE59C30804847E5E9D4BFF0730EE8312183F9BE1CC351A4A0F2438430FF4C2CB4B9C9A166 X9D4863DDEC121B430CB097485075B9B0AAD4206B9B523E8855E8645946825FA55DF6FC36 X559D66879E6F5453854CB8D8DDE5961CDD402DBA6383DB945A6C4DF71BFDCBC1C321AF98 X0ACD6B3EFC8C3FF3F18DE5C1D38D8FF230D6D54E73445BE4A8D4ABC0DC9023B3F91BACC4 XFD40D77FEAEBDACA74859F2232CD25EE459FECDEA6FF5298303396D1255D449F8BDA52FD X0E2793A8D555331D651C23F06B6068D71227DFEFF56CEF5FCFC58DC5B9CE11938194F08D X74E9D009B706D2E2F7FC95A6039421EF2DCC240DC5AD770FFC2D1E70B78FCCA549D3632A XE5EC1DA2951E3C883BD6AB999506252685331801E7AC212C729EB001A4D92C8F7AD66637 X54BFAE25800EA0A526239343E606788DF2EDF087EAADC2ED8C043050256D59DF75CB7064 X859CE2A09182A6ADA97BB12839327AAA79FCD147F79F6507DD7902CE32CE6345F6A7ACDE X8CC4A7426C54E536CA0D495AE6302266D08BC5A99D41D9D288E5A05503CF29CAAC7D7395 XDF3D844BB41BC871DBA1926A021853E23CD69FAEB41A9BDBB535598DFABD5E0C951CD13E X60C394F8DC8AFFDED57AD288BE4676080AB3639247763B72612F6B40EB65B3BAA2683276 X4D3203FDDFD0C4DB81108ABA19AA68F07CA7CAFB64E0710A26A3F515A7C369D49AD2EC0D XB0198F1B189E61FD86B14BAA7B73539F8AB7DB9B283B5DA47AEFE00DD528098ED9BD14B3 XE240DD57F1BDDBCBDA5E2B0118063606928905C866CA11E4750535656F75E21EF8E2665A X79C6D72E13DFDCD8A0E16A2CFDC0C13F7E END_OF_FILE if test 1517 -ne `wc -c <'encoded.ps'`; then echo shar: \"'encoded.ps'\" unpacked with wrong size! fi # end of 'encoded.ps' fi if test -f 'labyrinth.ANSWER' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'labyrinth.ANSWER'\" else echo shar: Extracting \"'labyrinth.ANSWER'\" \(4190 characters\) sed "s/^X//" >'labyrinth.ANSWER' <<'END_OF_FILE' XThis is the description that David gave us of the program, after we Xtold him that he had won the contest. X XFirst, all the parameters get pushed on to the stack; they'll all be left Xthere for a bit while the program goes and does other things. X XSecond, one-character synonyms are defined for the 35 most common Xprimitive operators that appear in the program by "load"ing each operator Xand "def"ing the synonym to it. Before any definitions are done, a new Xdictionary is defined which is large enough to accommodate the whole Xprogram's definitions. X XThird, the entire program is unpacked. Since the vast majority of the code Xin the program can now be written as a sequence of single characters or Xslash-characters separated by spaces, there is no reason to put most of Xthe spaces in the code. So the program is pushed as a sequence of 50 Xstrings, starting with the string describing the end of the program, Xalternately with spaces removed and without spaces removed. The strings Xare then unpacked into a big string by putting in the appropriate spaces. XAt the end of the unpacking, the big string contains the main progrm body Xwhich is then executed. X XThe string contains a program which does the following: it begins by Xtaking the parameters and using them to define constants. Some of the Xconstants which are defined are procedures or arrays of procedures which Xare defined differently depending on the parameters. X XThe maze is actually generated in an array of integers by an algorithm Xthat randomly wanders through the array, marking its path, backtracking Xwhenever it is caught in a dead end, and branching (after backtracking) Xas early as possible. Once the algorithm has backtracked all the way Xback to its starting point and cannot branch any more, the whole array Xwill have been visited, and furthermore, the marked path must form Xa tree, which means there are no loops. This tree is our maze, and a Xrandom start and end point are chosen. In a 3-dimensional maze, the Xconcept of a "dead end" is redefined so that the wandering point can Xjump over any number of paths that are at right angles to the jump, as Xlong as eventually there is an empty square to land in. X XAfter the maze is generated, a solution has to be found. A new wandering Xpoint algorithm begins at the "start" point and moves within the maze given Xby the first algorithm; the soltuion algorithm, however, doesn't make Xrandom turns. It systematcally "follows the left wall", favoring left Xturns over straight moves, and straight moves over right turns. In this Xkind of maze, this solution algorithm is guaranteed to get to the end Xpoint eventually, and if it marks the maze correctly, we can deduce the Xshortest path from beginning to end. X XFinally, the maze and the solution are drawn. Each "cell" of the maze Xcan look like a straightaway, an elbow, a dead end, or a T shape. Rather Xthan deal with each of these cases in all their orientations seperately, Xwe notice that the walls only have a few shapes: inside small corner, Xoutside big corner, straight wall, and dead-end. The program contains Xa simple algorithm that goes counterclockwise around the four possible Xexit directions of a maze cell and draws the appropriate kind of wall Xdepending on the angle spanned by two adjacent cell exits. The definition Xof the walls depends on whether the maze is curvy or straight. Finally, Xin a 3-d maze, special case code is required to draw crossing paths. X XAfter the maze is printed out, the solution path is drawn and printed Xout in a very similar way. X XNow, you might ask, why might somebody go to all the trouble of writing Xthis big program with one-letter identifiers? The answer is that I did Xnot do that. I wrote the maze-drawing program with regular variable names Xand then wrote a little lex program to translate all the identifiers in Xthe program into a one-character identifiers. With this translator, it Xwas easy to develop the compact code and reuse precious one-letter variable Xnames without having source code that was too confusing. X XThe final compacting trick of assembling the main program out of strings Xwith a short whitespace-inserting algorithm was done entirely by hand. X XDavid END_OF_FILE if test 4190 -ne `wc -c <'labyrinth.ANSWER'`; then echo shar: \"'labyrinth.ANSWER'\" unpacked with wrong size! fi # end of 'labyrinth.ANSWER' fi if test -f 'labyrinth.HINT' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'labyrinth.HINT'\" else echo shar: Extracting \"'labyrinth.HINT'\" \(700 characters\) sed "s/^X//" >'labyrinth.HINT' <<'END_OF_FILE' Xlabyrinth.ps by David Bau (bau@cs.cornell.edu) is a maze-making program Xthat prints the maze on the first page and the solution on the second. X XOBFUSCATED CONTEST WINNER -- 1st Prize X-- The most coveted prize. The most maliciously tangled, yet functional, code. X XThis program has a number of parameters that can be changed. Try experimenting Xand see what results you get! X XThe versatility of this program to have user-changeable parameters weighed Xheavily in the judges' evaluation. The code itself is a complex algorithm Xthat is itself encoded in a self-extracting form. Just getting the algorithm Xout by itself is a challenge! X XCompare the size of the unobfuscated program with the obfuscated one. END_OF_FILE if test 700 -ne `wc -c <'labyrinth.HINT'`; then echo shar: \"'labyrinth.HINT'\" unpacked with wrong size! fi # end of 'labyrinth.HINT' fi if test -f 'labyrinth.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'labyrinth.ps'\" else echo shar: Extracting \"'labyrinth.ps'\" \(2253 characters\) sed "s/^X//" >'labyrinth.ps' <<'END_OF_FILE' X%! X% Expert Labyrinth for Postscript printers, Copyright 1992 by David Bau X X.30 .15 % line weights (0..1) % This file is free puzzleware. :) X .70 % tunnel shape (-1..1) % Please distribute it and its output only for X 40 % twistiness (0..100) % free, and keep my original comments intact. X true % tunnel crossings % If you like it or if you have a suggestion, X612 792 % page dimensions % send me a postcard from your home town! X 78 168 % page margins % 777 South Avenue; Weston, MA 02193; USA X 24 % cell size % bau@cs.cornell.edu X X/`/def/*/repeat/~/lineto/!/mod/@/if/#/ifelse/$/dup/^/add/|/stroke/'/put/S/gsave X/./exit/,/and/&/sub/-/or/_/loop/=/exch/+/eq/Z/ne/"/bind/:/get/;/bitshift/W/exec X/N/mul/O/translate/Y/setlinewidth/T/grestore/X/showpage/$/dup/V/arc/?/moveto 78 Xdict begin/M/pop/P/neg/Q/for/R/div 35{load def}repeat(,b5;-'/aabaCPbzN^`/bbw4!` X}{/bb3^4!`}#}_}"WYI$0Z{SXTIYH}{M}#XT)(31)({.}@}_c1eK$G4-'c0eK5'cfdK$G1-'cfdKw5' X/hfwdK`/a0eK`/b0`{ah+{.}@aGbJ,0Z{caaG)(ge)(gf2R&)(abs)(;`b4+{.}@/abaCPbzNa^`}#} X_{/eghw`/dghw`de&)(-5)(,u5;-}@'}_caaGu$5;=J--'}{/baG)(lt{15 xor 31)(-6h3)(16)(` XcaaGbJ-'baC{BcaaG)(xor)({{l$i=:d{.}@M}_}{4}#}#`b4Z{/ub2)S(100 h n ge ,{b}{false Xi{d -}forall)('}Q}*/afhwghwK`/bl`ca45;'{/b/i[013{aC}Q]`ib:d)(16)(,0Z/nA013{$Ji, X0Z{/lA/ml`4{/mmw4!`LmJi,0Z{tl4^m&4!:W/lm`}@}*.}@LM}QT}Q}D/H{/a0eK`/b0`{/maG5P;` XSaEOb{L}*mw4!b+{L}@kmb^2!:WTah+{.}@/bm`baCP{BSaEOb2!{L}*1y?sy~ry?0y~|T}_}_}D{01 Xpx{c=0'}Q01opo&1p0opoxop4{x{c=)(15 ,/i A 16)(O}D/B{/aabz^`x$0+{M.}@}D/I{01px{/a XASaEOaG$)(/L{90 rotate 0 -1)(,FZ{P.}@}_}D)(15)(,0+{M.}@)(31)(#/FA0{w/jjv^`jG$)( X{10}{5})(0V|}"}{{0y?yy~y1~|}"}#]`/l{{5h$4Z{.}@M}_}D/C{/jA$z/vA1,0+)(270)(V|}"}{ X{1r?sr~s0~|}"}#]`/k[{1y?0y~|}"e{{01y)(90 180)(V|}"}{{1r?rr~rs~1s~|}"{1r?rr~r1~| X}"}#{1r?0r~n{s0?sr~r0?rr~}@|}"e{{10r)(180 270)(V1r~|}"{11s)(90 270)(x}D/z{[1o1P XoP]=:}D/J{1=;}D/G{c=:}D/t[e{{1s?yyq)(idiv)(=!}D/K{oN^}D/E{$o!x=o)(array/w{1 ^}D X`/x{1 &}D/h{rand)(2R/qA/ryq&`/syq^`/of2^`/pog2^N`/cp)(ge/e A/y .5 ` abs)({{1+}} X#"/dA/nA$0)(scale{{1 ge}})($/fAcN&2R/oAopOcc)(cvi)$/m{32 j}/F 2157 string/j{F = Xi $ 1 ^/i = ` = '}/i 0/v{forall}5{`}*($/gAcN&2R/pA$o&cR)=(3{0`}*/D{"`}"`/A{=`}D X/cA$^/pA$^/oA$p&cR)F{M m}v/i 0 `(/b/a/u)25{{j}v m{$ j 47 Z{m}@}v}* F cvx W end END_OF_FILE if test 2253 -ne `wc -c <'labyrinth.ps'`; then echo shar: \"'labyrinth.ps'\" unpacked with wrong size! fi # end of 'labyrinth.ps' fi if test -f 'labyrinth.unobfs.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'labyrinth.unobfs.ps'\" else echo shar: Extracting \"'labyrinth.unobfs.ps'\" \(17274 characters\) sed "s/^X//" >'labyrinth.unobfs.ps' <<'END_OF_FILE' X%! Expert Labyrinth for postscript printers, Copyright 1992 by David Bau X X.30 .15 % line weights (0..1) % This file is free puzzleware :) Please pass it X .70 % tunnel shape(-1..1) % on only for free, and keep my original comments X 20 % twistiness (0..100) % intact. And if you like it or improve it, send X true % tunnel crossings % me a postcard from your home town! X612 792 % page dimensions X 18 18 % page margins % 777 South Avenue; Weston, MA 02193; USA X 12 % cell size % bau@cs.cornell.edu X X/*def/def X/*repeat/repeat X/*lineto/lineto X/*mod/mod X/*if/if X X/*ifelse/ifelse X/*dup/dup X/*add/add X/*stroke/stroke X/*put/put X X/*gsave/gsave X/*exit/exit X/*and/and X/*sub/sub X/*or/or X X/*loop/loop X/*exch/exch X/*eq/eq X/*ne/ne X/*bind/bind X X/*get/get X/*bitshift/bitshift X/*exec/exec X/*mul/mul X/*translate/translate X X/*neg/neg X/*for/for X/*moveto/moveto X/*grestore/grestore X/*showpage/showpage X X/*dup/dup X/*arc/arc X/*pop/pop X/*setlinewidth/setlinewidth X/*div/div X X% make enough room for all our identifiers X72 dict begin X X% define synonyms for primitives X35{load def}repeat X X% procedures for the most common def sequences X/bdef {*bind *def} *bind *def X/xdef {*exch *def} bdef X X X% COMPUTE THE PAGE GEOMETRY %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X X% the point width and height of a cell X/cellsize xdef X X% the user's requested margins X*dup *add /ymargin xdef X*dup *add /xmargin xdef X X% compute the number of cells we can fit vertically on a page, X% then increase the ymargin so that the resulting maze will be X% precisely centered (important when ymargin is small) X*dup X ymargin *sub cellsize *div cvi *dup X /Y xdef X cellsize *mul *sub 2 *div /ymargin xdef X X% do the same for the x direction X*dup X xmargin *sub cellsize *div cvi *dup X /X xdef X cellsize *mul *sub 2 *div /xmargin xdef X X% move to that system of coordinates, so we don't have to worry X% about it any more. now a cell is one unit, and the maze begins X% with lower-left corner zero zero Xxmargin ymargin *translate Xcellsize cellsize scale X X X X% MAZE GENERATION PARAMETERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X X% okay is a procedure that tells if it's okay for the maze generator X% to proceed in a direction: given a count of how far the next empty X% cell is (jumping over perpendicular tunnels), it outputs true or X% false. In a 3D maze, we can jump over any number of tunnels (so the X% procedure is {1 ge}, and in a non-3D maze, we can only move if the X% empty square is adjacent, so the procedure is {1 eq} X{{1 ge}}{{1 *eq}} *ifelse *bind /okay xdef X X% twistiness will determine the probability of the maze generator X% making a turn. X/twistiness xdef X X X X% MAZE DRAWING PARAMETERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X X% curvy is true when corners are to be drawn with curves instead of X% with right angles. X*dup 0 ge /curvy xdef X X% gratuitous constant for future calculations X/half .5 *def X X% hc is half a corridor-width Xabs 2 *div /hc xdef X X% sr is the x coordinate of a left-side vertical wall, and also the y X% coordinate of a bottom-side horizontal wall. lr is the corresponding X% right- or upper- wall coordinate. The original meaning of "sr" is X% the "small radius" of a curvy corner, and "lr" is the "large radius." X/sr half hc *sub *def X/lr half hc *add *def X X X X% MORE DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X X% The maze is stored as integers in a one-dimensional array A that X% really is used as a two-dimensional array. The "width" of a single X% logical row of the array is W, and the total size of the array is S. X/W X 2 *add *def X/S W Y 2 *add *mul *def X/A S array *def X X% increment, decrement, and random-nonnegative-integer-less-than-n X/incr {1 *add} bdef X/decr {1 *sub} bdef X/nrand {rand *exch *mod} bdef X X% given a logical x and y coordinate, find the index into A X/coordpos { X W *mul *add X } bdef X X% given an index into A, find the x and y coordinate X/poscoord { X *dup W *mod decr *exch W idiv decr X } bdef X X% given a direction from zero to three, compute the offset in A X% required to move one step in that direction. direction numbers X% are chosen on purpose to go counterclockwise: 0=east, 1=north, X% 2=west, 3=south. X/dirjump { X [1 W -1 W *neg] *exch *get X } bdef X X% when we need to save directions in bitfields, dirbit translates a X% direction into a number with the appropriate bit set X/dirbit { X 1 *exch *bitshift X } bdef X X% given an index into A, look extracts the data stored in the array X/look { X A *exch *get X } bdef X X X% MORE MAZE DRAWING DEFINITIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X X% wallprocs is an array (0..3) that contains the procedures defining X% the right shape for walls connecting exits at various relative X% positions. most walls have a curvy version and a squarish version. X X/wallprocs X [ X % curvy version: X curvy { X % zero: draw a curvy dead end X {1 lr *moveto X half half hc 90 270 *arc X 1 sr *lineto X *stroke} *bind X X % one: draw a large radius arc X {1 1 lr 180 270 *arc *stroke} *bind X } X X % squarish version: X { X % zero: draw a squarish dead end X {1 sr *moveto sr sr *lineto sr lr *lineto X 1 lr *lineto *stroke} *bind X % one: draw a large radius corner X {1 sr *moveto sr sr *lineto sr 1 *lineto *stroke} *bind X } *ifelse X X X % both versions: X % two: draw straightaway, possibly with a crossing-under tunnel X {1 sr *moveto 0 sr *lineto X cross { X lr 0 *moveto lr sr *lineto X sr 0 *moveto sr sr *lineto X } *if X *stroke} *bind X X X % curvy version: X curvy { X % three: draw a small-radius arc X {1 0 sr 90 180 *arc *stroke} *bind X } X % squarish version: X { X % three: draw a small radius corner X {1 sr *moveto lr sr *lineto lr 0 *lineto *stroke} *bind X } *ifelse X X ] *def X X X X% pathprocs is a similar array of procedures defining the drawing for X% curvy and squarish solution paths. X X/pathprocs X [ X % zero: draw straightaway X {1 half *moveto 0 half *lineto *stroke} *bind X curvy { X % one: curvy elbow X {0 1 half 270 0 *arc *stroke} *bind X } { X % one: squared elbow X {0 half *moveto half half *lineto half 1 *lineto *stroke} *bind X } *ifelse X ] *def X X X% DIRECTION-CHOOSING PROCEDURES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X X% chooses a random direction; the loop is required due to the bad X% random number generators contained in some older printers. X/randdir X { X {5 nrand *dup 4 *ne {*exit} *if *pop} *loop X } bdef X X% Given a direction and a position in A, testdir counts how many steps X% in the direction we have to go from that position to get to an X% unmarked square (we can jump over marked squares whose marked bits X% are equal to the bits in the variable "passing" because these X% squares are the ones with straight tunnels perpendicular to our X% direction. If the given direction is completely blocked, the X% procedure returns a negative number. X/testdir { X /testing xdef X *dup dirjump X /jump xdef X 1 *and 0 *eq {10} {5} *ifelse X /passing xdef X 0 X { X incr X /testing testing jump *add *def X testing look X *dup 31 *and 0 *eq {*pop *exit} *if X 15 *and passing *ne {*neg *exit} *if X } *loop X } bdef X X% advancecurtest is not a real procedure; it is a macro which assumes X% there is a count on the stack, moves curpos one square in the X% direction given by curdir, and then decrements the count, executing X% {pop exit} if the count goes to zero. X/advancecurtest { X /curpos curpos curdir dirjump *add *def X decr *dup 0 *eq {*pop *exit} *if X } bdef X X X% MORE DRAWING PROCEDURES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X X% If the origin is at one corner of a maze cell, rotate move the X% origin to the next (counterclockwise) corner of the cell, rotating X% accordingly. X/cellrotate { X 90 rotate 0 -1 *translate X } bdef X X% drawmaze simply loops trhough all the positions of A and draws the X% walls implied by the exits indicated by the marked bits X/drawmaze { X % for each position in A X 0 1 S decr { X /curpos xdef X *gsave X X % get the position's x-y coordinates and translate there X curpos poscoord *translate X X curpos look *dup X % pat to be the bitpattern with a 1 indicating an exit direction X 15 *and /pat xdef X % cross indicates if a tunnel crosses underneath X 16 *and 0 *ne /cross xdef X X % search each direction... X 0 1 3 { X % when the first exit is found... X *dup dirbit pat *and 0 *ne { X % d1 is the last seen exit direction X % d2 is the direction in which we are looking now X /d1 xdef X /d2 d1 *def X % repeat at most four times... X 4 { X % look at the next direction (mod 4 increment) X /d2 d2 incr 4 *mod *def X % and move origin X cellrotate X % if there is an exit in this new direction, X d2 dirbit pat *and 0 *ne { X % draw the appropriate wall from d1 to d2 X wallprocs d1 4 *add d2 *sub 4 *mod *get *exec X % and update d1 X /d1 d2 *def X } *if X } *repeat X % once we have done all the drawing, exit X *exit X } *if X % if we haven't found any exits, move to next direction X cellrotate X *pop X } *for X *grestore X } *for X } bdef X X X X% drawsol follows the solution path and draws it in the maze X/drawsol { X % starting at the start position, going east X /curpos 0 B coordpos *def X /curdir 0 *def X { X % look at the current position for the direction to follow for X % the solution - it is the number stored in bits six and up. X /newdir curpos look -5 *bitshift *def X *gsave X % change the coordinate system depending on the current X % position and direction X curpos poscoord *translate X curdir {cellrotate} *repeat X % differentiate right turns from left turns in this step X newdir incr 4 *mod curdir *eq {cellrotate} *if X % draw one of two path shapes (elbow or straightaway) X pathprocs newdir curdir *add 2 *mod *get *exec X *grestore X % if we're at the end, stop X curpos endpos *eq {*exit} *if X % otherwise, advance curpos in the new direction by sliding X % underneath any crossing paths. X /curdir newdir *def X curdir curpos testdir *neg X { X advancecurtest X % this is the code to draw an "underneath" crossing pth X *gsave X curpos poscoord *translate X curdir 2 *mod {cellrotate} *repeat X 1 half *moveto lr half *lineto X sr half *moveto 0 half *lineto *stroke X *grestore X } *loop X } *loop X } bdef X X X% THE MAZE CALCULATION PROCEDURE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X { X X % CALCULATE THE MAZE ------------------------------------------------------ X % zero out the array A X 0 1 S decr {A *exch 0 *put} *for X X % but mark a bit in the four "edges" of A so our dead-end X % detecting will unwittingly also prevent us from going out of X % bounds X 0 1 W S W *sub 1 S 0 W S W decr W S X 4{decr {A *exch 16 *put} *for}*repeat X X % pick a random starting position and direction X /curpos X nrand incr Y nrand incr coordpos *def X /curdir randdir *def X X % label the backtracking direction from the current point as X % direction "4", a nondirection, so we will know when we're done. X A curpos 4 5 *bitshift *put X X % loop this: X { X % set curdir to.... X /curdir X % but wait, set cango to an array (0..3) so that X % the dth entry gives the result of d testdir X /cango [0 1 3 {curpos testdir} *for] *def X X % and if curdir is a direction in which we can go, and if X % a random twistiness test also fails, then, okay finally, X % set curdir to itself, and keep the same old direction X cango curdir *get okay 100 nrand twistiness ge *and X {curdir} X % otherwise, there is work to be done... X { X % is any direction "okay"? X false cango {okay *or} forall X % if so, choose a random one... X { X { X randdir *dup cango *exch *get okay {*exit} *if X *pop X } *loop X } X % if not, chose the nondirection 4 X {4} *ifelse X } *ifelse X % finally! set that number to curdir X *def X X % as long as we're talking about a real direction, X curdir 4 *ne { X % determine the backtracking direction (opposite curdir) X /backdir curdir 2 xor *def X % mark an exit in the current maze cell in the X % direction curdir X A curpos X curpos look X curdir dirbit X *or *put X % how many steps we need to take to go in curdir X curdir curpos testdir X { X % exit if only one step, else advance X advancecurtest X % flip the "crossover" bit in this cell, X % and randomly decide whether to cross over X % or under, and flip appropriate bits X A curpos curpos look 16 *or X 6 nrand 3 lt { X 15 xor X 31 *and X % if we cross over, we need to save the backdir X backdir 5 *bitshift *or X } *if *put X } *loop X % when curpos is advanced to the empty cell, we need to X % mark the backtracking direction as an exit, and we also X % have to save the backtracking direction for backtracking X A curpos X curpos look X backdir X *dup 5 *bitshift X *exch dirbit *or X *or *put X } X X % ... if we're not talking about a real direction, backtrack X { X % retreive the backtracking direction X /curdir curpos look -5 *bitshift *def X % if it's also not a real direction, we're done! yay! X curdir 4 *eq {*exit} *if X % otherwise, jump all the way back using testdir X /curpos X curdir curpos testdir *neg X curdir dirjump *mul X curpos *add X *def X } *ifelse X } *loop X X % now, randomly select a beginning and ending point for the maze X { X /B Y nrand incr *def X /E Y nrand incr *def X E B *sub abs Y X 2 *div *sub ge {*exit} *if X } *loop X X % flip bits at beginning and ending to graft in entrance and exit X % corridors X A 1 B coordpos *dup look 4 *or *put X A 0 B coordpos 5 *put X A X E coordpos *dup look 1 *or *put X A X E coordpos incr 5 *put X X % CALCULATE THE SOLUTION -------------------------------------------------- X % now, we go about the business of solving the maze X /endpos X incr E coordpos *def X % start at the beginning, going east X /curpos 0 B coordpos *def X /curdir 0 *def X { X % if we're at the end position, then we're done X curpos endpos *eq {*exit} *if X % otherwise, if we can move in the current direction, X curpos look curdir dirbit *and 0 *ne X { X % mark here that we are moving in curdir direction, X % erasing any previous marked direction X A curpos X curpos look 31 *and X curdir 5 *bitshift *or *put X % move to our new location based on testdir X /curpos X curpos X curdir curpos testdir *neg X curdir dirjump *mul *add *def X % when we get there, begin by turning left! X /curdir curdir incr 4 *mod *def X } X % if we can't move in the current direction, X % turn left X { X /curdir curdir 3 *add 4 *mod *def X } *ifelse X } *loop X } *bind *exec X X% once the maze is calculated, draw the maze X*setlinewidth Xdrawmaze X X% and if the solution pathwidth is not zero,... X*dup 0 *ne X { X % page out X *gsave *showpage *grestore X % draw the maze... X drawmaze X % and at another line width, draw the solution. X *setlinewidth X drawsol X } X {*pop} *ifelse X*showpage X Xend END_OF_FILE if test 17274 -ne `wc -c <'labyrinth.unobfs.ps'`; then echo shar: \"'labyrinth.unobfs.ps'\" unpacked with wrong size! fi # end of 'labyrinth.unobfs.ps' fi if test -f 'literary.ANSWER' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'literary.ANSWER'\" else echo shar: Extracting \"'literary.ANSWER'\" \(1002 characters\) sed "s/^X//" >'literary.ANSWER' <<'END_OF_FILE' XThis from Michael Spring: X XThe code we have sent is derived from a development effort I have been Xengaged in to prototype virtual worlds. We have not provided any of Xthe actual code, which is quite unobfuscated and useful. The code Xenables the rapid prototyping and view of a virtual space constructed Xquickly and simply from data sets. It allow the user to look at the Xspace from many different perspectives. One of my students used the Xheader recently to visualize the IETF RFC data base! X X"literary.ps" represents the closest approximation to our current work X-- that is, imaging based upon real data from non postscript sources. XTo protect the innocent we have, I hope thoroughly, obfuscated the Xdata source by making it something literary. You can draw different Ximages by including different text passages! X XWhat you will not see in the submission, because we can't quickly Xobfuscate them, are the more significant headers based on real data Xthat use postscript to navigate virtual worlds. END_OF_FILE if test 1002 -ne `wc -c <'literary.ANSWER'`; then echo shar: \"'literary.ANSWER'\" unpacked with wrong size! fi # end of 'literary.ANSWER' fi if test -f 'literary.HINT' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'literary.HINT'\" else echo shar: Extracting \"'literary.HINT'\" \(434 characters\) sed "s/^X//" >'literary.HINT' <<'END_OF_FILE' Xliterary.ps by David Dubin (dsdst3@icarus.lis.pitt.edu) and XMichael Spring (spring+@pitt.edu) is a "virtual world" picture. X XBEST OBFUSCATED ARTWORK -- 3rd prize X -- The 2nd most coveted prize. These combine obfuscation with great artwork. X XWhat happens when you change the text at the bottom? XWhat happens when you change xlight and ylight? XCan you tell how the z variable is used? X XWhy was sethsbcolor used instead of setrgbcolor? END_OF_FILE if test 434 -ne `wc -c <'literary.HINT'`; then echo shar: \"'literary.HINT'\" unpacked with wrong size! fi # end of 'literary.HINT' fi if test -f 'literary.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'literary.ps'\" else echo shar: Extracting \"'literary.ps'\" \(2240 characters\) sed "s/^X//" >'literary.ps' <<'END_OF_FILE' X%!PS-Adobe-2.0 X%%Creator: Michael B. Spring and David S. Dubin X%%For: Whom the Bell Tolls X%%Title: literary.ps X%%CreationDate: January 6, 1993 X X/res 2 def X/xlight {1.5 div neg } def X/ylight {2 div } def X X X/Helvetica-Bold findfont 30 scalefont setfont X/sa 1000 array def X/i 0 def X X/macbeth {999 eq X {bacon /i i 2 sub def X 0 browne X 0 1 i {gordon}for X showpage X currentfile token pop exit X } if X } def X X/bacon { gsave 306 396 translate X 1 xlight 1 ylight atan neg rotate X res 10 mul setlinewidth X -600 res 8 mul 600 {dup 1100 add 1600 div setgray X -600 exch moveto 1200 0 rlineto stroke X } for X grestore X } def X X/gordon {/n exch def res setlinewidth sa n X get dup 0 get 600 mod /x exch def dup 1 get X 700 mod /y exch def dup 2 Xget /z exch def X dup 3 get 100 mod 10 add /r exch def X dup 4 get 1000 mod 1000 div /h exch def 5 get X 1000 mod 1000 div .6 Xadd /b exch def X /xdelta r xlight def /ydelta r ylight def X newpath X res setlinewidth X h 1 b sethsbcolor x y r 0 360 arc fill gsave x y r 0 360 X arc clip X /hx x xdelta add def /hy y ydelta add def X r res neg 0 {/hr exch def X h hr r div b 1 hr r div sub .25 mul add sethsbcolor X hx hy hr 0 360 arc fill X } for X /sr r 2.5 mul def X sr res neg r {/tr exch def h X 1 r tr div b mul sethsbcolor X hx hy tr 0 360 arc stroke X } for X grestore X } def X X/browne {/j exch def X j 1 i {/k exch def sa k get 2 get sa j get 2 get X lt {sa k get sa j get sa exch k exch put sa exch j exch put}if X } for X /j j 1 add def X j i lt {j browne} if X }def X X X/loop-de- { Xsa i [ X6 {currentfile token pop dup type 1 type ne X{20 string cvs dup dup length dup rand exch mod 3 1 roll rand exch mod Xget 3 1 roll get mul }if } repeat X] put sa i get 1 get /i i 1 add def macbeth X } def X X{loop-de-} loop XThe world's a bubble; and the life of man Less than a span. XLife is mostly froth and bubble, Two things stand like stone, Xand thus far we may maintain the music of the spheres; XAnd chase the new-blown bubbles of the day. XHe that but once too nearly hears The music of forfended spheres XWith beaded bubbles winking at the brim, XThe earth hath bubbles, as the water has, And these are of them. X999 999 999 999 999 999 X%%EOF END_OF_FILE if test 2240 -ne `wc -c <'literary.ps'`; then echo shar: \"'literary.ps'\" unpacked with wrong size! fi # end of 'literary.ps' fi if test -f 'literary.unobfs.ps' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'literary.unobfs.ps'\" else echo shar: Extracting \"'literary.unobfs.ps'\" \(4583 characters\) sed "s/^X//" >'literary.unobfs.ps' <<'END_OF_FILE' X%!PS-Adobe-2.0 X%%Creator: Michael B. Spring and David S. Dubin X%%For: Whom the Bell Tolls X%%Title: literary.ps X%%CreationDate: January 6, 1993 X X%% This program builds a big array of arrays. Each sub array has 6 values X%% which are taken from text at the end of the program. After reading X%% the text into the big array (end of file is marked with 999), then X%% the big array is printed out by drawing bubbles at the location X%% given by the sub arrays. First, however, the big array is sorted X%% by z value. X X/res 2 def % how thick do we want the diagonal lines X/xlight X{ X 1.5 div neg X} def X X/ylight X{ X 2 div X} def X X/bigarray 1000 array def X/i 0 def X X% draws the diagonal lines that appear first in the image. X/diaglines X{ X gsave X 306 396 translate X 1 xlight 1 ylight atan neg rotate X res 10 mul setlinewidth X -600 res 8 mul 600 X { X dup 1100 add 1600 div setgray X -600 exch moveto X 1200 0 rlineto X stroke X } for X grestore X} def X X% this routine is fed one random number from stack. "i" is set to X% point into bigarray where array of 6 awaits. X% X% This is some kind of cleanup. Only execute at end of program. X/usebigarray X{ X 999 eq % marker for end of program. X { X diaglines X /i i 2 sub def X 0 bubble-sort % not needed???? X 0 1 i % run through the bigarray and do something for each element X { X draw-a-bubble X } for X showpage % new page. End of program. X currentfile token pop exit % grab the EOF???? Exit from the loop. X } if X} def X X/draw-a-bubble X{ X% res setlinewidth % unneeded X bigarray exch get % take the array-of-6 out of index X dup 0 get 600 mod /x exch def % the first is an x value X dup 1 get 700 mod /y exch def % the second is a y value X dup 2 get /z exch def % the third is a z value. z?? X dup 3 get 100 mod 10 add /r exch def % the fourth is a radius value. X dup 4 get 1000 mod 1000 div /h exch def % the fifth is hue X 5 get 1000 mod 1000 div .6 add /b exch def % the sixth is brightness X /xdelta r xlight def X /ydelta r ylight def X newpath X res setlinewidth X h 1 b sethsbcolor X x y r 0 360 arc fill X gsave X x y r 0 360 arc clip X /hx x xdelta add def X /hy y ydelta add def X X %% draw the lighter part X r res neg 0 X { X /hr exch def X h hr r div b 1 hr r div sub .25 mul add sethsbcolor X hx hy hr 0 360 arc X fill X } for X X %% draw the darker part X /sr r 2.5 mul def X sr res neg r X { X /tr exch def X h 1 r tr div b mul sethsbcolor X hx hy tr 0 360 arc X stroke X } for X grestore X} def X X% sort the bigarray bubbles by z value. Oh, that's what z is used for. X/bubble-sort X{ X /j exch def X j 1 i X { X /k exch def X bigarray k get 2 get bigarray j get 2 get lt X { X bigarray k get bigarray j get bigarray exch k exch put X bigarray exch j exch put X }if X } for X /j j 1 add def X j i lt X { X j bubble-sort X } if X}def X X%% This loop makes an array and puts it into "bigarray" at the ith position. X%% Then it increments i. X{ X bigarray i X [ X 6 % read in six words and convert the "executable name" to string. X { X % read in a word and see if it's an integer X currentfile token pop dup type /integertype ne X % now get a number which is the last character times a randomly X % chosen character in the string. Don't do this if it's an integer. X { X 20 string cvs % convert implied variable into string X dup dup length % string string length X dup rand exch mod % string string length randmodlength X 3 1 roll rand exch mod % string randmodlength string length X get % string randmodlength lastchar X 3 1 roll get mul % lastchar times randomchar X } X if X } repeat X ] X X % now we have an array of 6 quasi-random numbers X put % put the array into the bigarray at position i. X bigarray i get 1 get % get the first value from array above. X /i i 1 add def % increase i by one. X usebigarray % feed first value into usebigarray. X} loop XThe world's a bubble; and the life of man Less than a span. XLife is mostly froth and bubble, Two things stand like stone, Xand thus far we may maintain the music of the spheres; XAnd chase the new-blown bubbles of the day. XHe that but once too nearly hears The music of forfended spheres XWith beaded bubbles winking at the brim, XThe earth hath bubbles, as the water has, And these are of them. X999 999 999 999 999 999 X%%EOF END_OF_FILE if test 4583 -ne `wc -c <'literary.unobfs.ps'`; then echo shar: \"'literary.unobfs.ps'\" unpacked with wrong size! fi # end of 'literary.unobfs.ps' fi echo shar: End of shell archive. exit 0