diff options
Diffstat (limited to 'bench/example.rubyfast')
-rw-r--r-- | bench/example.rubyfast | 10428 |
1 files changed, 0 insertions, 10428 deletions
diff --git a/bench/example.rubyfast b/bench/example.rubyfast deleted file mode 100644 index a3367d1..0000000 --- a/bench/example.rubyfast +++ /dev/null @@ -1,10428 +0,0 @@ -#n ist das anzuzeigende Feld, o die Zwischenablage für eine #Iteration p dient dazu zu jedem Feld die Nachbarschaft zu definieren. -n=Array.new -o=Array.new -p=Array.new -x=0 - -#Anlegen des Arrays n -while x<=10000 - n[x]=0 - x +=1 -end - -#Anlegen des Arrays p -x=0 -while x<=10000 - p[x]=3 - x +=1 -end - - -n[2]=1 -n[102]=1 -n[202]=1 - -loop{ -x=0 -#Die nachbarschaften aller Felder werden überprüft -while x<10000 -#a bis f dienen dazu die Nachbarschaft festzulegen. Man stelle sich die #Zahl von 1 bis 64 im Binärcode vor 1 bedeutet an 0 aus - a=p[x]/32<1 ? 0 : 1 - b=(p[x]%32)/16<1 ? 0 : 1 - c=(p[x]%16)/8<1 ? 0 : 1 - d=(p[x]%8)/4<1 ? 0 : 1 - e=(p[x]%4)/2<1 ? 0 : 1 - f=(p[x]%2)<1 ? 0 : 1 -#t= n[x-201].to_i*b+n[x-200].to_i*d+n[x-199].to_i*b+ - #n[x-102].to_i*a+n[x-101].to_i*e+n[x-100].to_i+n[x-99].to_i*f+n[x-98]#to_i*a+ - #n[x-2].to_i*c+n[x-1].to_i+n[x+1].to_i+n[x+2].to_i*c+ - #n[x+98].to_i*a+n[x+99].to_i*f+n[x+100].to_i+n[x+101].#to_i*e+n[x+102].to_i*a+ - #n[x+199].to_i*b+n[x+200].to_i*d+n[x+201].to_i*b - #Die Summe der Felder die zur Nachbarschaft gerechnet werden a bis f -#sind hierbei Multiplikatoren mit dem Wert 1oder 0 - -t=(x-201>=0? n[x-201].to_i : 0)*b+(x-200>=0? n[x-200].to_i : 0)*d+(x-199>=0? n[x-199].to_i : 0)*b+ - (x-102>=0? n[x-102].to_i : 0)*a+(x-101>=0?n[x-101].to_i : 0)*e+n[x-100].to_i+(x-99>=0? n[x-99].to_i : 0)*f+(x-98>=0? n[x-98].to_i : 0)*a+ - (x-2>=0? n[x-2].to_i : 0)*c+(x-1>=0? n[x-1].to_i : 0)+n[x+1].to_i+n[x+2].to_i*c+ - n[x+98].to_i*a+n[x+99].to_i*f+n[x+100].to_i+n[x+101].to_i*e+n[x+102].to_i*a+ - n[x+199].to_i*b+n[x+200].to_i*d+n[x+201].to_i*b - -#Bedingungen wann eine Zelle lebt,stirbt oder geboren wird im Moment -#sind die regeln 3 Nachbarn =Geburt und Nachbarn 3,2=Überleben -#sonst Tod - if t==3 - o[x]=1 - elsif t==2 and n[x]=1 - o[x]=1 - else - o[x]=0 - end - x+=1 -end -#wird überschrieben -n=o - -#und die Ausgabe folgt -g=%w{} -x=0 - -while x<100 - g[x]=n[100*x+1..100*x+100] - x+=1 -end -x=0 - -while x<100 - puts"#{g[x]}" - x+=1 -end - -puts"" -sleep(10) -} - -1E1E1 -puts 30.send(:/, 5) # prints 6 - -"instance variables can be #@included, #@@class_variables and #$globals as well." - -#%W[ but #@0illegal_values look strange.] - -%s#ruby allows strange#{constructs}. -%s#ruby allows strange#$constructs -%s#ruby allows strange#@@constructs - -%r\VERY STRANGE!\x00 - -~%r#<XMP>#i .. ~%r#</XMP>#i; - -a = <<"EOF" -This is a multiline here document -terminated by EOF on a line by itself -EOF - -b=(p[x] %32)/16<1 ? 0 : 1 - -<<"" -#{test} -#@bla -#die suppe!!! -\xfffff - -super <<-EOE % [ - EOE - -<<X -X -X -%s(uninter\)pre\ted) -%q(uninter\)pre\ted) -%Q(inter\)pre\ted) -:"inter\)pre\ted" -:'uninter\'pre\ted' - -%q[haha! [nesting [rocks] ! ] ] - - -################################################################## -class NP -def initialize a=@p=[], b=@b=[]; end -def +@;@b<<1;b2c end;def-@;@b<<0;b2c end -def b2c;if @b.size==8;c=0;@b.each{|b|c<<=1;c|=b};send( - 'lave'.reverse,(@p.join))if c==0;@p<<c.chr;@b=[] end - self end end ; begin _ = NP.new end -c -# ^ This is a bug :( - -# The Programming Language `NegaPosi' -+-+--++----+--+-+++--+-------+--++--+++---+-+++-+-+-+++-----+++-_ -+--++++--+---++-+-+-+++--+--+-+------+--++++-++---++-++---++-++-_ -+++--++-+-+--++--+++--+------+----+--++--+++-++-+----++------+--_ --+-+----+++--+--+----+--+--+-++-++--+++-++++-++-----+-+-+----++-_ ----------+-+---- _ -################################################################## - - -# date: 03/18/2004 -# title: primes less than 1000 ( 2005 Obfuscated Ruby Contest ) -# author: Jim Lawless -# email: jimbo at radiks dotski net -# comments: This program will display all positive prime integers -# less than 1000. Program licens is the same as the Ruby -# license ( http://www.ruby-lang.org/en/LICENSE.txt ) - - $e="" - -def a() - $a=$a+1 -end - -def b() - $a=$a+5 -end - -def c() - $e=$e+$a.chr -end - -def d() - $a=10 -end - -def e() - $a=$a+16 -end - -d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c;d;e;e;e;a;a;a;c;d;e;e;b;b; -a;c;d;c;d;e;e;e;e;e;e;b;b;a;a;a;c;d;e;e;e;e;e;b;b;a;a;a;a;c;d;e;e;e;e; -e;b;b;a;a;a;a;a;c;d;e;e;e;e;e;e;a;a;c;d;e;e;e;e;e;b;b;a;c;d;e;b;a;c; -d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c;d;e;e;e;a;a;c;d;e;e;b;a;a; -c;d;e;e;b;a;c;d;e;e;b;a;c;d;e;e;b;a;c;d;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;b; -b;a;a;a;a;a;c;d;e;e;e;a;a;a;c;d;e;e;b;a;a;a;c;d;c;d;e;b;a;a;a;a;a;c;d;e; -e;e;e;e;b;b;a;a;c;d;e;e;e;e;e;e;a;a;a;a;a;c;d;e;e;e;e;e;e;b;b;a;c; -d;e;e;e;e;e;e;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a;a;a;c;d;e;e;e;a;a;a;c;d;e; -e;b;a;c;d;c;d;e;e;e;e;e;e;b;b;a;a;a;c;d;e;e;e;e;e;b;b;a;a;a;a;c;d;e;e; -e;e;e;b;b;a;a;a;a;a;c;d;e;e;e;e;e;e;a;a;c;d;e;e;e;e;e;b;b;a;c;d;e;b; -a;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;b;b;a;a;a;a;a;c;d;e;e;e;a;a;c;d;e;b; -a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c;d;e;b;a;c;d;c;d;e;e;e;e;e;b;b;a; -a;a;a;a;c;d;e;e;e;e;e;b;b;a;a;c;d;e;b;b;a;a;a;a;c;d;e;b;a;c;d;e;b;b;a; -a;a;a;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c;d;e;e;a;a;a;a;c; -d;e;e;e;e;e;e;a;a;a;c;d;e;e;e;e;e;e;a;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a; -a;a;c;d;e;e;e;e;e;e;b;b;a;c;d;e;e;e;e;e;e;a;a;c;d;e;e;e;e;e;e;a;a;a; -a;a;c;d;e;b;b;a;a;a;a;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;b;b;a;a;a;a;a; -c;d;e;b;b;a;a;a;a;a;c;d;e;b;b;a;a;a;a;a;c;d;e;e;e;a;a;a;c;d;e;e;e;a;a; -a;c;d;e;e;b;a;c;d;e;b;b;a;a;a;a;a;c;d;e;b;a;c;d;c;d;e;b;a;a;a;a;a;c;d;e;e; -e;e;e;b;b;a;a;c;d;e;e;e;e;e;e;a;a;a;a;a;c;d;e;e;e;e;e;e;b;b;a;c;d;e; -e;e;e;e;e;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a;a;a;c;d;e;e;e;a;a;a;c;d;e;e; -b;a;a;c;d;c;d;e;e;e;e;e;b;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;c;d;e;e;e;e;e; -b;b;a;c;d;e;e;e;e;e;b;a;a;c;d;e;e;e;e;e;e;a;c;d;c;d;e;e;e;e;e;b;b;a;c; -d;e;e;e;e;e;e;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a;a;a;c;d;c;d;e;b;a;a;a;a;a; -c;d;e;e;e;e;e;b;b;a;a;a;a;a;c;d;e;e;e;a;a;a;c;d;e;b;a;a;a;a;a;c;d;e;e; -e;e;e;b;b;a;a;a;a;a;c;d;e;e;a;c;d;e;e;b;a;a;c;d;c;d;e;e;e;e;e;b;b;a;a; -a;a;a;c;d;e;e;e;e;e;b;b;a;a;c;d;e;b;a;c;d;e;b;b;a;a;a;a;c;d;e;b;b;a;a; -a;a;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;b;b;a;a;a;a;a;c;d;e;b;b;b;a;c;d;e; -b;a;a;a;a;a;c;d;e;e;e;e;e;b;b;a;a;a;a;a;c;d;e;b;b;a;a;a;a;a;c;d;e;e; -e;a;a;a;a;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c;d;e;b;b;a;a; -a;a;a;c;d;c;d;e;e;e;e;e;b;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;c;d;e;e;e;e;e; -b;b;a;c;d;e;e;e;e;e;b;a;a;c;d;e;e;e;e;e;e;a;c;d;c;d;e;e;e;e;e;b;b;a;c; -d;e;e;e;e;e;e;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a;a;a;c;d;c;d;e;e;e;e;e;b;b; -a;c;d;e;e;e;e;e;e;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a;a;a;c;d;c;d;e;e;e;e;e; -b;b;a;a;a;a;a;c;d;e;e;e;e;e;b;b;a;a;c;d;e;b;b;a;a;a;a;c;d;e;b;a;a;a; -a;a;c;d;e;e;e;e;e;b;b;a;a;c;d;e;e;e;e;e;e;a;a;a;a;a;c;d;e;e;e;e;e;e; -b;b;a;c;d;e;e;e;e;e;e;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a;a;a;c;d;e;e;e;a; -a;a;c;d;e;e;e;a;a;a;c;d;e;e;b;a;c;d;e;b;b;a;a;a;a;a;c;d;e;b;a;c;d;c;d;e;e; -e;e;e;e;b;a;c;d;e;e;e;e;e;e;b;b;a;c;d;e;e;e;e;e;e;b;a;a;a;a;a;c;d;e; -e;e;e;e;e;b;a;a;a;a;c;d;e;b;a;c;d;e;b;a;a;a;c;d;e;b;a;a;a;a;c;d;e;e;e; -e;e;e;e;a;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c;d;e;e;e;e;e; -e;e;a;a;a;c;d;e;b;a;c;d;e;e;e;e;e;b;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a; -a;a;a;c;d;e;b;a;c;d;e;e;e;e;e;e;b;a;c;d;e;e;e;e;e;e;b;a;a;a;c;d;e;e;e; -e;e;b;b;a;a;a;a;a;c;d;e;e;e;e;e;e;a;a;a;c;d;e;e;e;e;e;b;b;a;c;d;e;b; -a;a;a;c;d;c;d;e;e;e;e;e;b;b;a;c;d;e;e;e;e;e;e;a;a;a;a;c;d;e;e;e;e;e;b; -a;a;a;a;a;c;d;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c;d;e;b;a;c; -d;e;e;e;a;a;a;c;d;e;b;a;c;d;e;b;a;a;a;a;a;c;d;e;e;e;e;e;e;b;a;a;a;a;c; -d;e;b;a;c;d;e;e;a;c;d;e;b;a;c;d;e;e;b;a;a;c;d;c;d;e;e;e;e;e;b;b;a;c;d;e;e;e; -e;e;e;a;a;a;a;c;d;e;e;e;e;e;b;a;a;a;a;a;c;d;c;d;e;b;a;c;d;e;b;a;c;d;e;b; -a;c;d;e;b;a;c;d;e;b;a;c;d;e;b;a;c;d;e;b;a;c;eval $e - -$_=%{q,l= %w{Ruby\\ Quiz Loader} -n,p,a= "\#{q.do#{%w{w a n c}.sort{|o,t|t<=>o}}se.d\x65l\x65t\x65(' ')}.com/", -{"bmJzcA==\n".\x75np\x61ck("m")[0]=>" ","bHQ=\n".\x75np\x61ck((?n-1).chr)[0]=> -:<,"Z3Q=\n".\x75np\x61ck("m")[0]=>:>,"YW1w\n".\x75np\x61ck((?l+1).chr)[0]=>:&}, -[[/^\\s+<\\/div>.+/m,""],[/^\\s+/,""],[/\n/,"\n\n"],[/<br \\/>/,"\n"], -[/<hr \\/>/,"-="*40],[/<[^>]+>/,""],[/^ruby/,""],[/\n{3,}/,"\n\n"]];p\165ts" -\#{l[0..-3]}ing...\n\n";send(Kernel.methods.find_all{|x|x[0]==?e}[-1], -"re\#{q[5...8].downcase}re '111112101110-117114105'.scan(/-|\\\\d{3}/). -inject(''){|m,v|v.length>1?m+v.to_i.chr: m+v}");o#{%w{e P}.sort.join.downcase -}n("http://www.\#{n}"){|w|$F=w.read.sc\x61n(/li>.+?"([^"]+)..([^<]+)/)};\160uts\ -"\#{q}\n\n";$F.\145\141ch{|e|i=e[0][/\\d+/];s="%2s. %s"%[i,e[1]];i.to_i%2==0 ? -\160ut\x73(s) : #{%w{s p}[-1]}rint("%-38s "%s)};p\x72\x69\x6et"\n? ";e\x76al( -['puts"\n\#{l[0..3]}ing...\n\n"','$c=gets.chomp.to_i'].sort.join(";"));#{111.chr -}pen("http://www.\#{n}"+$F[$c-1][0]){|n|$_=n.read[/^\\s+<span.+/m];#{('a'.."z"). -to_a[10-5*2]}.e\141ch{|(z,f)|\x67sub!(z,f)};\147sub!(/&(\\w+);/){|y|p. -ke\171\077($1)?p[$1]:y};while$_=~/([^\n]{81,})/:z=$1.dup;f=$1.dup;f[f.rindex( -" ",80),1]="\n";f.s\165b!(/\n[ \t]+/,"\n");s\165b!(/\#{R\x65g\x65xp. -\x65scap\x65(z)}/,f)end};while\040\163ub!(/^(?:[^\n]*\n){20}/, ""):puts"\#$& ---\x4dO\x52E--";g=$_;g#{"\145"}ts;;#{"excited"[0..4].delete("c")}\040if$_[0]==?q -$_=g;end;$_.d#{"Internet Service Provider".scan(/[A-Z]/).join.downcase -}lay};eval$_ - - d=[30644250780,9003106878, - 30636278846,66641217692,4501790980, - 671_24_603036,131_61973916,66_606629_920, - 30642677916,30643069058];a,s=[],$*[0] - s.each_byte{|b|a<<("%036b"%d[b. - chr.to_i]).scan(/\d{6}/)} - a.transpose.each{ |a| - a.join.each_byte{\ - |i|print i==49?\ - ($*[1]||"#")\ - :32.chr} - puts - } - -#! /usr/bin/env ruby -# License: If Ruby is licensed to the general public in a certain way, this is also licensed in that way. -require'zlib';eval(Zlib::Inflate.inflate("x\332\355WKo\333F\020\276\367W\250\262\001\222\tM\357\246M\017\242\211\242h\200\036\212`\201\026\350\205`\f=h\233\301Zt%\273A-2\277\275\363\315\222\334\241,#v\214\366T\331\262\326\303y\3177\263\243M\371\347]\265)\203UuYnoO\257Wo\203\364>[T\353U\265\276L\257\353\325\235-'\277\226\233ui\323Uy1\251\027\027\341\253\371\346r\e\245u\366\216\205f\263\367\357\336&\353\362S\010zr=\277\3315w\315]r[\237o\333\344c]\255#>\343O\025\352\037\334\177\341\367\364\271\t\003\245\337|\027\304\364aM@:\363\260\316>\237\232\323(\326\252(\327\253\t\275\323\332h\253\224V\306d\247\037\362\371\311}\321\314f\356\363C\016\311\342\365\361ij\026\037\313\345\355\3577\363e\231\224\363\345\325y\315\204]\263l\3620\177\317\241\024M\376\263\235o\267Et\222/\223%\037\213\374D\323\373M\3214Kv-\373<\361\026\233&\\\304\253,\354\270\263\314)\232\3748\311\247]z\216v\3136\235\306\323\243\035\262\263\214\332\f\024\342\257\327\345\264\230\205\313o36\3122\254e2\260\236\2610\202\354\037\260\256 (f=/\313:Z\024\245\313\244Zoo\347\353ey~]\336^\325\253-\a\273k\252fqv6\235\333j\276\355\236tV\252\230\377F\276\n\333\277\257\241\345\206\262\323\306G\273\352\340\203t\332\246\2441`'\316\316\266\245\275H\0032\377l\253\017,=42E\002\360\236\246\345_s;Y\274^\305\367Q\233\036\233\276\016\312\2450=\256=\305U\202\230\254\"\222\265\004\217\237~\373\345\017\"h\243\210\307j\235\251\205V8\353\304X\372!1CGc-\251\240\337\020\317\361#\036\023\n\2556\254Cg3\002}\265\356s\235\202K[K\022\020 \243\206\216\241p3\33255\350\232\036\030q$\233\344!\363\204^},$\023Xg\235:\364r1\"1\344\277\261\207\031(\301DE\260\344\026Y\177\345\036\221\204mP\263\266Mk\305\366\210%3\220\302S\322\306IR\316\377!\203 S\336\310\216\215\203\315\002-\211 5D2\257\210\302\321p\234\364\205\222Jj\220\022E\321h\347\223RQ*94K\022\243\314H`4{LV\003\021N\f\333\364I\347l\327UR\305t\340\332i>\241x=Mu4R\245\373\223\244\251NB\211\247\236\3465\253^bx\332Yc\263\252M\220b\253\220\310\004\331\242\020,`\005T\021Y\251P@\020\365Ax\310z\364\264\240\265vj2\037?0\v\"en\244\374\251\032\225\253v\346\253\3712\215\032\322(o\206~A\006\010\f\324\22357\026\"\316\024\365\021\360@\277:\363.$\f\342\016$\200\v\341\302\230\020\340\341\201K\017\270+i\326-\312\313j\235\n[\376({\330u\254\266\334\034\031\367%:CK\210{\311h\aQH\333Q\023\250\210;e\360\322\362\213\202\247\216\266\340C&(p\274HT7\336&B\352\300\036z\206\204\375 \032z\304\233\217\034\267AK\207R\363\213\324u\334\203\272h\234 \304&\364S\302]|\024\233b\000\023E\034\005\300!\330\2274\026\205\316\363\203\364\"\316\245!\242\360Y?4\204b\023.\2009\036X\300\213p\200]\304\324\200$^\204\025\222D\325X \363\324\004\223\205\207\241M\245\352\341(s\3415\260w\226\313=\2422 \200\177\344\355\211\3350\004\341\217\207\215r%x\030\302\304\230\335{#\250#o\204h\327;\220\242\275B%j&\343e\005\226/\r\200\035\035\206K\243\027\216Z\230\323.\335\356^!\vF\002K\366\246kG\321\364E\301\362\250\275a\f\031\207i%\216\342&ie\205\260\324}\272\252ho\222\306\370\362!}6\364C\003\2717\206'!.\315\036mhMm\370\252\241\365\221g\275\326A\302\254\270X,\371\353\232:\222\321\253\025\217v%\222\023!\243r\272\364(\376\177\236\374\233\363\3048\330b\241xdTp\325\321\377\3428F\234\214\263\357\255f\324\306\226\257\022\"\000\354\003\024C\207\na\353\240&O\305\376\004ncy\350\f\276\357+Q|\201bBi\206\277\345u\251\273\310\367\242\303*\204d\n\271}\016\2345r8\034\201[\343:>\364*\242\266\025+HZ\263e\212\0247q\357\310X\267[\333(9_o}P\201\324>\266\364\000\217hh\352\225a\213q\260\031\334\022sg\360\e\206\234B=\246\2421\341e\364\270\321\224\347\0056L\267\227)\244\210\307\027\257<\343\257\000\303\264u{\235\326\352i\303^\332\200\n\236\243a\277\034J#~S\335'2\371\001q\3745$\356\027^\371\325\344\331\036\362\004\267\330\251<\212\237\257\345kr\371\302d\362r\376\344d\252C\311\374R6\017e\375\005\271yAV\363/\257\345\261(\340hW\020\222\a\027k)60\354\217\363\3501\263rt\0364\025\025|\265\031\355\276d\357\3159\367\225\025\223U\273n\027\324\321H\031\030\036\357\356\377\010\266\337\374\003\3375Q\335")) -#include "ruby.h" /* - /sLaSh * - oBfUsCaTeD RuBy * - cOpYrIgHt 2005 * -bY SiMoN StRaNdGaArD * - #{X=320;Y=200;Z=20} */ - -#define GUN1 42: -#define GUN2 43: -#define bo do -#define when(gun) /**/ -#define DATA "p 'Hello embedded world'" -#define DIRTY(argc,argv)\ -argc,argv,char=eval(\ -"#{DATA.read}\n[3,2,1]"\ -);sun=O.new\ -if(0) - -int -sun[]={12,9,16,9,2,1,7,1,3,9,27,4, 13,2,11,5,4,1,25, -5,0,1,14,9,15,4,26,9,23,2,17,6,31, 6,10,8,22,9,21,1, -24,8,20,8,18,9,29,5,9,5,1,1,28,8,8,1,30, 9,6,8, 5,1, -19,9,36,19,43, 9,34,11,50,19,48,18,49,9, 35,8,42,18, -51,8,44,11,32, 11,47,9,37,1,39,9,38,19, 45,8,40,12, -41,9,46,12,33,1,57,1,85,5,88,28,83,4,87, 6,62,28,89, -9,80,28,60,21,52,21,72,29,54,21,75,8,70,29,58,28,65, -9,91,8,74,29,79,2,77,1,53,1,81,5, 69,2,64,21, 86,29, -67,9,59,1,61,5,73,6,76,28,56,21,68,29,78,29,63,5,66, -28,90,29, 71,4,55,9,84,28,82,29,101,5, 103,9, 98,35, -97,1,94,35,93,1,100,35,92,31,99,5,96,39,95,5,102,35}; - -void run(int gun=0) { // [gun]=[:GUN1,:GUN2] - printf("run() %i\n", gun); - switch(gun) { - case GUN1 when(2) - printf("when2\n"); - break; // end - case GUN2 when(3) - printf("when3\n"); - break; // end - } -} - -int main(int argc, char** argv) { - printf("hello world. number of arguments=%i\n", argc); - int fun=5; - bo { - fun -= 1; //.id - gun = fun - run(fun); - } while(fun>0); - ruby_init(); - rb_eval_string(DATA); - return 0; -} - -#if 0 // nobody reads un-defined code -__END__ -#CODE -def goto*s;$s=[];Y.times{s=[];X.times{s<<[0]*3};$s<< s}end;A=0.5 -include Math;def u g,h,i,j,k,l;f,*m=((j-h).abs>(k-i).abs)?[proc{ -|n,o| g[o] [n ]=l },[h ,i ],[j,k]]:[proc{ -|p,q| g[ p][ q] =l} ,[ i,h ], [k,j]];b,a=m.sort -c,d=a [1 ]-b [1 ],a [0 ]-b [0 ];d.times{|e|f. -call( e+b[ 0] ,c* e/d+b [1])};end;V=0;def bo&u -$u||= V; ;$u += 1+V ;; return u.call if$u>1;q=128.0 -;x=(V .. 255 ). map {| y|f1,z =sin(y.to_f*PI/q), -sin(( y. to_f + 200 )*PI/( q));[(f1*30.0+110.0). -to_i,((f1+z)*10.0+40.0).to_i,(z*20.0+120.0).to_i]};Y.times{|i|X. -times{|j|i1=((i*0.3+150)*(j*1.1+50)/50.0).to_i;i2=((i*0.8+510)*( -j*0.9+1060)/51.0).to_i;$s[i][j]=x[(i1*i2)%255].clone}};$a=(0..25). -inject([]){|a,i|a<<(V..3).inject([]){|r,j|r<<$c[i*4+j]}};u.call;end -I=LocalJumpError;def run*a,&b;return if a.size==V;if a[V]==666;$b=b -elsif$b;$b.call;end;end;def main s,&u;$m=V;u.call rescue I;end -def rb_eval_string(*a);end # you promised not to look here -def ruby_init;q=2.0;l=((X**q)*A+(Y**q)*A)**A;V.upto(Y-4){|s|V. -upto(X-4){|q|d=((q-X/A)**q+(s-Y/A)**q)**A;e=(cos(d*PI/(l/q))/q -+A)*3.0+1.0;v=2;f=v/e;a,p,b=$s[s],$s[s+1],$s[s+v];r=a[q][V]*e+ -p[q][V]+a[q+1][V]+b[q][V]+a[q+v][V]+b[q+v/v][V]+p[q+v][V]+b[q+ -v][V]*f;g=[a[q][V],b[q][V],a[q+v][V],b[q+v][V]];h=(g.max-g.min -)*f;$s[s][q][V]=[[(r/(e+f+6.0)+A+(h*0.4)).to_i,255].min,V].max -}};File.open("res.ppm","w+"){|f|f.write(# secret.greetings :-) -"P3\n# res.ppm\n#{X} #{Y}\n255\n"+$s.map{|a|a.map{|b|b.join' ' -}.join(' ')+"\n"}.join)};end;def switch i,&b;b.call;return unless -defined?($m);b=(X*0.01).to_i;d=1.0/40.0;e=0.09;c=(Y*0.01).to_i -a=$a.map{|(f,g,h,j)|[f*d,g*e,h*d,j*e]};a.each{|(k,l,m,n)|u($s,(k*X -).to_i+b+i,(l*Y).to_i+c+i,(m*X).to_i+b+i,(n*Y).to_i+c+i,[Z]*3)} -a.each{|(o,q,r,s)|u($s,(o*(X-Z)).to_i+i,(q*(Y-Z)).to_i+i,(r*(X- -Z)).to_i+i,(s*(Y-Z)).to_i+i,[(1<<8)-1]*3)};end;Q=Object;class -Regexp;def []=(v,is);is.each{|s|Q.send(:remove_const,s)if Q. -const_defined? s;Q.const_set(s,v)};end;end;def int*ptr;666 -end;class O;def []=(a,b=nil);$c=a;end;end;alias:void:goto -#endif // pretend as if you havn't seen anything - -module CodeRay - module Scanners - -class Ruby < Scanner - - RESERVED_WORDS = [ - 'and', 'def', 'end', 'in', 'or', 'unless', 'begin', - 'defined?', 'ensure', 'module', 'redo', 'super', 'until', - 'BEGIN', 'break', 'do', 'next', 'rescue', 'then', - 'when', 'END', 'case', 'else', 'for', 'retry', - 'while', 'alias', 'class', 'elsif', 'if', 'not', 'return', - 'undef', 'yield', - ] - - DEF_KEYWORDS = ['def'] - MODULE_KEYWORDS = ['class', 'module'] - DEF_NEW_STATE = WordList.new(:initial). - add(DEF_KEYWORDS, :def_expected). - add(MODULE_KEYWORDS, :module_expected) - - WORDS_ALLOWING_REGEXP = [ - 'and', 'or', 'not', 'while', 'until', 'unless', 'if', 'elsif', 'when' - ] - REGEXP_ALLOWED = WordList.new(false). - add(WORDS_ALLOWING_REGEXP, :set) - - PREDEFINED_CONSTANTS = [ - 'nil', 'true', 'false', 'self', - 'DATA', 'ARGV', 'ARGF', '__FILE__', '__LINE__', - ] - - IDENT_KIND = WordList.new(:ident). - add(RESERVED_WORDS, :reserved). - add(PREDEFINED_CONSTANTS, :pre_constant) - - METHOD_NAME = / #{IDENT} [?!]? /xo - METHOD_NAME_EX = / - #{METHOD_NAME} # common methods: split, foo=, empty?, gsub! - | \*\*? # multiplication and power - | [-+~]@? # plus, minus - | [\/%&|^`] # division, modulo or format strings, &and, |or, ^xor, `system` - | \[\]=? # array getter and setter - | <=?>? | >=? # comparison, rocket operator - | << | >> # append or shift left, shift right - | ===? # simple equality and case equality - /ox - GLOBAL_VARIABLE = / \$ (?: #{IDENT} | \d+ | [~&+`'=\/,;_.<>!@0$?*":F\\] | -[a-zA-Z_0-9] ) /ox - - DOUBLEQ = / " [^"\#\\]* (?: (?: \#\{.*?\} | \#(?:$")? | \\. ) [^"\#\\]* )* "? /ox - SINGLEQ = / ' [^'\\]* (?: \\. [^'\\]* )* '? /ox - STRING = / #{SINGLEQ} | #{DOUBLEQ} /ox - SHELL = / ` [^`\#\\]* (?: (?: \#\{.*?\} | \#(?:$`)? | \\. ) [^`\#\\]* )* `? /ox - REGEXP = / \/ [^\/\#\\]* (?: (?: \#\{.*?\} | \#(?:$\/)? | \\. ) [^\/\#\\]* )* \/? /ox - - DECIMAL = /\d+(?:_\d+)*/ # doesn't recognize 09 as octal error - OCTAL = /0_?[0-7]+(?:_[0-7]+)*/ - HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/ - BINARY = /0b[01]+(?:_[01]+)*/ - - EXPONENT = / [eE] [+-]? #{DECIMAL} /ox - FLOAT = / #{DECIMAL} (?: #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? ) / - INTEGER = /#{OCTAL}|#{HEXADECIMAL}|#{BINARY}|#{DECIMAL}/ - - def reset - super - @regexp_allowed = false - end - - def next_token - return if @scanner.eos? - - kind = :error - if @scanner.scan(/\s+/) # in every state - kind = :space - @regexp_allowed = :set if @regexp_allowed or @scanner.matched.index(?\n) # delayed flag setting - - elsif @state == :def_expected - if @scanner.scan(/ (?: (?:#{IDENT}(?:\.|::))* | (?:@@?|$)? #{IDENT}(?:\.|::) ) #{METHOD_NAME_EX} /ox) - kind = :method - @state = :initial - else - @scanner.getch - end - @state = :initial - - elsif @state == :module_expected - if @scanner.scan(/<</) - kind = :operator - else - if @scanner.scan(/ (?: #{IDENT} (?:\.|::))* #{IDENT} /ox) - kind = :method - else - @scanner.getch - end - @state = :initial - end - - elsif # state == :initial - # IDENTIFIERS, KEYWORDS - if @scanner.scan(GLOBAL_VARIABLE) - kind = :global_variable - elsif @scanner.scan(/ @@ #{IDENT} /ox) - kind = :class_variable - elsif @scanner.scan(/ @ #{IDENT} /ox) - kind = :instance_variable - elsif @scanner.scan(/ __END__\n ( (?!\#CODE\#) .* )? | \#[^\n]* | =begin(?=\s).*? \n=end(?=\s|\z)(?:[^\n]*)? /mx) - kind = :comment - elsif @scanner.scan(METHOD_NAME) - if @last_token_dot - kind = :ident - else - matched = @scanner.matched - kind = IDENT_KIND[matched] - if kind == :ident and matched =~ /^[A-Z]/ - kind = :constant - elsif kind == :reserved - @state = DEF_NEW_STATE[matched] - @regexp_allowed = REGEXP_ALLOWED[matched] - end - end - - elsif @scanner.scan(STRING) - kind = :string - elsif @scanner.scan(SHELL) - kind = :shell - elsif @scanner.scan(/<< - (?: - ([a-zA-Z_0-9]+) - (?: .*? ^\1$ | .* ) - | - -([a-zA-Z_0-9]+) - (?: .*? ^\s*\2$ | .* ) - | - (["\'`]) (.+?) \3 - (?: .*? ^\4$ | .* ) - | - - (["\'`]) (.+?) \5 - (?: .*? ^\s*\6$ | .* ) - ) - /mxo) - kind = :string - elsif @scanner.scan(/\//) and @regexp_allowed - @scanner.unscan - @scanner.scan(REGEXP) - kind = :regexp -/%(?:[Qqxrw](?:\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\\\\])(?:(?!\1)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\1)[^#\\\\])*)*\1?)|\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\s\\\\])(?:(?!\2)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\2)[^#\\\\])*)*\2?|\\\\[^#\\\\]*(?:(?:#\{.*?\}|#)[^#\\\\]*)*\\\\?)/ - elsif @scanner.scan(/:(?:#{GLOBAL_VARIABLE}|#{METHOD_NAME_EX}|#{STRING})/ox) - kind = :symbol - elsif @scanner.scan(/ - \? (?: - [^\s\\] - | - \\ (?:M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-))? (?: \\ (?: . | [0-7]{3} | x[0-9A-Fa-f][0-9A-Fa-f] ) - ) - /mox) - kind = :integer - - elsif @scanner.scan(/ [-+*\/%=<>;,|&!()\[\]{}~?] | \.\.?\.? | ::? /x) - kind = :operator - @regexp_allowed = :set if @scanner.matched[-1,1] =~ /[~=!<>|&^,\(\[+\-\/\*%]\z/ - elsif @scanner.scan(FLOAT) - kind = :float - elsif @scanner.scan(INTEGER) - kind = :integer - else - @scanner.getch - end - end - - token = Token.new @scanner.matched, kind - - if kind == :regexp - token.text << @scanner.scan(/[eimnosux]*/) - end - - @regexp_allowed = (@regexp_allowed == :set) # delayed flag setting - - token - end -end - -register Ruby, 'ruby', 'rb' - - end -end -class Set - include Enumerable - - # Creates a new set containing the given objects. - def self.[](*ary) - new(ary) - end - - # Creates a new set containing the elements of the given enumerable - # object. - # - # If a block is given, the elements of enum are preprocessed by the - # given block. - def initialize(enum = nil, &block) # :yields: o - @hash ||= Hash.new - - enum.nil? and return - - if block - enum.each { |o| add(block[o]) } - else - merge(enum) - end - end - - # Copy internal hash. - def initialize_copy(orig) - @hash = orig.instance_eval{@hash}.dup - end - - # Returns the number of elements. - def size - @hash.size - end - alias length size - - # Returns true if the set contains no elements. - def empty? - @hash.empty? - end - - # Removes all elements and returns self. - def clear - @hash.clear - self - end - - # Replaces the contents of the set with the contents of the given - # enumerable object and returns self. - def replace(enum) - if enum.class == self.class - @hash.replace(enum.instance_eval { @hash }) - else - enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" - clear - enum.each { |o| add(o) } - end - - self - end - - # Converts the set to an array. The order of elements is uncertain. - def to_a - @hash.keys - end - - def flatten_merge(set, seen = Set.new) - set.each { |e| - if e.is_a?(Set) - if seen.include?(e_id = e.object_id) - raise ArgumentError, "tried to flatten recursive Set" - end - - seen.add(e_id) - flatten_merge(e, seen) - seen.delete(e_id) - else - add(e) - end - } - - self - end - protected :flatten_merge - - # Returns a new set that is a copy of the set, flattening each - # containing set recursively. - def flatten - self.class.new.flatten_merge(self) - end - - # Equivalent to Set#flatten, but replaces the receiver with the - # result in place. Returns nil if no modifications were made. - def flatten! - if detect { |e| e.is_a?(Set) } - replace(flatten()) - else - nil - end - end - - # Returns true if the set contains the given object. - def include?(o) - @hash.include?(o) - end - alias member? include? - - # Returns true if the set is a superset of the given set. - def superset?(set) - set.is_a?(Set) or raise ArgumentError, "value must be a set" - return false if size < set.size - set.all? { |o| include?(o) } - end - - # Returns true if the set is a proper superset of the given set. - def proper_superset?(set) - set.is_a?(Set) or raise ArgumentError, "value must be a set" - return false if size <= set.size - set.all? { |o| include?(o) } - end - - # Returns true if the set is a subset of the given set. - def subset?(set) - set.is_a?(Set) or raise ArgumentError, "value must be a set" - return false if set.size < size - all? { |o| set.include?(o) } - end - - # Returns true if the set is a proper subset of the given set. - def proper_subset?(set) - set.is_a?(Set) or raise ArgumentError, "value must be a set" - return false if set.size <= size - all? { |o| set.include?(o) } - end - - # Calls the given block once for each element in the set, passing - # the element as parameter. - def each - @hash.each_key { |o| yield(o) } - self - end - - # Adds the given object to the set and returns self. Use +merge+ to - # add several elements at once. - def add(o) - @hash[o] = true - self - end - alias << add - - # Adds the given object to the set and returns self. If the - # object is already in the set, returns nil. - def add?(o) - if include?(o) - nil - else - add(o) - end - end - - # Deletes the given object from the set and returns self. Use +subtract+ to - # delete several items at once. - def delete(o) - @hash.delete(o) - self - end - - # Deletes the given object from the set and returns self. If the - # object is not in the set, returns nil. - def delete?(o) - if include?(o) - delete(o) - else - nil - end - end - - # Deletes every element of the set for which block evaluates to - # true, and returns self. - def delete_if - @hash.delete_if { |o,| yield(o) } - self - end - - # Do collect() destructively. - def collect! - set = self.class.new - each { |o| set << yield(o) } - replace(set) - end - alias map! collect! - - # Equivalent to Set#delete_if, but returns nil if no changes were - # made. - def reject! - n = size - delete_if { |o| yield(o) } - size == n ? nil : self - end - - # Merges the elements of the given enumerable object to the set and - # returns self. - def merge(enum) - if enum.is_a?(Set) - @hash.update(enum.instance_eval { @hash }) - else - enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" - enum.each { |o| add(o) } - end - - self - end - - # Deletes every element that appears in the given enumerable object - # and returns self. - def subtract(enum) - enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" - enum.each { |o| delete(o) } - self - end - - # Returns a new set built by merging the set and the elements of the - # given enumerable object. - def |(enum) - enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" - dup.merge(enum) - end - alias + | ## - alias union | ## - - # Returns a new set built by duplicating the set, removing every - # element that appears in the given enumerable object. - def -(enum) - enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" - dup.subtract(enum) - end - alias difference - ## - - # Returns a new array containing elements common to the set and the - # given enumerable object. - def &(enum) - enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" - n = self.class.new - enum.each { |o| n.add(o) if include?(o) } - n - end - alias intersection & ## - - # Returns a new array containing elements exclusive between the set - # and the given enumerable object. (set ^ enum) is equivalent to - # ((set | enum) - (set & enum)). - def ^(enum) - enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" - n = dup - enum.each { |o| if n.include?(o) then n.delete(o) else n.add(o) end } - n - end - - # Returns true if two sets are equal. The equality of each couple - # of elements is defined according to Object#eql?. - def ==(set) - equal?(set) and return true - - set.is_a?(Set) && size == set.size or return false - - hash = @hash.dup - set.all? { |o| hash.include?(o) } - end - - def hash # :nodoc: - @hash.hash - end - - def eql?(o) # :nodoc: - return false unless o.is_a?(Set) - @hash.eql?(o.instance_eval{@hash}) - end - - # Classifies the set by the return value of the given block and - # returns a hash of {value => set of elements} pairs. The block is - # called once for each element of the set, passing the element as - # parameter. - # - # e.g.: - # - # require 'set' - # files = Set.new(Dir.glob("*.rb")) - # hash = files.classify { |f| File.mtime(f).year } - # p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>, - # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>, - # # 2002=>#<Set: {"f.rb"}>} - def classify # :yields: o - h = {} - - each { |i| - x = yield(i) - (h[x] ||= self.class.new).add(i) - } - - h - end - - # Divides the set into a set of subsets according to the commonality - # defined by the given block. - # - # If the arity of the block is 2, elements o1 and o2 are in common - # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are - # in common if block.call(o1) == block.call(o2). - # - # e.g.: - # - # require 'set' - # numbers = Set[1, 3, 4, 6, 9, 10, 11] - # set = numbers.divide { |i,j| (i - j).abs == 1 } - # p set # => #<Set: {#<Set: {1}>, - # # #<Set: {11, 9, 10}>, - # # #<Set: {3, 4}>, - # # #<Set: {6}>}> - def divide(&func) - if func.arity == 2 - require 'tsort' - - class << dig = {} # :nodoc: - include TSort - - alias tsort_each_node each_key - def tsort_each_child(node, &block) - fetch(node).each(&block) - end - end - - each { |u| - dig[u] = a = [] - each{ |v| func.call(u, v) and a << v } - } - - set = Set.new() - dig.each_strongly_connected_component { |css| - set.add(self.class.new(css)) - } - set - else - Set.new(classify(&func).values) - end - end - - InspectKey = :__inspect_key__ # :nodoc: - - # Returns a string containing a human-readable representation of the - # set. ("#<Set: {element1, element2, ...}>") - def inspect - ids = (Thread.current[InspectKey] ||= []) - - if ids.include?(object_id) - return sprintf('#<%s: {...}>', self.class.name) - end - - begin - ids << object_id - return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) - ensure - ids.pop - end - end - - def pretty_print(pp) # :nodoc: - pp.text sprintf('#<%s: {', self.class.name) - pp.nest(1) { - pp.seplist(self) { |o| - pp.pp o - } - } - pp.text "}>" - end - - def pretty_print_cycle(pp) # :nodoc: - pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...') - end -end - -# SortedSet implements a set which elements are sorted in order. See Set. -class SortedSet < Set - @@setup = false - - class << self - def [](*ary) # :nodoc: - new(ary) - end - - def setup # :nodoc: - @@setup and return - - begin - require 'rbtree' - - module_eval %{ - def initialize(*args, &block) - @hash = RBTree.new - super - end - } - rescue LoadError - module_eval %{ - def initialize(*args, &block) - @keys = nil - super - end - - def clear - @keys = nil - super - end - - def replace(enum) - @keys = nil - super - end - - def add(o) - @keys = nil - @hash[o] = true - self - end - alias << add - - def delete(o) - @keys = nil - @hash.delete(o) - self - end - - def delete_if - n = @hash.size - @hash.delete_if { |o,| yield(o) } - @keys = nil if @hash.size != n - self - end - - def merge(enum) - @keys = nil - super - end - - def each - to_a.each { |o| yield(o) } - end - - def to_a - (@keys = @hash.keys).sort! unless @keys - @keys - end - } - end - - @@setup = true - end - end - - def initialize(*args, &block) # :nodoc: - SortedSet.setup - initialize(*args, &block) - end -end - -module Enumerable - # Makes a set from the enumerable object with given arguments. - def to_set(klass = Set, *args, &block) - klass.new(self, *args, &block) - end -end - -# =begin -# == RestricedSet class -# RestricedSet implements a set with restrictions defined by a given -# block. -# -# === Super class -# Set -# -# === Class Methods -# --- RestricedSet::new(enum = nil) { |o| ... } -# --- RestricedSet::new(enum = nil) { |rset, o| ... } -# Creates a new restricted set containing the elements of the given -# enumerable object. Restrictions are defined by the given block. -# -# If the block's arity is 2, it is called with the RestrictedSet -# itself and an object to see if the object is allowed to be put in -# the set. -# -# Otherwise, the block is called with an object to see if the object -# is allowed to be put in the set. -# -# === Instance Methods -# --- restriction_proc -# Returns the restriction procedure of the set. -# -# =end -# -# class RestricedSet < Set -# def initialize(*args, &block) -# @proc = block or raise ArgumentError, "missing a block" -# -# if @proc.arity == 2 -# instance_eval %{ -# def add(o) -# @hash[o] = true if @proc.call(self, o) -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(self, o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# -# def replace(enum) -# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" -# clear -# enum.each { |o| add(o) } -# -# self -# end -# -# def merge(enum) -# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable" -# enum.each { |o| add(o) } -# -# self -# end -# } -# else -# instance_eval %{ -# def add(o) -# if @proc.call(o) -# @hash[o] = true -# end -# self -# end -# alias << add -# -# def add?(o) -# if include?(o) || !@proc.call(o) -# nil -# else -# @hash[o] = true -# self -# end -# end -# } -# end -# -# super(*args) -# end -# -# def restriction_proc -# @proc -# end -# end - -if $0 == __FILE__ - eval DATA.read, nil, $0, __LINE__+4 -end - -# = rweb - CGI Support Library -# -# Author:: Johannes Barre (mailto:rweb@igels.net) -# Copyright:: Copyright (c) 2003, 04 by Johannes Barre -# License:: GNU Lesser General Public License (COPYING, http://www.gnu.org/copyleft/lesser.html) -# Version:: 0.1.0 -# CVS-ID:: $Id: rweb.rb 6 2004-06-16 15:56:26Z igel $ -# -# == What is Rweb? -# Rweb is a replacement for the cgi class included in the ruby distribution. -# -# == How to use -# -# === Basics -# -# This class is made to be as easy as possible to use. An example: -# -# require "rweb" -# -# web = Rweb.new -# web.out do -# web.puts "Hello world!" -# end -# -# The visitor will get a simple "Hello World!" in his browser. Please notice, -# that won't set html-tags for you, so you should better do something like this: -# -# require "rweb" -# -# web = Rweb.new -# web.out do -# web.puts "<html><body>Hello world!</body></html>" -# end -# -# === Set headers -# Of course, it's also possible to tell the browser, that the content of this -# page is plain text instead of html code: -# -# require "rweb" -# -# web = Rweb.new -# web.out do -# web.header("content-type: text/plain") -# web.puts "Hello plain world!" -# end -# -# Please remember, headers can't be set after the page content has been send. -# You have to set all nessessary headers before the first puts oder print. It's -# possible to cache the content until everything is complete. Doing it this -# way, you can set headers everywhere. -# -# If you set a header twice, the second header will replace the first one. The -# header name is not casesensitive, it will allways converted in to the -# capitalised form suggested by the w3c (http://w3.org) -# -# === Set cookies -# Setting cookies is quite easy: -# include 'rweb' -# -# web = Rweb.new -# Cookie.new("Visits", web.cookies['visits'].to_i +1) -# web.out do -# web.puts "Welcome back! You visited this page #{web.cookies['visits'].to_i +1} times" -# end -# -# See the class Cookie for more details. -# -# === Get form and cookie values -# There are four ways to submit data from the browser to the server and your -# ruby script: via GET, POST, cookies and file upload. Rweb doesn't support -# file upload by now. -# -# include 'rweb' -# -# web = Rweb.new -# web.out do -# web.print "action: #{web.get['action']} " -# web.puts "The value of the cookie 'visits' is #{web.cookies['visits']}" -# web.puts "The post parameter 'test['x']' is #{web.post['test']['x']}" -# end - -RWEB_VERSION = "0.1.0" -RWEB = "rweb/#{RWEB_VERSION}" - -#require 'rwebcookie' -> edit by bunny :-) - -class Rweb - # All parameter submitted via the GET method are available in attribute - # get. This is Hash, where every parameter is available as a key-value - # pair. - # - # If your input tag has a name like this one, it's value will be available - # as web.get["fieldname"] - # <input name="fieldname"> - # You can submit values as a Hash - # <input name="text['index']"> - # <input name="text['index2']"> - # will be available as - # web.get["text"]["index"] - # web.get["text"]["index2"] - # Integers are also possible - # <input name="int[2]"> - # <input name="int[3]['hi']> - # will be available as - # web.get["int"][2] - # web.get["int"][3]["hi"] - # If you specify no index, the lowest unused index will be used: - # <input name="int[]"><!-- First Field --> - # <input name="int[]"><!-- Second one --> - # will be available as - # web.get["int"][0] # First Field - # web.get["int"][1] # Second one - # Please notice, this doesn'd work like you might expect: - # <input name="text[index]"> - # It will not be available as web.get["text"]["index"] but - # web.get["text[index]"] - attr_reader :get - - # All parameters submitted via POST are available in the attribute post. It - # works like the get attribute. - # <input name="text[0]"> - # will be available as - # web.post["text"][0] - attr_reader :post - - # All cookies submitted by the browser are available in cookies. This is a - # Hash, where every cookie is a key-value pair. - attr_reader :cookies - - # The name of the browser identification is submitted as USER_AGENT and - # available in this attribute. - attr_reader :user_agent - - # The IP address of the client. - attr_reader :remote_addr - - # Creates a new Rweb object. This should only done once. You can set various - # options via the settings hash. - # - # "cache" => true: Everything you script send to the client will be cached - # until the end of the out block or until flush is called. This way, you - # can modify headers and cookies even after printing something to the client. - # - # "safe" => level: Changes the $SAFE attribute. By default, $SAFE will be set - # to 1. If $SAFE is already higher than this value, it won't be changed. - # - # "silend" => true: Normaly, Rweb adds automaticly a header like this - # "X-Powered-By: Rweb/x.x.x (Ruby/y.y.y)". With the silend option you can - # suppress this. - def initialize (settings = {}) - # {{{ - @header = {} - @cookies = {} - @get = {} - @post = {} - - # Internal attributes - @status = nil - @reasonPhrase = nil - @setcookies = [] - @output_started = false; - @output_allowed = false; - - @mod_ruby = false - @env = ENV.to_hash - - if defined?(MOD_RUBY) - @output_method = "mod_ruby" - @mod_ruby = true - elsif @env['SERVER_SOFTWARE'] =~ /^Microsoft-IIS/i - @output_method = "nph" - else - @output_method = "ph" - end - - unless settings.is_a?(Hash) - raise TypeError, "settings must be a Hash" - end - @settings = settings - - unless @settings.has_key?("safe") - @settings["safe"] = 1 - end - - if $SAFE < @settings["safe"] - $SAFE = @settings["safe"] - end - - unless @settings.has_key?("cache") - @settings["cache"] = false - end - - # mod_ruby sets no QUERY_STRING variable, if no GET-Parameters are given - unless @env.has_key?("QUERY_STRING") - @env["QUERY_STRING"] = "" - end - - # Now we split the QUERY_STRING by the seperators & and ; or, if - # specified, settings['get seperator'] - unless @settings.has_key?("get seperator") - get_args = @env['QUERY_STRING'].split(/[&;]/) - else - get_args = @env['QUERY_STRING'].split(@settings['get seperator']) - end - - get_args.each do | arg | - arg_key, arg_val = arg.split(/=/, 2) - arg_key = Rweb::unescape(arg_key) - arg_val = Rweb::unescape(arg_val) - - # Parse names like name[0], name['text'] or name[] - pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/ - keys = [] - while match = pattern.match(arg_key) - arg_key = match[1] - keys = [match[2]] + keys - end - keys = [arg_key] + keys - - akt = @get - last = nil - lastkey = nil - keys.each do |key| - if key == "" - # No key specified (like in "test[]"), so we use the - # lowerst unused Integer as key - key = 0 - while akt.has_key?(key) - key += 1 - end - elsif /^[0-9]*$/ =~ key - # If the index is numerical convert it to an Integer - key = key.to_i - elsif key[0].chr == "'" || key[0].chr == '"' - key = key[1, key.length() -2] - end - if !akt.has_key?(key) || !akt[key].class == Hash - # create an empty Hash if there isn't already one - akt[key] = {} - end - last = akt - lastkey = key - akt = akt[key] - end - last[lastkey] = arg_val - end - - if @env['REQUEST_METHOD'] == "POST" - if @env.has_key?("CONTENT_TYPE") && @env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" && @env.has_key?('CONTENT_LENGTH') - unless @settings.has_key?("post seperator") - post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(/[&;]/) - else - post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(@settings['post seperator']) - end - post_args.each do | arg | - arg_key, arg_val = arg.split(/=/, 2) - arg_key = Rweb::unescape(arg_key) - arg_val = Rweb::unescape(arg_val) - - # Parse names like name[0], name['text'] or name[] - pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/ - keys = [] - while match = pattern.match(arg_key) - arg_key = match[1] - keys = [match[2]] + keys - end - keys = [arg_key] + keys - - akt = @post - last = nil - lastkey = nil - keys.each do |key| - if key == "" - # No key specified (like in "test[]"), so we use - # the lowerst unused Integer as key - key = 0 - while akt.has_key?(key) - key += 1 - end - elsif /^[0-9]*$/ =~ key - # If the index is numerical convert it to an Integer - key = key.to_i - elsif key[0].chr == "'" || key[0].chr == '"' - key = key[1, key.length() -2] - end - if !akt.has_key?(key) || !akt[key].class == Hash - # create an empty Hash if there isn't already one - akt[key] = {} - end - last = akt - lastkey = key - akt = akt[key] - end - last[lastkey] = arg_val - end - else - # Maybe we should print a warning here? - $stderr.print("Unidentified form data recived and discarded.") - end - end - - if @env.has_key?("HTTP_COOKIE") - cookie = @env['HTTP_COOKIE'].split(/; ?/) - cookie.each do | c | - cookie_key, cookie_val = c.split(/=/, 2) - - @cookies [Rweb::unescape(cookie_key)] = Rweb::unescape(cookie_val) - end - end - - if defined?(@env['HTTP_USER_AGENT']) - @user_agent = @env['HTTP_USER_AGENT'] - else - @user_agent = nil; - end - - if defined?(@env['REMOTE_ADDR']) - @remote_addr = @env['REMOTE_ADDR'] - else - @remote_addr = nil - end - # }}} - end - - # Prints a String to the client. If caching is enabled, the String will - # buffered until the end of the out block ends. - def print(str = "") - # {{{ - unless @output_allowed - raise "You just can write to output inside of a Rweb::out-block" - end - - if @settings["cache"] - @buffer += [str.to_s] - else - unless @output_started - sendHeaders - end - $stdout.print(str) - end - nil - # }}} - end - - # Prints a String to the client and adds a line break at the end. Please - # remember, that a line break is not visible in HTML, use the <br> HTML-Tag - # for this. If caching is enabled, the String will buffered until the end - # of the out block ends. - def puts(str = "") - # {{{ - self.print(str + "\n") - # }}} - end - - # Alias to print. - def write(str = "") - # {{{ - self.print(str) - # }}} - end - - # If caching is enabled, all cached data are send to the cliend and the - # cache emptied. - def flush - # {{{ - unless @output_allowed - raise "You can't use flush outside of a Rweb::out-block" - end - buffer = @buffer.join - - unless @output_started - sendHeaders - end - $stdout.print(buffer) - - @buffer = [] - # }}} - end - - # Sends one or more header to the client. All headers are cached just - # before body data are send to the client. If the same header are set - # twice, only the last value is send. - # - # Example: - # web.header("Last-Modified: Mon, 16 Feb 2004 20:15:41 GMT") - # web.header("Location: http://www.ruby-lang.org") - # - # You can specify more than one header at the time by doing something like - # this: - # web.header("Content-Type: text/plain\nContent-Length: 383") - # or - # web.header(["Content-Type: text/plain", "Content-Length: 383"]) - def header(str) - # {{{ - if @output_started - raise "HTTP-Headers are already send. You can't change them after output has started!" - end - unless @output_allowed - raise "You just can set headers inside of a Rweb::out-block" - end - if str.is_a?Array - str.each do | value | - self.header(value) - end - - elsif str.split(/\n/).length > 1 - str.split(/\n/).each do | value | - self.header(value) - end - - elsif str.is_a? String - str.gsub!(/\r/, "") - - if (str =~ /^HTTP\/1\.[01] [0-9]{3} ?.*$/) == 0 - pattern = /^HTTP\/1.[01] ([0-9]{3}) ?(.*)$/ - - result = pattern.match(str) - self.setstatus(result[0], result[1]) - elsif (str =~ /^status: [0-9]{3} ?.*$/i) == 0 - pattern = /^status: ([0-9]{3}) ?(.*)$/i - - result = pattern.match(str) - self.setstatus(result[0], result[1]) - else - a = str.split(/: ?/, 2) - - @header[a[0].downcase] = a[1] - end - end - # }}} - end - - # Changes the status of this page. There are several codes like "200 OK", - # "302 Found", "404 Not Found" or "500 Internal Server Error". A list of - # all codes is available at - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 - # - # You can just send the code number, the reason phrase will be added - # automaticly with the recommendations from the w3c if not specified. If - # you set the status twice or more, only the last status will be send. - # Examples: - # web.status("401 Unauthorized") - # web.status("410 Sad but true, this lonely page is gone :(") - # web.status(206) - # web.status("400") - # - # The default status is "200 OK". If a "Location" header is set, the - # default status is "302 Found". - def status(str) - # {{{ - if @output_started - raise "HTTP-Headers are already send. You can't change them after output has started!" - end - unless @output_allowed - raise "You just can set headers inside of a Rweb::out-block" - end - if str.is_a?Integer - @status = str - elsif str.is_a?String - p1 = /^([0-9]{3}) ?(.*)$/ - p2 = /^HTTP\/1\.[01] ([0-9]{3}) ?(.*)$/ - p3 = /^status: ([0-9]{3}) ?(.*)$/i - - if (a = p1.match(str)) == nil - if (a = p2.match(str)) == nil - if (a = p3.match(str)) == nil - raise ArgumentError, "Invalid argument", caller - end - end - end - @status = a[1].to_i - if a[2] != "" - @reasonPhrase = a[2] - else - @reasonPhrase = getReasonPhrase(@status) - end - else - raise ArgumentError, "Argument of setstatus must be integer or string", caller - end - # }}} - end - - # Handles the output of your content and rescues all exceptions. Send all - # data in the block to this method. For example: - # web.out do - # web.header("Content-Type: text/plain") - # web.puts("Hello, plain world!") - # end - def out - # {{{ - @output_allowed = true - @buffer = []; # We use an array as buffer, because it's more performant :) - - begin - yield - rescue Exception => exception - $stderr.puts "Ruby exception rescued (#{exception.class}): #{exception.message}" - $stderr.puts exception.backtrace.join("\n") - - unless @output_started - self.setstatus(500) - @header = {} - end - - unless (@settings.has_key?("hide errors") and @settings["hide errors"] == true) - unless @output_started - self.header("Content-Type: text/html") - self.puts "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" - self.puts "<html>" - self.puts "<head>" - self.puts "<title>500 Internal Server Error</title>" - self.puts "</head>" - self.puts "<body>" - end - if @header.has_key?("content-type") and (@header["content-type"] =~ /^text\/html/i) == 0 - self.puts "<h1>Internal Server Error</h1>" - self.puts "<p>The server encountered an exception and was unable to complete your request.</p>" - self.puts "<p>The exception has provided the following information:</p>" - self.puts "<pre style=\"background: #FFCCCC; border: black solid 2px; margin-left: 2cm; margin-right: 2cm; padding: 2mm;\"><b>#{exception.class}</b>: #{exception.message} <b>on</b>" - self.puts - self.puts "#{exception.backtrace.join("\n")}</pre>" - self.puts "</body>" - self.puts "</html>" - else - self.puts "The server encountered an exception and was unable to complete your request" - self.puts "The exception has provided the following information:" - self.puts "#{exception.class}: #{exception.message}" - self.puts - self.puts exception.backtrace.join("\n") - end - end - end - - if @settings["cache"] - buffer = @buffer.join - - unless @output_started - unless @header.has_key?("content-length") - self.header("content-length: #{buffer.length}") - end - - sendHeaders - end - $stdout.print(buffer) - elsif !@output_started - sendHeaders - end - @output_allowed = false; - # }}} - end - - # Decodes URL encoded data, %20 for example stands for a space. - def Rweb.unescape(str) - # {{{ - if defined? str and str.is_a? String - str.gsub!(/\+/, " ") - str.gsub(/%.{2}/) do | s | - s[1,2].hex.chr - end - end - # }}} - end - - protected - def sendHeaders - # {{{ - - Cookie.disallow # no more cookies can be set or modified - if !(@settings.has_key?("silent") and @settings["silent"] == true) and !@header.has_key?("x-powered-by") - if @mod_ruby - header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION}, #{MOD_RUBY})"); - else - header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION})"); - end - end - - if @output_method == "ph" - if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location")) - header("content-type: text/html") - end - - if @status != nil - $stdout.print "Status: #{@status} #{@reasonPhrase}\r\n" - end - - @header.each do |key, value| - key = key *1 # "unfreeze" key :) - key[0] = key[0,1].upcase![0] - - key = key.gsub(/-[a-z]/) do |char| - "-" + char[1,1].upcase - end - - $stdout.print "#{key}: #{value}\r\n" - end - cookies = Cookie.getHttpHeader # Get all cookies as an HTTP Header - if cookies - $stdout.print cookies - end - - $stdout.print "\r\n" - - elsif @output_method == "nph" - elsif @output_method == "mod_ruby" - r = Apache.request - - if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location")) - header("text/html") - end - - if @status != nil - r.status_line = "#{@status} #{@reasonPhrase}" - end - - r.send_http_header - @header.each do |key, value| - key = key *1 # "unfreeze" key :) - - key[0] = key[0,1].upcase![0] - key = key.gsub(/-[a-z]/) do |char| - "-" + char[1,1].upcase - end - puts "#{key}: #{value.class}" - #r.headers_out[key] = value - end - end - @output_started = true - # }}} - end - - def getReasonPhrase (status) - # {{{ - if status == 100 - "Continue" - elsif status == 101 - "Switching Protocols" - elsif status == 200 - "OK" - elsif status == 201 - "Created" - elsif status == 202 - "Accepted" - elsif status == 203 - "Non-Authoritative Information" - elsif status == 204 - "No Content" - elsif status == 205 - "Reset Content" - elsif status == 206 - "Partial Content" - elsif status == 300 - "Multiple Choices" - elsif status == 301 - "Moved Permanently" - elsif status == 302 - "Found" - elsif status == 303 - "See Other" - elsif status == 304 - "Not Modified" - elsif status == 305 - "Use Proxy" - elsif status == 307 - "Temporary Redirect" - elsif status == 400 - "Bad Request" - elsif status == 401 - "Unauthorized" - elsif status == 402 - "Payment Required" - elsif status == 403 - "Forbidden" - elsif status == 404 - "Not Found" - elsif status == 405 - "Method Not Allowed" - elsif status == 406 - "Not Acceptable" - elsif status == 407 - "Proxy Authentication Required" - elsif status == 408 - "Request Time-out" - elsif status == 409 - "Conflict" - elsif status == 410 - "Gone" - elsif status == 411 - "Length Required" - elsif status == 412 - "Precondition Failed" - elsif status == 413 - "Request Entity Too Large" - elsif status == 414 - "Request-URI Too Large" - elsif status == 415 - "Unsupported Media Type" - elsif status == 416 - "Requested range not satisfiable" - elsif status == 417 - "Expectation Failed" - elsif status == 500 - "Internal Server Error" - elsif status == 501 - "Not Implemented" - elsif status == 502 - "Bad Gateway" - elsif status == 503 - "Service Unavailable" - elsif status == 504 - "Gateway Time-out" - elsif status == 505 - "HTTP Version not supported" - else - raise "Unknown Statuscode. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 for more information." - end - # }}} - end -end - -class Cookie - attr_reader :name, :value, :maxage, :path, :domain, :secure, :comment - - # Sets a cookie. Please see below for details of the attributes. - def initialize (name, value = nil, maxage = nil, path = nil, domain = nil, secure = false) - # {{{ - # HTTP headers (Cookies are a HTTP header) can only set, while no content - # is send. So an exception will be raised, when @@allowed is set to false - # and a new cookie has set. - unless defined?(@@allowed) - @@allowed = true - end - unless @@allowed - raise "You can't set cookies after the HTTP headers are send." - end - - unless defined?(@@list) - @@list = [] - end - @@list += [self] - - unless defined?(@@type) - @@type = "netscape" - end - - unless name.class == String - raise TypeError, "The name of a cookie must be a string", caller - end - if value.class.superclass == Integer || value.class == Float - value = value.to_s - elsif value.class != String && value != nil - raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller - end - if maxage.class == Time - maxage = maxage - Time.now - elsif !maxage.class.superclass == Integer || !maxage == nil - raise TypeError, "The maxage date of a cookie must be an Integer or Time object or nil.", caller - end - unless path.class == String || path == nil - raise TypeError, "The path of a cookie must be nil or a string", caller - end - unless domain.class == String || domain == nil - raise TypeError, "The value of a cookie must be nil or a string", caller - end - unless secure == true || secure == false - raise TypeError, "The secure field of a cookie must be true or false", caller - end - - @name, @value, @maxage, @path, @domain, @secure = name, value, maxage, path, domain, secure - @comment = nil - # }}} - end - - # Modifies the value of this cookie. The information you want to store. If the - # value is nil, the cookie will be deleted by the client. - # - # This attribute can be a String, Integer or Float object or nil. - def value=(value) - # {{{ - if value.class.superclass == Integer || value.class == Float - value = value.to_s - elsif value.class != String && value != nil - raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller - end - @value = value - # }}} - end - - # Modifies the maxage of this cookie. This attribute defines the lifetime of - # the cookie, in seconds. A value of 0 means the cookie should be discarded - # imediatly. If it set to nil, the cookie will be deleted when the browser - # will be closed. - # - # Attention: This is different from other implementations like PHP, where you - # gives the seconds since 1/1/1970 0:00:00 GMT. - # - # This attribute must be an Integer or Time object or nil. - def maxage=(maxage) - # {{{ - if maxage.class == Time - maxage = maxage - Time.now - elsif maxage.class.superclass == Integer || !maxage == nil - raise TypeError, "The maxage of a cookie must be an Interger or Time object or nil.", caller - end - @maxage = maxage - # }}} - end - - # Modifies the path value of this cookie. The client will send this cookie - # only, if the requested document is this directory or a subdirectory of it. - # - # The value of the attribute must be a String object or nil. - def path=(path) - # {{{ - unless path.class == String || path == nil - raise TypeError, "The path of a cookie must be nil or a string", caller - end - @path = path - # }}} - end - - # Modifies the domain value of this cookie. The client will send this cookie - # only if it's connected with this domain (or a subdomain, if the first - # character is a dot like in ".ruby-lang.org") - # - # The value of this attribute must be a String or nil. - def domain=(domain) - # {{{ - unless domain.class == String || domain == nil - raise TypeError, "The domain of a cookie must be a String or nil.", caller - end - @domain = domain - # }}} - end - - # Modifies the secure flag of this cookie. If it's true, the client will only - # send this cookie if it is secured connected with us. - # - # The value od this attribute has to be true or false. - def secure=(secure) - # {{{ - unless secure == true || secure == false - raise TypeError, "The secure field of a cookie must be true or false", caller - end - @secure = secure - # }}} - end - - # Modifies the comment value of this cookie. The comment won't be send, if - # type is "netscape". - def comment=(comment) - # {{{ - unless comment.class == String || comment == nil - raise TypeError, "The comment of a cookie must be a string or nil", caller - end - @comment = comment - # }}} - end - - # Changes the type of all cookies. - # Allowed values are RFC2109 and netscape (default). - def Cookie.type=(type) - # {{{ - unless @@allowed - raise "The cookies are allready send, so you can't change the type anymore." - end - unless type.downcase == "rfc2109" && type.downcase == "netscape" - raise "The type of the cookies must be \"RFC2109\" or \"netscape\"." - end - @@type = type; - # }}} - end - - # After sending this message, no cookies can be set or modified. Use it, when - # HTTP-Headers are send. Rweb does this for you. - def Cookie.disallow - # {{{ - @@allowed = false - true - # }}} - end - - # Returns a HTTP header (type String) with all cookies. Rweb does this for - # you. - def Cookie.getHttpHeader - # {{{ - if defined?(@@list) - if @@type == "netscape" - str = "" - @@list.each do |cookie| - if cookie.value == nil - cookie.maxage = 0 - cookie.value = "" - end - # TODO: Name and value should be escaped! - str += "Set-Cookie: #{cookie.name}=#{cookie.value}" - unless cookie.maxage == nil - expire = Time.now + cookie.maxage - expire.gmtime - str += "; Expire=#{expire.strftime("%a, %d-%b-%Y %H:%M:%S %Z")}" - end - unless cookie.domain == nil - str += "; Domain=#{cookie.domain}" - end - unless cookie.path == nil - str += "; Path=#{cookie.path}" - end - if cookie.secure - str += "; Secure" - end - str += "\r\n" - end - return str - else # type == "RFC2109" - str = "Set-Cookie: " - comma = false; - - @@list.each do |cookie| - if cookie.value == nil - cookie.maxage = 0 - cookie.value = "" - end - if comma - str += "," - end - comma = true - - str += "#{cookie.name}=\"#{cookie.value}\"" - unless cookie.maxage == nil - str += "; Max-Age=\"#{cookie.maxage}\"" - end - unless cookie.domain == nil - str += "; Domain=\"#{cookie.domain}\"" - end - unless cookie.path == nil - str += "; Path=\"#{cookie.path}\"" - end - if cookie.secure - str += "; Secure" - end - unless cookie.comment == nil - str += "; Comment=\"#{cookie.comment}\"" - end - str += "; Version=\"1\"" - end - str - end - else - false - end - # }}} - end -end - -require 'strscan' - -module BBCode - DEBUG = true - - use 'encoder', 'tags', 'tagstack', 'smileys' - -=begin - The Parser class takes care of the encoding. - It scans the given BBCode (as plain text), finds tags - and smilies and also makes links of urls in text. - - Normal text is send directly to the encoder. - - If a tag was found, an instance of a Tag subclass is created - to handle the case. - - The @tagstack manages tag nesting and ensures valid HTML. -=end - - class Parser - class Attribute - # flatten and use only one empty_arg - def self.create attr - attr = flatten attr - return @@empty_attr if attr.empty? - new attr - end - - private_class_method :new - - # remove leading and trailing whitespace; concat lines - def self.flatten attr - attr.strip.gsub(/\n/, ' ') - # -> ^ and $ can only match at begin and end now - end - - ATTRIBUTE_SCAN = / - (?!$) # don't match at end - \s* - ( # $1 = key - [^=\s\]"\\]* - (?: - (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? ) - [^=\s\]"\\]* - )* - ) - (?: - = - ( # $2 = value - [^\s\]"\\]* - (?: - (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? ) - [^\s\]"\\]* - )* - )? - )? - \s* - /x - - def self.parse source - source = source.dup - # empty_tag: the tag looks like [... /] - # slice!: this deletes the \s*/] at the end - # \s+ because [url=http://rubybb.org/forum/] is NOT an empty tag. - # In RubyBBCode, you can use [url=http://rubybb.org/forum/ /], and this has to be - # interpreted correctly. - empty_tag = source.sub!(/^:/, '=') or source.slice!(/\/$/) - debug 'PARSE: ' + source.inspect + ' => ' + empty_tag.inspect - #-> we have now an attr that's EITHER empty OR begins and ends with non-whitespace. - - attr = Hash.new - attr[:flags] = [] - source.scan(ATTRIBUTE_SCAN) { |key, value| - if not value - attr[:flags] << unescape(key) - else - next if value.empty? and key.empty? - attr[unescape(key)] = unescape(value) - end - } - debug attr.inspect - - return empty_tag, attr - end - - def self.unescape_char esc - esc[1] - end - - def self.unquote qt - qt[1..-1].chomp('"').gsub(/\\./) { |esc| unescape_char esc } - end - - def self.unescape str - str.gsub(/ (\\.) | (" [^"\\]* (?:\\.[^"\\]*)* "?) /x) { - if $1 - unescape_char $1 - else - unquote $2 - end - } - end - - include Enumerable - def each &block - @args.each(&block) - end - - attr_reader :source, :args, :value - - def initialize source - @source = source - debug 'Attribute#new(%p)' % source - @empty_tag, @attr = Attribute.parse source - @value = @attr[''].to_s - end - - def empty? - self == @@empty_attr - end - - def empty_tag? - @empty_tag - end - - def [] *keys - res = @attr[*keys] - end - - def flags - attr[:flags] - end - - def to_s - @attr - end - - def inspect - 'ATTR[' + @attr.inspect + (@empty_tag ? ' | empty tag' : '') + ']' - end - end - class Attribute - @@empty_attr = new '' - end - end - - class Parser - def Parser.flatten str - # replace mac & dos newlines with unix style - str.gsub(/\r\n?/, "\n") - end - - def initialize input = '' - # input manager - @scanner = StringScanner.new '' - # output manager - @encoder = Encoder.new - @output = '' - # tag manager - @tagstack = TagStack.new(@encoder) - - @do_magic = true - # set the input - feed input - end - - # if you want, you can feed a parser instance after creating, - # or even feed it repeatedly. - def feed food - @scanner.string = Parser.flatten food - end - - # parse through the string using parse_token - def parse - parse_token until @scanner.eos? - @tagstack.close_all - @output = parse_magic @encoder.output - end - - def output - @output - end - - # ok, internals start here - private - # the default output functions. everything should use them or the tags. - def add_text text = @scanner.matched - @encoder.add_text text - end - - # use this carefully - def add_html html - @encoder.add_html html - end - - # highlights the text as error - def add_garbage garbage - add_html '<span class="error">' if DEBUG - add_text garbage - add_html '</span>' if DEBUG - end - - # unknown and incorrectly nested tags are ignored and - # sent as plaintext (garbage in - garbage out). - # in debug mode, garbage is marked with lime background. - def garbage_out start - @scanner.pos = start - garbage = @scanner.scan(/./m) - debug 'GARBAGE: ' + garbage - add_garbage garbage - end - - # simple text; everything but [, \[ allowed - SIMPLE_TEXT_SCAN_ = / - [^\[\\]* # normal* - (?: # ( - \\.? # special - [^\[\\]* # normal* - )* # )* - /mx - SIMPLE_TEXT_SCAN = /[^\[]+/ - -=begin - - WHAT IS A TAG? - ============== - - Tags in BBCode can be much more than just a simple [b]. - I use many terms here to differ the parts of each tag. - - Basic scheme: - [ code ] - TAG START TAG INFO TAG END - - Most tags need a second tag to close the range it opened. - This is done with CLOSING TAGS: - [/code] - or by using empty tags that have no content and close themselfes: - [url=winamp.com /] - You surely know this from HTML. - These slashes define the TAG KIND = normal|closing|empty and - cannot be used together. - - Everything between [ and ] and expluding the slashes is called the - TAG INFO. This info may contain: - - TAG ID - - TAG NAME including the tag id - - attributes - - The TAG ID is the first char of the info: - - TAG | ID - ----------+---- - [quote] | q - [±] | & - ["[b]"] | " - [/url] | u - [---] | - - - As you can see, the tag id shows the TAG TYPE, it can be a - normal tag, a formatting tag or an entity. - Therefor, the parser first scans the id to decide how to go - on with parsing. -=end - # tag - # TODO more complex expression allowing - # [quote="[ladico]"] and [quote=\[ladico\]] to be correct tags - TAG_BEGIN_SCAN = / - \[ # tag start - ( \/ )? # $1 = closing tag? - ( [^\]] ) # $2 = tag id - /x - TAG_END_SCAN = / - [^\]]* # rest that was not handled - \]? # tag end - /x - CLOSE_TAG_SCAN = / - ( [^\]]* ) # $1 = the rest of the tag info - ( \/ )? # $2 = empty tag? - \]? # tag end - /x - UNCLOSED_TAG_SCAN = / \[ /x - - CLASSIC_TAG_SCAN = / [a-z]* /ix - - SEPARATOR_TAG_SCAN = / \** /x - - FORMAT_TAG_SCAN = / -- -* /x - - QUOTED_SCAN = / - ( # $1 = quoted text - [^"\\]* # normal* - (?: # ( - \\. # special - [^"\\]* # normal* - )* # )* - ) - "? # end quote " - /mx - - ENTITY_SCAN = / - ( [^;\]]+ ) # $1 = entity code - ;? # optional ending semicolon - /ix - - SMILEY_SCAN = Smileys::SMILEY_PATTERN - - # this is the main parser loop that separates - # text - everything until "[" - # from - # tags - starting with "[", ending with "]" - def parse_token - if @scanner.scan(SIMPLE_TEXT_SCAN) - add_text - else - handle_tag - end - end - - def handle_tag - tag_start = @scanner.pos - - unless @scanner.scan TAG_BEGIN_SCAN - garbage_out tag_start - return - end - - closing, id = @scanner[1], @scanner[2] - #debug 'handle_tag(%p)' % @scanner.matched - - handled = - case id - - when /[a-z]/i - if @scanner.scan(CLASSIC_TAG_SCAN) - if handle_classic_tag(id + @scanner.matched, closing) - already_closed = true - end - end - - when '*' - if @scanner.scan(SEPARATOR_TAG_SCAN) - handle_asterisk tag_start, id + @scanner.matched - true - end - - when '-' - if @scanner.scan(FORMAT_TAG_SCAN) - #format = id + @scanner.matched - @encoder.add_html "\n<hr>\n" - true - end - - when '"' - if @scanner.scan(QUOTED_SCAN) - @encoder.add_text unescape(@scanner[1]) - true - end - - when '&' - if @scanner.scan(ENTITY_SCAN) - @encoder.add_entity @scanner[1] - true - end - - when Smileys::SMILEY_START_CHARSET - @scanner.pos = @scanner.pos - 1 # (ungetch) - if @scanner.scan(SMILEY_SCAN) - @encoder.add_html Smileys.smiley_to_image(@scanner.matched) - true - end - - end # case - - return garbage_out(tag_start) unless handled - - @scanner.scan(TAG_END_SCAN) unless already_closed - end - - ATTRIBUTES_SCAN = / - ( - [^\]"\\]* - (?: - (?: - \\. - | - " - [^"\\]* - (?: - \\. - [^"\\]* - )* - "? - ) - [^\]"\\]* - )* - ) - \]? - /x - - def handle_classic_tag name, closing - debug 'TAG: ' + (closing ? '/' : '') + name - # flatten - name.downcase! - tag_class = TAG_LIST[name] - return unless tag_class - - #debug((opening ? 'OPEN ' : 'CLOSE ') + tag_class.name) - - # create an attribute object to handle it - @scanner.scan(ATTRIBUTES_SCAN) - #debug name + ':' + @scanner[1] - attr = Attribute.create @scanner[1] - #debug 'ATTRIBUTES %p ' % attr #unless attr.empty? - - #debug 'closing: %p; name=%s, attr=%p' % [closing, name, attr] - - # OPEN - if not closing and tag = @tagstack.try_open_class(tag_class, attr) - #debug 'opening' - tag.do_open @scanner - # this should be done by the tag itself. - if attr.empty_tag? - tag.handle_empty - @tagstack.close_tag - elsif tag.special_content? - handle_special_content(tag) - @tagstack.close_tag - # # ignore asterisks directly after the opening; these are phpBBCode - # elsif tag.respond_to? :asterisk - # debug 'SKIP ASTERISKS: ' if @scanner.skip(ASTERISK_TAGS_SCAN) - end - - # CLOSE - elsif @tagstack.try_close_class(tag_class) - #debug 'closing' - # GARBAGE - else - return - end - - true - end - - def handle_asterisk tag_start, stars - #debug 'ASTERISK: ' + stars.to_s - # rule for asterisk tags: they belong to the last tag - # that handles them. tags opened after this tag are closed. - # if no open tag uses them, all are closed. - tag = @tagstack.close_all_until { |tag| tag.respond_to? :asterisk } - unless tag and tag.asterisk stars, @scanner - garbage_out tag_start - end - end - - def handle_special_content tag - scanned = @scanner.scan_until(tag.closing_tag) - if scanned - scanned.slice!(-(@scanner.matched.size)..-1) - else - scanned = @scanner.scan(/.*/m).to_s - end - #debug 'SPECIAL CONTENT: ' + scanned - tag.handle_content(scanned) - end - - def unescape text - # input: correctly formatted quoted string (without the quotes) - text.gsub(/\\(?:(["\\])|.)/) { $1 or $& } - end - - - # MAGIC FEAUTURES - - URL_PATTERN = /(?:(?:www|ftp)\.|(?>\w{3,}):\/\/)\S+/ - EMAIL_PATTERN = /(?>[\w\-_.]+)@[\w\-\.]+\.\w+/ - - HAS_MAGIC = /[&@#{Smileys::SMILEY_START_CHARS}]|(?i:www|ftp)/ - - MAGIC_PATTERN = Regexp.new('(\W|^)(%s)' % - [Smileys::MAGIC_SMILEY_PATTERN, URL_PATTERN, EMAIL_PATTERN].map { |pattern| - pattern.to_s - }.join('|') ) - - IS_SMILEY_PATTERN = Regexp.new('^%s' % Smileys::SMILEY_START_CHARSET.to_s ) - IS_URL_PATTERN = /^(?:(?i:www|ftp)\.|(?>\w+):\/\/)/ - URL_STARTS_WITH_PROTOCOL = /^\w+:\/\// - IS_EMAIL_PATTERN = /^[\w\-_.]+@/ - - def to_magic text - # debug MAGIC_PATTERN.to_s - text.gsub!(MAGIC_PATTERN) { - magic = $2 - $1 + case magic - when IS_SMILEY_PATTERN - Smileys.smiley_to_img magic - when IS_URL_PATTERN - last = magic.slice_punctation! # no punctation in my URL - href = magic - href.insert(0, 'http://') unless magic =~ URL_STARTS_WITH_PROTOCOL - '<a href="' + href + '">' + magic + '</a>' + last - when IS_EMAIL_PATTERN - last = magic.slice_punctation! - '<a href="mailto:' + magic + '">' + magic + '</a>' + last - else - raise '{{{' + magic + '}}}' - end - } - text - end - - # handles smileys and urls - def parse_magic html - return html unless @do_magic - scanner = StringScanner.new html - out = '' - while scanner.rest? - if scanner.scan(/ < (?: a\s .*? <\/a> | pre\W .*? <\/pre> | [^>]* > ) /mx) - out << scanner.matched - elsif scanner.scan(/ [^<]+ /x) - out << to_magic(scanner.matched) - - # this should never happen - elsif scanner.scan(/./m) - raise 'ERROR: else case reached' - end - end - out - end - end # Parser -end - -class String - def slice_punctation! - slice!(/[.:,!\?]+$/).to_s # return '' instead of nil - end -end - -# -# = Grammar -# -# An implementation of common algorithms on grammars. -# -# This is used by Shinobu, a visualization tool for educating compiler-building. -# -# Thanks to Andreas Kunert for his wonderful LR(k) Pamphlet (German, see http://www.informatik.hu-berlin.de/~kunert/papers/lr-analyse), and Aho/Sethi/Ullman for their Dragon Book. -# -# Homepage:: http://shinobu.cYcnus.de (not existing yet) -# Author:: murphy (Kornelius Kalnbach) -# Copyright:: (cc) 2005 cYcnus -# License:: GPL -# Version:: 0.2.0 (2005-03-27) - -require 'set_hash' -require 'ctype' -require 'tools' -require 'rules' -require 'trace' - -require 'first' -require 'follow' - -# = Grammar -# -# == Syntax -# -# === Rules -# -# Each line is a rule. -# The syntax is -# -# left - right -# -# where +left+ and +right+ can be uppercase and lowercase letters, -# and <code>-</code> can be any combination of <, >, - or whitespace. -# -# === Symbols -# -# Uppercase letters stand for meta symbols, lowercase for terminals. -# -# You can make epsilon-derivations by leaving <code><right></code> empty. -# -# === Example -# S - Ac -# A - Sc -# A - b -# A - -class Grammar - - attr_reader :tracer - # Creates a new Grammar. - # If $trace is true, the algorithms explain (textual) what they do to $stdout. - def initialize data, tracer = Tracer.new - @tracer = tracer - @rules = Rules.new - @terminals, @meta_symbols = SortedSet.new, Array.new - @start_symbol = nil - add_rules data - end - - attr_reader :meta_symbols, :terminals, :rules, :start_symbol - - alias_method :sigma, :terminals - alias_method :alphabet, :terminals - alias_method :variables, :meta_symbols - alias_method :nonterminals, :meta_symbols - - # A string representation of the grammar for debugging. - def inspect productions_too = false - 'Grammar(meta symbols: %s; alphabet: %s; productions: [%s]; start symbol: %s)' % - [ - meta_symbols.join(', '), - terminals.join(', '), - if productions_too - @rules.inspect - else - @rules.size - end, - start_symbol - ] - end - - # Add rules to the grammar. +rules+ should be a String or respond to +scan+ in a similar way. - # - # Syntax: see Grammar. - def add_rules grammar - @rules = Rules.parse grammar do |rule| - @start_symbol ||= rule.left - @meta_symbols << rule.left - @terminals.merge rule.right.split('').select { |s| terminal? s } - end - @meta_symbols.uniq! - update - end - - # Returns a hash acting as FIRST operator, so that - # <code>first["ABC"]</code> is FIRST(ABC). - # See http://en.wikipedia.org/wiki/LL_parser "Constructing an LL(1) parsing table" for details. - def first - first_operator - end - - # Returns a hash acting as FOLLOW operator, so that - # <code>first["A"]</code> is FOLLOW(A). - # See http://en.wikipedia.org/wiki/LL_parser "Constructing an LL(1) parsing table" for details. - def follow - follow_operator - end - - LLError = Class.new(Exception) - LLErrorType1 = Class.new(LLError) - LLErrorType2 = Class.new(LLError) - - # Tests if the grammar is LL(1). - def ll1? - begin - for meta in @meta_symbols - first_sets = @rules[meta].map { |alpha| first[alpha] } - first_sets.inject(Set[]) do |already_used, another_first_set| - unless already_used.disjoint? another_first_set - raise LLErrorType1 - end - already_used.merge another_first_set - end - - if first[meta].include? EPSILON and not first[meta].disjoint? follow[meta] - raise LLErrorType2 - end - end - rescue LLError - false - else - true - end - end - -private - - def first_operator - @first ||= FirstOperator.new self - end - - def follow_operator - @follow ||= FollowOperator.new self - end - - def update - @first = @follow = nil - end - -end - -if $0 == __FILE__ - eval DATA.read, nil, $0, __LINE__+4 -end - -require 'test/unit' - -class TestCaseGrammar < Test::Unit::TestCase - - include Grammar::Symbols - - def fifo s - Set[*s.split('')] - end - - def test_fifo - assert_equal Set[], fifo('') - assert_equal Set[EPSILON, END_OF_INPUT, 'x', 'Y'], fifo('?xY$') - end - - TEST_GRAMMAR_1 = <<-EOG -S - ABCD -A - a -A - -B - b -B - -C - c -C - -D - S -D - - EOG - - def test_symbols - assert EPSILON - assert END_OF_INPUT - end - - def test_first_1 - g = Grammar.new TEST_GRAMMAR_1 - - f = nil - assert_nothing_raised { f = g.first } - assert_equal(Set['a', EPSILON], f['A']) - assert_equal(Set['b', EPSILON], f['B']) - assert_equal(Set['c', EPSILON], f['C']) - assert_equal(Set['a', 'b', 'c', EPSILON], f['D']) - assert_equal(f['D'], f['S']) - end - - def test_follow_1 - g = Grammar.new TEST_GRAMMAR_1 - - f = nil - assert_nothing_raised { f = g.follow } - assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['A']) - assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['B']) - assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['C']) - assert_equal(Set[END_OF_INPUT], f['D']) - assert_equal(Set[END_OF_INPUT], f['S']) - end - - - TEST_GRAMMAR_2 = <<-EOG -S - Ed -E - EpT -E - EmT -E - T -T - TuF -T - TdF -T - F -F - i -F - n -F - aEz - EOG - - def test_first_2 - g = Grammar.new TEST_GRAMMAR_2 - - f = nil - assert_nothing_raised { f = g.first } - assert_equal(Set['a', 'n', 'i'], f['E']) - assert_equal(Set['a', 'n', 'i'], f['F']) - assert_equal(Set['a', 'n', 'i'], f['T']) - assert_equal(Set['a', 'n', 'i'], f['S']) - end - - def test_follow_2 - g = Grammar.new TEST_GRAMMAR_2 - - f = nil - assert_nothing_raised { f = g.follow } - assert_equal(Set['m', 'd', 'z', 'p'], f['E']) - assert_equal(Set['m', 'd', 'z', 'p', 'u'], f['F']) - assert_equal(Set['m', 'd', 'z', 'p', 'u'], f['T']) - assert_equal(Set[END_OF_INPUT], f['S']) - end - - LLError = Grammar::LLError - - TEST_GRAMMAR_3 = <<-EOG -E - TD -D - pTD -D - -T - FS -S - uFS -S - -S - p -F - aEz -F - i - EOG - - NoError = Class.new(Exception) - - def test_first_3 - g = Grammar.new TEST_GRAMMAR_3 - - # Grammar 3 is LL(1), so all first-sets must be disjoint. - f = nil - assert_nothing_raised { f = g.first } - assert_equal(Set['a', 'i'], f['E']) - assert_equal(Set[EPSILON, 'p'], f['D']) - assert_equal(Set['a', 'i'], f['F']) - assert_equal(Set['a', 'i'], f['T']) - assert_equal(Set[EPSILON, 'u', 'p'], f['S']) - for m in g.meta_symbols - r = g.rules[m] - firsts = r.map { |x| f[x] }.to_set - assert_nothing_raised do - firsts.inject(Set.new) do |already_used, another_first_set| - raise LLError, 'not disjoint!' unless already_used.disjoint? another_first_set - already_used.merge another_first_set - end - end - end - end - - def test_follow_3 - g = Grammar.new TEST_GRAMMAR_3 - - # Grammar 3 is not LL(1), because epsilon is in FIRST(S), - # but FIRST(S) and FOLLOW(S) are not disjoint. - f = nil - assert_nothing_raised { f = g.follow } - assert_equal(Set['z', END_OF_INPUT], f['E']) - assert_equal(Set['z', END_OF_INPUT], f['D']) - assert_equal(Set['z', 'p', 'u', END_OF_INPUT], f['F']) - assert_equal(Set['p', 'z', END_OF_INPUT], f['T']) - assert_equal(Set['p', 'z', END_OF_INPUT], f['S']) - for m in g.meta_symbols - first_m = g.first[m] - next unless first_m.include? EPSILON - assert_raise(m == 'S' ? LLError : NoError) do - if first_m.disjoint? f[m] - raise NoError # this is fun :D - else - raise LLError - end - end - end - end - - TEST_GRAMMAR_3b = <<-EOG -E - TD -D - pTD -D - PTD -D - -T - FS -S - uFS -S - -F - aEz -F - i -P - p - EOG - - def test_first_3b - g = Grammar.new TEST_GRAMMAR_3b - - # Grammar 3b is NOT LL(1), since not all first-sets are disjoint. - f = nil - assert_nothing_raised { f = g.first } - assert_equal(Set['a', 'i'], f['E']) - assert_equal(Set[EPSILON, 'p'], f['D']) - assert_equal(Set['p'], f['P']) - assert_equal(Set['a', 'i'], f['F']) - assert_equal(Set['a', 'i'], f['T']) - assert_equal(Set[EPSILON, 'u'], f['S']) - for m in g.meta_symbols - r = g.rules[m] - firsts = r.map { |x| f[x] } - assert_raise(m == 'D' ? LLError : NoError) do - firsts.inject(Set.new) do |already_used, another_first_set| - raise LLError, 'not disjoint!' unless already_used.disjoint? another_first_set - already_used.merge another_first_set - end - raise NoError - end - end - end - - def test_follow_3b - g = Grammar.new TEST_GRAMMAR_3b - - # Although Grammar 3b is NOT LL(1), the FOLLOW-condition is satisfied. - f = nil - assert_nothing_raised { f = g.follow } - assert_equal(fifo('z$'), f['E'], 'E') - assert_equal(fifo('z$'), f['D'], 'D') - assert_equal(fifo('ai'), f['P'], 'P') - assert_equal(fifo('z$pu'), f['F'], 'F') - assert_equal(fifo('z$p'), f['T'], 'T') - assert_equal(fifo('z$p'), f['S'], 'S') - for m in g.meta_symbols - first_m = g.first[m] - next unless first_m.include? EPSILON - assert_raise(NoError) do - if first_m.disjoint? f[m] - raise NoError # this is fun :D - else - raise LLError - end - end - end - end - - def test_ll1? - assert_equal false, Grammar.new(TEST_GRAMMAR_3).ll1?, 'Grammar 3' - assert_equal false, Grammar.new(TEST_GRAMMAR_3b).ll1?, 'Grammar 3b' - end - - def test_new - assert_nothing_raised { Grammar.new '' } - assert_nothing_raised { Grammar.new TEST_GRAMMAR_1 } - assert_nothing_raised { Grammar.new TEST_GRAMMAR_2 } - assert_nothing_raised { Grammar.new TEST_GRAMMAR_3 } - assert_nothing_raised { Grammar.new TEST_GRAMMAR_1 + TEST_GRAMMAR_2 + TEST_GRAMMAR_3 } - assert_raise(ArgumentError) { Grammar.new 'S - ?' } - end -end - -# vim:foldmethod=syntax - -#!/usr/bin/env ruby - -require 'fox12' - -include Fox - -class Window < FXMainWindow - def initialize(app) - super(app, app.appName + ": First Set Calculation", nil, nil, DECOR_ALL, 0, 0, 800, 600, 0, 0) - - # {{{ menubar - menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X) - - filemenu = FXMenuPane.new(self) - - FXMenuCommand.new(filemenu, "&Start\tCtl-S\tStart the application.", nil, getApp()).connect(SEL_COMMAND, method(:start)) - FXMenuCommand.new(filemenu, "&Quit\tAlt-F4\tQuit the application.", nil, getApp(), FXApp::ID_QUIT) - FXMenuTitle.new(menubar, "&File", nil, filemenu) - # }}} menubar - - # {{{ statusbar - @statusbar = FXStatusBar.new(self, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER) - # }}} statusbar - - # {{{ window content - horizontalsplitt = FXSplitter.new(self, SPLITTER_VERTICAL|LAYOUT_SIDE_TOP|LAYOUT_FILL) - - - @productions = FXList.new(horizontalsplitt, nil, 0, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT|LIST_SINGLESELECT) - @productions.height = 100 - - @result = FXTable.new(horizontalsplitt, nil, 0, LAYOUT_FILL) - @result.height = 200 - @result.setTableSize(2, 2, false) - @result.rowHeaderWidth = 0 - - header = @result.columnHeader - header.setItemText 0, 'X' - header.setItemText 1, 'FIRST(X)' - for item in header - item.justification = FXHeaderItem::CENTER_X - end - - @debug = FXText.new(horizontalsplitt, nil, 0, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT) - @debug.height = 200 - - # }}} window content - end - - def load_grammar grammar - @tracer = FirstTracer.new(self) - @grammar = Grammar.new grammar, @tracer - @rules_indexes = Hash.new - @grammar.rules.each_with_index do |rule, i| - @productions.appendItem rule.inspect - @rules_indexes[rule] = i - end - end - - def create - super - show(PLACEMENT_SCREEN) - end - - def rule rule - @productions.selectItem @rules_indexes[rule] - sleep 0.1 - end - - def iterate i - setTitle i.to_s - sleep 0.1 - end - - def missing what - @debug.appendText what + "\n" - sleep 0.1 - end - - def start sender, sel, pointer - Thread.new do - begin - @grammar.first - rescue => boom - @debug.appendText [boom.to_s, *boom.backtrace].join("\n") - end - end - end - -end - -$: << 'grammar' -require 'grammar' - -require 'first_tracer' - -app = FXApp.new("Shinobu", "cYcnus") - -# fenster erzeugen -window = Window.new app - -unless ARGV.empty? - grammar = File.read(ARGV.first) -else - grammar = <<-EOG1 -Z --> S -S --> Sb -S --> bAa -A --> aSc -A --> a -A --> aSb - EOG1 -end - -window.load_grammar grammar - -app.create -app.run - -require 'erb' -require 'ftools' -require 'yaml' -require 'redcloth' - -module WhyTheLuckyStiff - class Book - attr_accessor :author, :title, :terms, :image, :teaser, - :chapters, :expansion_paks, :encoding, :credits - def [] x - @lang.fetch(x) do - warn warning = "[not translated: '#{x}'!]" - warning - end - end - end - - def Book::load( file_name ) - YAML::load( File.open( file_name ) ) - end - - class Section - attr_accessor :index, :header, :content - def initialize( i, h, c ) - @index, @header, @content = i, h, RedCloth::new( c.to_s ) - end - end - - class Sidebar - attr_accessor :title, :content - end - - YAML::add_domain_type( 'whytheluckystiff.net,2003', 'sidebar' ) do |taguri, val| - YAML::object_maker( Sidebar, 'title' => val.keys.first, 'content' => RedCloth::new( val.values.first ) ) - end - class Chapter - attr_accessor :index, :title, :sections - def initialize( i, t, sects ) - @index = i - @title = t - i = 0 - @sections = sects.collect do |s| - if s.respond_to?( :keys ) - i += 1 - Section.new( i, s.keys.first, s.values.first ) - else - s - end - end - end - end - - YAML::add_domain_type( 'whytheluckystiff.net,2003', 'book' ) do |taguri, val| - ['chapters', 'expansion_paks'].each do |chaptype| - i = 0 - val[chaptype].collect! do |c| - i += 1 - Chapter::new( i, c.keys.first, c.values.first ) - end - end - val['teaser'].collect! do |t| - Section::new( 1, t.keys.first, t.values.first ) - end - val['terms'] = RedCloth::new( val['terms'] ) - YAML::object_maker( Book, val ) - end - - class Image - attr_accessor :file_name - end - - YAML::add_domain_type( 'whytheluckystiff.net,2003', 'img' ) do |taguri, val| - YAML::object_maker( Image, 'file_name' => "i/" + val ) - end -end - -# -# Convert the book to HTML -# -if __FILE__ == $0 - unless ARGV[0] - puts "Usage: #{$0} [/path/to/save/html]" - exit - end - - site_path = ARGV[0] - book = WhyTheLuckyStiff::Book::load( 'poignant.yml' ) - chapter = nil - - # Write index page - index_tpl = ERB::new( File.open( 'index.erb' ).read ) - File.open( File.join( site_path, 'index.html' ), 'w' ) do |out| - out << index_tpl.result - end - - book.chapters = book.chapters[0,3] if ARGV.include? '-fast' - - # Write chapter pages - chapter_tpl = ERB::new( File.open( 'chapter.erb' ).read ) - book.chapters.each do |chapter| - File.open( File.join( site_path, "chapter-#{ chapter.index }.html" ), 'w' ) do |out| - out << chapter_tpl.result - end - end - exit if ARGV.include? '-fast' - - # Write expansion pak pages - expak_tpl = ERB::new( File.open( 'expansion-pak.erb' ).read ) - book.expansion_paks.each do |pak| - File.open( File.join( site_path, "expansion-pak-#{ pak.index }.html" ), 'w' ) do |out| - out << expak_tpl.result( binding ) - end - end - - # Write printable version - print_tpl = ERB::new( File.open( 'print.erb' ).read ) - File.open( File.join( site_path, "print.html" ), 'w' ) do |out| - out << print_tpl.result - end - - # Copy css + images into site - copy_list = ["guide.css"] + - Dir["i/*"].find_all { |image| image =~ /\.(gif|jpg|png)$/ } - - File.makedirs( File.join( site_path, "i" ) ) - copy_list.each do |copy_file| - File.copy( copy_file, File.join( site_path, copy_file ) ) - end -end - -#!/usr/bin/env ruby - -require 'fox' -begin - require 'opengl' -rescue LoadError - require 'fox/missingdep' - MSG = <<EOM - Sorry, this example depends on the OpenGL extension. Please - check the Ruby Application Archives for an appropriate - download site. -EOM - missingDependency(MSG) -end - - -include Fox -include Math - -Deg2Rad = Math::PI / 180 - -D_MAX = 6 -SQUARE_SIZE = 2.0 / D_MAX -SQUARE_DISTANCE = 4.0 / D_MAX -AMPLITUDE = SQUARE_SIZE -LAMBDA = D_MAX.to_f / 2 - -class GLTestWindow < FXMainWindow - - # How often our timer will fire (in milliseconds) - TIMER_INTERVAL = 500 - - # Rotate the boxes when a timer message is received - def onTimeout(sender, sel, ptr) - @angle += 10.0 -# @size = 0.5 + 0.2 * Math.cos(Deg2Rad * @angle) - drawScene() - @timer = getApp().addTimeout(TIMER_INTERVAL, method(:onTimeout)) - end - - # Rotate the boxes when a chore message is received - def onChore(sender, sel, ptr) - @angle += 10.0 -# @angle %= 360.0 -# @size = 0.5 + 0.2 * Math.cos(Deg2Rad * @angle) - drawScene() - @chore = getApp().addChore(method(:onChore)) - end - - # Draw the GL scene - def drawScene - lightPosition = [15.0, 10.0, 5.0, 1.0] - lightAmbient = [ 0.1, 0.1, 0.1, 1.0] - lightDiffuse = [ 0.9, 0.9, 0.9, 1.0] - redMaterial = [ 0.0, 0.0, 1.0, 1.0] - blueMaterial = [ 0.0, 1.0, 0.0, 1.0] - - width = @glcanvas.width.to_f - height = @glcanvas.height.to_f - aspect = width/height - - # Make context current - @glcanvas.makeCurrent() - - GL.Viewport(0, 0, @glcanvas.width, @glcanvas.height) - - GL.ClearColor(1.0/256, 0.0, 5.0/256, 1.0) - GL.Clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT) - GL.Enable(GL::DEPTH_TEST) - - GL.Disable(GL::DITHER) - - GL.MatrixMode(GL::PROJECTION) - GL.LoadIdentity() - GLU.Perspective(30.0, aspect, 1.0, 100.0) - - GL.MatrixMode(GL::MODELVIEW) - GL.LoadIdentity() - GLU.LookAt(5.0, 10.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) - - GL.ShadeModel(GL::SMOOTH) - GL.Light(GL::LIGHT0, GL::POSITION, lightPosition) - GL.Light(GL::LIGHT0, GL::AMBIENT, lightAmbient) - GL.Light(GL::LIGHT0, GL::DIFFUSE, lightDiffuse) - GL.Enable(GL::LIGHT0) - GL.Enable(GL::LIGHTING) - - GL.Rotated(0.1*@angle, 0.0, 1.0, 0.0) - for x in -D_MAX..D_MAX - for y in -D_MAX..D_MAX - h1 = (x + y - 2).abs - h2 = (y - x + 1).abs - GL.PushMatrix - c = [1, 0, 0, 1] - GL.Material(GL::FRONT, GL::AMBIENT, c) - GL.Material(GL::FRONT, GL::DIFFUSE, c) - - GL.Translated( - y * SQUARE_DISTANCE, - AMPLITUDE * h1, - x * SQUARE_DISTANCE - ) - - GL.Begin(GL::TRIANGLE_STRIP) - GL.Normal(1.0, 0.0, 0.0) - GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE) - GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE) - GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE) - GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE) - GL.End - - GL.PopMatrix - - GL.PushMatrix - c = [0, 0, 1, 1] - GL.Material(GL::FRONT, GL::AMBIENT, c) - GL.Material(GL::FRONT, GL::DIFFUSE, c) - - GL.Translated( - y * SQUARE_DISTANCE, - AMPLITUDE * h2, - x * SQUARE_DISTANCE - ) - - GL.Begin(GL::TRIANGLE_STRIP) - GL.Normal(1.0, 0.0, 0.0) - GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE) - GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE) - GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE) - GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE) - GL.End - - GL.PopMatrix - - GL.PushMatrix - c = [0.0 + (x/10.0), 0.0 + (y/10.0), 0, 1] - GL.Material(GL::FRONT, GL::AMBIENT, c) - GL.Material(GL::FRONT, GL::DIFFUSE, c) - - GL.Translated( - y * SQUARE_DISTANCE, - 0, - x * SQUARE_DISTANCE - ) - - GL.Begin(GL::TRIANGLE_STRIP) - GL.Normal(1.0, 0.0, 0.0) - GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE) - GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE) - GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE) - GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE) - GL.End - - GL.PopMatrix - end - end - - # Swap if it is double-buffered - if @glvisual.isDoubleBuffer - @glcanvas.swapBuffers - end - - # Make context non-current - @glcanvas.makeNonCurrent - end - - def initialize(app) - # Invoke the base class initializer - super(app, "OpenGL Test Application", nil, nil, DECOR_ALL, 0, 0, 1024, 768) - - # Construct the main window elements - frame = FXHorizontalFrame.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y) - frame.padLeft, frame.padRight = 0, 0 - frame.padTop, frame.padBottom = 0, 0 - - # Left pane to contain the glcanvas - glcanvasFrame = FXVerticalFrame.new(frame, - LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT) - glcanvasFrame.padLeft, glcanvasFrame.padRight = 10, 10 - glcanvasFrame.padTop, glcanvasFrame.padBottom = 10, 10 - - # Label above the glcanvas - FXLabel.new(glcanvasFrame, "OpenGL Canvas Frame", nil, - JUSTIFY_CENTER_X|LAYOUT_FILL_X) - - # Horizontal divider line - FXHorizontalSeparator.new(glcanvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X) - - # Drawing glcanvas - glpanel = FXVerticalFrame.new(glcanvasFrame, (FRAME_SUNKEN|FRAME_THICK| - LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)) - glpanel.padLeft, glpanel.padRight = 0, 0 - glpanel.padTop, glpanel.padBottom = 0, 0 - - # A visual to draw OpenGL - @glvisual = FXGLVisual.new(getApp(), VISUAL_DOUBLEBUFFER) - - # Drawing glcanvas - @glcanvas = FXGLCanvas.new(glpanel, @glvisual, nil, 0, - LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT) - @glcanvas.connect(SEL_PAINT) { - drawScene - } - @glcanvas.connect(SEL_CONFIGURE) { - if @glcanvas.makeCurrent - GL.Viewport(0, 0, @glcanvas.width, @glcanvas.height) - @glcanvas.makeNonCurrent - end - } - - # Right pane for the buttons - buttonFrame = FXVerticalFrame.new(frame, LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT) - buttonFrame.padLeft, buttonFrame.padRight = 10, 10 - buttonFrame.padTop, buttonFrame.padBottom = 10, 10 - - # Label above the buttons - FXLabel.new(buttonFrame, "Button Frame", nil, - JUSTIFY_CENTER_X|LAYOUT_FILL_X) - - # Horizontal divider line - FXHorizontalSeparator.new(buttonFrame, SEPARATOR_RIDGE|LAYOUT_FILL_X) - - # Spin according to timer - spinTimerBtn = FXButton.new(buttonFrame, - "Spin &Timer\tSpin using interval timers\nNote the app - blocks until the interal has elapsed...", nil, - nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT) - spinTimerBtn.padLeft, spinTimerBtn.padRight = 10, 10 - spinTimerBtn.padTop, spinTimerBtn.padBottom = 5, 5 - spinTimerBtn.connect(SEL_COMMAND) { - @spinning = true - @timer = getApp().addTimeout(TIMER_INTERVAL, method(:onTimeout)) - } - spinTimerBtn.connect(SEL_UPDATE) { |sender, sel, ptr| - @spinning ? sender.disable : sender.enable - } - - # Spin according to chore - spinChoreBtn = FXButton.new(buttonFrame, - "Spin &Chore\tSpin as fast as possible using chores\nNote even though the - app is very responsive, it never blocks;\nthere is always something to - do...", nil, - nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT) - spinChoreBtn.padLeft, spinChoreBtn.padRight = 10, 10 - spinChoreBtn.padTop, spinChoreBtn.padBottom = 5, 5 - spinChoreBtn.connect(SEL_COMMAND) { - @spinning = true - @chore = getApp().addChore(method(:onChore)) - } - spinChoreBtn.connect(SEL_UPDATE) { |sender, sel, ptr| - @spinning ? sender.disable : sender.enable - } - - # Stop spinning - stopBtn = FXButton.new(buttonFrame, - "&Stop Spin\tStop this mad spinning, I'm getting dizzy", nil, - nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT) - stopBtn.padLeft, stopBtn.padRight = 10, 10 - stopBtn.padTop, stopBtn.padBottom = 5, 5 - stopBtn.connect(SEL_COMMAND) { - @spinning = false - if @timer - getApp().removeTimeout(@timer) - @timer = nil - end - if @chore - getApp().removeChore(@chore) - @chore = nil - end - } - stopBtn.connect(SEL_UPDATE) { |sender, sel, ptr| - @spinning ? sender.enable : sender.disable - } - - # Exit button - exitBtn = FXButton.new(buttonFrame, "&Exit\tExit the application", nil, - getApp(), FXApp::ID_QUIT, - FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT) - exitBtn.padLeft, exitBtn.padRight = 10, 10 - exitBtn.padTop, exitBtn.padBottom = 5, 5 - - # Make a tooltip - FXTooltip.new(getApp()) - - # Initialize private variables - @spinning = false - @chore = nil - @timer = nil - @angle = 0.0 - @size = 0.5 - end - - # Create and initialize - def create - super - show(PLACEMENT_SCREEN) - end -end - -if __FILE__ == $0 - # Construct the application - application = FXApp.new("GLTest", "FoxTest") - - # To ensure that the chores-based spin will run as fast as possible, - # we can disable the chore in FXRuby's event loop that tries to schedule - # other threads. This is OK for this program because there aren't any - # other Ruby threads running. - - #application.disableThreads - - # Construct the main window - GLTestWindow.new(application) - - # Create the app's windows - application.create - - # Run the application - application.run -end - -class Facelet - attr_accessor :color - def initialize(color) - @color = color - end - - def to_s - @color - end -end - -class Edge - attr_accessor :facelets, :colors - - def initialize(facelets) - @facelets = facelets - @colors = @facelets.map { |fl| fl.color } - end - - def apply(edge) - @facelets.each_with_index { |fl, i| - fl.color = edge.colors[i] - } - end - - def inspect - "\n%s %s\n%s %s %s" % facelets - end -end - -class Side - attr_reader :num, :facelets - attr_accessor :sides - - def initialize(num) - @num = num - @sides = [] - @facelets = [] - @fl_by_side = {} - end - - # facelets & sides - # 0 - # 0 1 2 - # 3 3 4 5 1 - # 6 7 8 - # 2 - - def facelets=(facelets) - @facelets = facelets.map { |c| Facelet.new(c) } - init_facelet 0, 3,0 - init_facelet 1, 0 - init_facelet 2, 0,1 - init_facelet 3, 3 - init_facelet 5, 1 - init_facelet 6, 2,3 - init_facelet 7, 2 - init_facelet 8, 1,2 - end - - def <=>(side) - self.num <=> side.num - end - - def init_facelet(pos, *side_nums) - sides = side_nums.map { |num| @sides[num] }.sort - @fl_by_side[sides] = pos - end - - def []=(color, *sides) - @facelets[@fl_by_side[sides.sort]].color = color - end - - def values_at(*sides) - sides.map { |sides| @facelets[@fl_by_side[sides.sort]] } - end - - def inspect(range=nil) - if range - @facelets.values_at(*(range.to_a)).join(' ') - else - <<-EOS.gsub(/\d/) { |num| @facelets[num.to_i] }.gsub(/[ABCD]/) { |side| @sides[side[0]-?A].num.to_s } - A - 0 1 2 - D 3 4 5 B - 6 7 8 - C - EOS - end - end - - def get_edge(side) - trio = (-1..1).map { |x| (side + x) % 4 } - prev_side, this_side, next_side = @sides.values_at(*trio) - e = Edge.new( - self .values_at( [this_side], [this_side, next_side] ) + - this_side.values_at( [self, prev_side], [self ], [self, next_side] ) - ) - #puts 'Edge created for side %d: ' % side + e.inspect - e - end - - def turn(dir) - #p 'turn side %d in %d' % [num, dir] - edges = (0..3).map { |n| get_edge n } - for i in 0..3 - edges[i].apply edges[(i-dir) % 4] - end - end -end - -class Cube - def initialize - @sides = [] - %w(left front right back top bottom).each_with_index { |side, i| - eval("@sides[#{i}] = @#{side} = Side.new(#{i})") - } - @left.sides = [@top, @front, @bottom, @back] - @front.sides = [@top, @right, @bottom, @left] - @right.sides = [@top, @back, @bottom, @front] - @back.sides = [@top, @left, @bottom, @right] - @top.sides = [@back, @right, @front, @left] - @bottom.sides = [@front, @right, @back, @left] - end - - def read_facelets(fs) - pattern = Regexp.new(<<-EOP.gsub(/\w/, '\w').gsub(/\s+/, '\s*')) - (w w w) - (w w w) - (w w w) -(r r r) (g g g) (b b b) (o o o) -(r r r) (g g g) (b b b) (o o o) -(r r r) (g g g) (b b b) (o o o) - (y y y) - (y y y) - (y y y) - EOP - md = pattern.match(fs).to_a - - @top.facelets = parse_facelets(md.values_at(1,2,3)) - @left.facelets = parse_facelets(md.values_at(4,8,12)) - @front.facelets = parse_facelets(md.values_at(5,9,13)) - @right.facelets = parse_facelets(md.values_at(6,10,14)) - @back.facelets = parse_facelets(md.values_at(7,11,15)) - @bottom.facelets = parse_facelets(md.values_at(16,17,18)) - end - - def turn(side, dir) - #p 'turn %d in %d' % [side, dir] - @sides[side].turn(dir) - #puts inspect - end - - def inspect - <<-EOF.gsub(/(\d):(\d)-(\d)/) { @sides[$1.to_i].inspect(Range.new($2.to_i, $3.to_i)) } - 4:0-2 - 4:3-5 - 4:6-8 -0:0-2 1:0-2 2:0-2 3:0-2 -0:3-5 1:3-5 2:3-5 3:3-5 -0:6-8 1:6-8 2:6-8 3:6-8 - 5:0-2 - 5:3-5 - 5:6-8 - EOF - end - -private - def parse_facelets(rows) - rows.join.delete(' ').split(//) - end -end - -#$stdin = DATA - -gets.to_i.times do |i| - puts "Scenario ##{i+1}:" - fs = '' - 9.times { fs << gets } - cube = Cube.new - cube.read_facelets fs - gets.to_i.times do |t| - side, dir = gets.split.map {|s| s.to_i} - cube.turn(side, dir) - end - puts cube.inspect - puts -end - -# 2004 by murphy <korny@cYcnus.de> -# GPL -class Scenario - class TimePoint - attr_reader :data - def initialize *data - @data = data - end - - def [] i - @data[i] or 0 - end - - include Comparable - def <=> tp - r = 0 - [@data.size, tp.data.size].max.times do |i| - r = self[i] <=> tp[i] - return r if r.nonzero? - end - 0 - end - - def - tp - r = [] - [@data.size, tp.data.size].max.times do |i| - r << self[i] - tp[i] - end - r - end - - def inspect - # 01/01/1800 00:00:00 - '%02d/%02d/%04d %02d:%02d:%02d' % @data.values_at(1, 2, 0, 3, 4, 5) - end - end - - ONE_HOUR = TimePoint.new 0, 0, 0, 1, 0, 0 - - APPOINTMENT_PATTERN = / - ( \d{4} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s - ( \d{4} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) - /x - - def initialize io - @team_size = io.gets.to_i - @data = [ [TimePoint.new(1800, 01, 01, 00, 00, 00), @team_size] ] - @team_size.times do # each team member - io.gets.to_i.times do # each appointment - m = APPOINTMENT_PATTERN.match io.gets - @data << [TimePoint.new(*m.captures[0,6].map { |x| x.to_i }), -1] - @data << [TimePoint.new(*m.captures[6,6].map { |x| x.to_i }), +1] - end - end - @data << [TimePoint.new(2200, 01, 01, 00, 00, 00), -@team_size] - end - - def print_time_plan - n = 0 - appointment = nil - no_appointment = true - @data.sort_by { |x| x[0] }.each do |x| - tp, action = *x - n += action - # at any time during the meeting, at least two team members need to be there - # and at most one team member is allowed to be absent - if n >= 2 and (@team_size - n) <= 1 - appointment ||= tp - else - if appointment - # the meeting should be at least one hour in length - if TimePoint.new(*(tp - appointment)) >= ONE_HOUR - puts 'appointment possible from %p to %p' % [appointment, tp] - no_appointment = false - end - appointment = false - end - end - end - puts 'no appointment possible' if no_appointment - end -end - -# read the data -DATA.gets.to_i.times do |si| # each scenario - puts 'Scenario #%d:' % (si + 1) - sc = Scenario.new DATA - sc.print_time_plan - puts -end - -#__END__ -2 -3 -3 -2002 06 28 15 00 00 2002 06 28 18 00 00 TUD Contest Practice Session -2002 06 29 10 00 00 2002 06 29 15 00 00 TUD Contest -2002 11 15 15 00 00 2002 11 17 23 00 00 NWERC Delft -4 -2002 06 25 13 30 00 2002 06 25 15 30 00 FIFA World Cup Semifinal I -2002 06 26 13 30 00 2002 06 26 15 30 00 FIFA World Cup Semifinal II -2002 06 29 13 00 00 2002 06 29 15 00 00 FIFA World Cup Third Place -2002 06 30 13 00 00 2002 06 30 15 00 00 FIFA World Cup Final -1 -2002 06 01 00 00 00 2002 06 29 18 00 00 Preparation of Problem Set -2 -1 -1800 01 01 00 00 00 2200 01 01 00 00 00 Solving Problem 8 -0 - -require 'token_consts' -require 'symbol' -require 'ctype' -require 'error' - -class Fixnum - # Treat char as a digit and return it's value as Fixnum. - # Returns nonsense for non-digits. - # Examples: - # <code> - # RUBY_VERSION[0].digit == '1.8.2'[0].digit == 1 - # </code> - # - # <code> - # ?6.digit == 6 - # </code> - # - # <code> - # ?A.digit == 17 - # </code> - def digit - self - ?0 - end -end - -## -# Stellt einen einfachen Scanner fr die lexikalische Analyse der Sprache Pas-0 dar. -# -# @author Andreas Kunert -# Ruby port by murphy -class Scanner - - include TokenConsts - - attr_reader :line, :pos - - # To allow Scanner.new without parameters. - DUMMY_INPUT = 'dummy file' - def DUMMY_INPUT.getc - nil - end - - ## - # Erzeugt einen Scanner, der als Eingabe das bergebene IO benutzt. - def initialize input = DUMMY_INPUT - @line = 1 - @pos = 0 - - begin - @input = input - @next_char = @input.getc - rescue IOError # TODO show the reason! - Error.ioError - raise - end - end - - ## - # Liest das n - def read_next_char - begin - @pos += 1 - @current_char = @next_char - @next_char = @input.getc - rescue IOError - Error.ioError - raise - end - end - - ## - # Sucht das n - # PascalSymbol-Objekt und gibt es zurck. - # @see Symbol - # @return das gefundene Symbol als PascalSymbol-Objekt - def get_symbol - current_symbol = nil - until current_symbol - read_next_char - - if @current_char.alpha? - identifier = @current_char.chr - while @next_char.alpha? or @next_char.digit? - identifier << @next_char - read_next_char - end - current_symbol = handle_identifier(identifier.upcase) - elsif @current_char.digit? - current_symbol = number - else - case @current_char - when ?\s - # ignore - when ?\n - new_line - when nil - current_symbol = PascalSymbol.new EOP - when ?{ - comment - - when ?: - if @next_char == ?= - read_next_char - current_symbol = PascalSymbol.new BECOMES - else - current_symbol = PascalSymbol.new COLON - end - - when ?< - if (@next_char == ?=) - read_next_char - current_symbol = PascalSymbol.new LEQSY - elsif (@next_char == ?>) - read_next_char - current_symbol = PascalSymbol.new NEQSY - else - current_symbol = PascalSymbol.new LSSSY - end - - when ?> - if (@next_char == ?=) - read_next_char - current_symbol = PascalSymbol.new GEQSY - else - current_symbol = PascalSymbol.new GRTSY - end - - when ?. then current_symbol = PascalSymbol.new PERIOD - when ?( then current_symbol = PascalSymbol.new LPARENT - when ?, then current_symbol = PascalSymbol.new COMMA - when ?* then current_symbol = PascalSymbol.new TIMES - when ?/ then current_symbol = PascalSymbol.new SLASH - when ?+ then current_symbol = PascalSymbol.new PLUS - when ?- then current_symbol = PascalSymbol.new MINUS - when ?= then current_symbol = PascalSymbol.new EQLSY - when ?) then current_symbol = PascalSymbol.new RPARENT - when ?; then current_symbol = PascalSymbol.new SEMICOLON - else - Error.error(100, @line, @pos) if @current_char > ?\s - end - end - end - current_symbol - end - -private - ## - # Versucht, in dem gegebenen String ein Schlsselwort zu erkennen. - # Sollte dabei ein Keyword gefunden werden, so gibt er ein PascalSymbol-Objekt zurck, das - # das entsprechende Keyword repr - # einem SymbolIdent-Objekt (abgeleitet von PascalSymbol), das den String 1:1 enth - # @see symbol - # @return falls Keyword gefunden, zugeh - def handle_identifier identifier - if sym = KEYWORD_SYMBOLS[identifier] - PascalSymbol.new sym - else - SymbolIdent.new identifier - end - end - - MAXINT = 2**31 - 1 - MAXINT_DIV_10 = MAXINT / 10 - MAXINT_MOD_10 = MAXINT % 10 - ## - # Versucht, aus dem gegebenen Zeichen und den folgenden eine Zahl zusammenzusetzen. - # Dabei wird der relativ intuitive Algorithmus benutzt, die endgltige Zahl bei - # jeder weiteren Ziffer mit 10 zu multiplizieren und diese dann mit der Ziffer zu - # addieren. Sonderf - # <BR> - # Treten dabei kein Punkt oder ein E auf, so gibt diese Methode ein SymbolIntCon-Objekt - # zurck, ansonsten (reelle Zahl) ein SymbolRealCon-Objekt. Beide Symbole enthalten - # jeweils die Zahlwerte. - # <BR> - # Anmerkung: Diese Funktion ist mit Hilfe der Java/Ruby-API deutlich leichter zu realisieren. - # Sie wurde dennoch so implementiert, um den Algorithmus zu demonstrieren - # @see symbol - # @return SymbolIntcon- oder SymbolRealcon-Objekt, das den Zahlwert enth - def number - is_integer = true - integer_too_long = false - exponent = 0 - exp_counter = -1 - exp_sign = 1 - - integer_mantisse = @current_char.digit - - while (@next_char.digit? and integer_mantisse < MAXINT_DIV_10) or - (integer_mantisse == MAXINT_DIV_10 and @next_char.digit <= MAXINT_MOD_10) - integer_mantisse *= 10 - integer_mantisse += @next_char.digit - read_next_char - end - - real_mantisse = integer_mantisse - - while @next_char.digit? - integer_too_long = true - real_mantisse *= 10 - real_mantisse += @next_char.digit - read_next_char - end - if @next_char == ?. - read_next_char - is_integer = false - unless @next_char.digit? - Error.error 101, @line, @pos - end - while @next_char.digit? - real_mantisse += @next_char.digit * (10 ** exp_counter) - read_next_char - exp_counter -= 1 - end - end - if @next_char == ?E - is_integer = false - read_next_char - if @next_char == ?- - exp_sign = -1 - read_next_char - end - unless @next_char.digit? - Error.error 101, @line, @pos - end - while @next_char.digit? - exponent *= 10 - exponent += @next_char.digit - read_next_char - end - end - - if is_integer - if integer_too_long - Error.error 102, @line, @pos - end - SymbolIntcon.new integer_mantisse - else - SymbolRealcon.new real_mantisse * (10 ** (exp_sign * exponent)) - end - end - - ## - # Sorgt fr ein - # Es werden einfach alle Zeichen bis zu einer schlie - # und verworfen. - def comment - while @current_char != ?} - forbid_eop - new_line if @current_char == ?\n - read_next_char - end - end - - def new_line - @line += 1 - @pos = 0 - end - - def forbid_eop - if eop? - Error.error 103, @line, @pos - end - exit - end - - def eop? - @current_char.nil? - end -end - -## -# L -# Dieses erzeugt sich ein Scanner-Objekt und ruft an diesem kontinuierlich bis zum Dateiende -# get_symbol auf. -if $0 == __FILE__ - scan = Scanner.new(File.new(ARGV[0] || 'test.pas')) - loop do - c = scan.get_symbol - puts c - break if c.typ == TokenConsts::EOP - end -end -# -*- ruby -*- - -# Local variables: -# indent-tabs-mode: nil -# ruby-indent-level: 4 -# End: - -# @@PLEAC@@_NAME -# @@SKIP@@ Ruby - -# @@PLEAC@@_WEB -# @@SKIP@@ http://www.ruby-lang.org - - -# @@PLEAC@@_1.0 -string = '\n' # two characters, \ and an n -string = 'Jon \'Maddog\' Orwant' # literal single quotes - -string = "\n" # a "newline" character -string = "Jon \"Maddog\" Orwant" # literal double quotes - -string = %q/Jon 'Maddog' Orwant/ # literal single quotes - -string = %q[Jon 'Maddog' Orwant] # literal single quotes -string = %q{Jon 'Maddog' Orwant} # literal single quotes -string = %q(Jon 'Maddog' Orwant) # literal single quotes -string = %q<Jon 'Maddog' Orwant> # literal single quotes - -a = <<"EOF" -This is a multiline here document -terminated by EOF on a line by itself -EOF - - -# @@PLEAC@@_1.1 -value = string[offset,count] -value = string[offset..-1] - -string[offset,count] = newstring -string[offset..-1] = newtail - -# in Ruby we can also specify intervals by their two offsets -value = string[offset..offs2] -string[offset..offs2] = newstring - -leading, s1, s2, trailing = data.unpack("A5 x3 A8 A8 A*") - -fivers = string.unpack("A5" * (string.length/5)) - -chars = string.unpack("A1" * string.length) - -string = "This is what you have" -# +012345678901234567890 Indexing forwards (left to right) -# 109876543210987654321- Indexing backwards (right to left) -# note that 0 means 10 or 20, etc. above - -first = string[0, 1] # "T" -start = string[5, 2] # "is" -rest = string[13..-1] # "you have" -last = string[-1, 1] # "e" -end_ = string[-4..-1] # "have" -piece = string[-8, 3] # "you" - -string[5, 2] = "wasn't" # change "is" to "wasn't" -string[-12..-1] = "ondrous" # "This wasn't wondrous" -string[0, 1] = "" # delete first character -string[-10..-1] = "" # delete last 10 characters - -if string[-10..-1] =~ /pattern/ - puts "Pattern matches in last 10 characters" -end - -string[0, 5].gsub!(/is/, 'at') - -a = "make a hat" -a[0, 1], a[-1, 1] = a[-1, 1], a[0, 1] - -a = "To be or not to be" -b = a.unpack("x6 A6") - -b, c = a.unpack("x6 A2 X5 A2") -puts "#{b}\n#{c}\n" - -def cut2fmt(*args) - template = '' - lastpos = 1 - for place in args - template += "A" + (place - lastpos).to_s + " " - lastpos = place - end - template += "A*" - return template -end - -fmt = cut2fmt(8, 14, 20, 26, 30) - - -# @@PLEAC@@_1.2 -# careful! "b is true" doesn't mean "b != 0" (0 is true in Ruby) -# thus no problem of "defined" later since only nil is false -# the following sets to `c' if `b' is nil or false -a = b || c - -# if you need Perl's behaviour (setting to `c' if `b' is 0) the most -# effective way is to use Numeric#nonzero? (thanks to Dave Thomas!) -a = b.nonzero? || c - -# you will still want to use defined? in order to test -# for scope existence of a given object -a = defined?(b) ? b : c - -dir = ARGV.shift || "/tmp" - - -# @@PLEAC@@_1.3 -v1, v2 = v2, v1 - -alpha, beta, production = %w(January March August) -alpha, beta, production = beta, production, alpha - - -# @@PLEAC@@_1.4 -num = char[0] -char = num.chr - -# Ruby also supports having a char from character constant -num = ?r - -char = sprintf("%c", num) -printf("Number %d is character %c\n", num, num) - -ascii = string.unpack("C*") -string = ascii.pack("C*") - -hal = "HAL" -ascii = hal.unpack("C*") -# We can't use Array#each since we can't mutate a Fixnum -ascii.collect! { |i| - i + 1 # add one to each ASCII value -} -ibm = ascii.pack("C*") -puts ibm - - -# @@PLEAC@@_1.5 -array = string.split('') - -array = string.unpack("C*") - -string.scan(/./) { |b| - # do something with b -} - -string = "an apple a day" -print "unique chars are: ", string.split('').uniq.sort, "\n" - -sum = 0 -for ascval in string.unpack("C*") # or use Array#each for a pure OO style :) - sum += ascval -end -puts "sum is #{sum & 0xffffffff}" # since Ruby will go Bignum if necessary - -# @@INCLUDE@@ include/ruby/slowcat.rb - - -# @@PLEAC@@_1.6 -revbytes = string.reverse - -revwords = string.split(" ").reverse.join(" ") - -revwords = string.split(/(\s+)/).reverse.join - -# using the fact that IO is Enumerable, you can directly "select" it -long_palindromes = File.open("/usr/share/dict/words"). - select { |w| w.chomp!; w.reverse == w && w.length > 5 } - - -# @@PLEAC@@_1.7 -while string.sub!("\t+") { ' ' * ($&.length * 8 - $`.length % 8) } -end - - -# @@PLEAC@@_1.8 -'You owe #{debt} to me'.gsub(/\#{(\w+)}/) { eval($1) } - -rows, cols = 24, 80 -text = %q(I am #{rows} high and #{cols} long) -text.gsub!(/\#{(\w+)}/) { eval("#{$1}") } -puts text - -'I am 17 years old'.gsub(/\d+/) { 2 * $&.to_i } - - -# @@PLEAC@@_1.9 -e = "bo peep".upcase -e.downcase! -e.capitalize! - -"thIS is a loNG liNE".gsub!(/\w+/) { $&.capitalize } - - -# @@PLEAC@@_1.10 -"I have #{n+1} guanacos." -print "I have ", n+1, " guanacos." - - -# @@PLEAC@@_1.11 -var = <<'EOF'.gsub(/^\s+/, '') - your text - goes here -EOF - - -# @@PLEAC@@_1.12 -string = "Folding and splicing is the work of an editor,\n"+ - "not a mere collection of silicon\n"+ - "and\n"+ - "mobile electrons!" - -def wrap(str, max_size) - all = [] - line = '' - for l in str.split - if (line+l).length >= max_size - all.push(line) - line = '' - end - line += line == '' ? l : ' ' + l - end - all.push(line).join("\n") -end - -print wrap(string, 20) -#=> Folding and -#=> splicing is the -#=> work of an editor, -#=> not a mere -#=> collection of -#=> silicon and mobile -#=> electrons! - - -# @@PLEAC@@_1.13 -string = %q(Mom said, "Don't do that.") -string.gsub(/['"]/) { '\\'+$& } -string.gsub(/['"]/, '\&\&') -string.gsub(/[^A-Z]/) { '\\'+$& } -"is a test!".gsub(/\W/) { '\\'+$& } # no function like quotemeta? - - -# @@PLEAC@@_1.14 -string.strip! - - -# @@PLEAC@@_1.15 -def parse_csv(text) - new = text.scan(/"([^\"\\]*(?:\\.[^\"\\]*)*)",?|([^,]+),?|,/) - new << nil if text[-1] == ?, - new.flatten.compact -end - -line = %q<XYZZY,"","O'Reilly, Inc","Wall, Larry","a \"glug\" bit,",5,"Error, Core Dumped"> -fields = parse_csv(line) -fields.each_with_index { |v,i| - print "#{i} : #{v}\n"; -} - - -# @@PLEAC@@_1.16 -# Use the soundex.rb Library from Michael Neumann. -# http://www.s-direktnet.de/homepages/neumann/rb_prgs/Soundex.rb -require 'Soundex' - -code = Text::Soundex.soundex(string) -codes = Text::Soundex.soundex(array) - -# substitution function for getpwent(): -# returns an array of user entries, -# each entry contains the username and the full name -def login_names - result = [] - File.open("/etc/passwd") { |file| - file.each_line { |line| - next if line.match(/^#/) - cols = line.split(":") - result.push([cols[0], cols[4]]) - } - } - result -end - -puts "Lookup user: " -user = STDIN.gets -user.chomp! -exit unless user -name_code = Text::Soundex.soundex(user) - -splitter = Regexp.new('(\w+)[^,]*\b(\w+)') -for username, fullname in login_names do - firstname, lastname = splitter.match(fullname)[1,2] - if name_code == Text::Soundex.soundex(username) - || name_code == Text::Soundex.soundex(firstname) - || name_code == Text::Soundex.soundex(lastname) - then - puts "#{username}: #{firstname} #{lastname}" - end -end - - -# @@PLEAC@@_1.17 -# @@INCLUDE@@ include/ruby/fixstyle.rb - - -# @@PLEAC@@_1.18 -# @@INCLUDE@@ include/ruby/psgrep.rb - - -# @@PLEAC@@_2.1 -# Matz tells that you can use Integer() for strict checked conversion. -Integer("abc") -#=> `Integer': invalid value for Integer: "abc" (ArgumentError) -Integer("567") -#=> 567 - -# You may use Float() for floating point stuff -Integer("56.7") -#=> `Integer': invalid value for Integer: "56.7" (ArgumentError) -Float("56.7") -#=> 56.7 - -# You may also use a regexp for that -if string =~ /^[+-]?\d+$/ - p 'is an integer' -else - p 'is not' -end - -if string =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/ - p 'is a decimal number' -else - p 'is not' -end - - -# @@PLEAC@@_2.2 -# equal(num1, num2, accuracy) : returns true if num1 and num2 are -# equal to accuracy number of decimal places -def equal(i, j, a) - sprintf("%.#{a}g", i) == sprintf("%.#{a}g", j) -end - -wage = 536 # $5.36/hour -week = 40 * wage # $214.40 -printf("One week's wage is: \$%.2f\n", week/100.0) - - -# @@PLEAC@@_2.3 -num.round # rounds to integer - -a = 0.255 -b = sprintf("%.2f", a) -print "Unrounded: #{a}\nRounded: #{b}\n" -printf "Unrounded: #{a}\nRounded: %.2f\n", a - -print "number\tint\tfloor\tceil\n" -a = [ 3.3 , 3.5 , 3.7, -3.3 ] -for n in a - printf("% .1f\t% .1f\t% .1f\t% .1f\n", # at least I don't fake my output :) - n, n.to_i, n.floor, n.ceil) -end - - -# @@PLEAC@@_2.4 -def dec2bin(n) - [n].pack("N").unpack("B32")[0].sub(/^0+(?=\d)/, '') -end - -def bin2dec(n) - [("0"*32+n.to_s)[-32..-1]].pack("B32").unpack("N")[0] -end - - -# @@PLEAC@@_2.5 -for i in x .. y - # i is set to every integer from x to y, inclusive -end - -x.step(y,7) { |i| - # i is set to every integer from x to y, stepsize = 7 -} - -print "Infancy is: " -(0..2).each { |i| - print i, " " -} -print "\n" - - -# @@PLEAC@@_2.6 -# We can add conversion methods to the Integer class, -# this makes a roman number just a representation for normal numbers. -class Integer - - @@romanlist = [["M", 1000], - ["CM", 900], - ["D", 500], - ["CD", 400], - ["C", 100], - ["XC", 90], - ["L", 50], - ["XL", 40], - ["X", 10], - ["IX", 9], - ["V", 5], - ["IV", 4], - ["I", 1]] - - def to_roman - remains = self - roman = "" - for sym, num in @@romanlist - while remains >= num - remains -= num - roman << sym - end - end - roman - end - - def Integer.from_roman(roman) - ustr = roman.upcase - sum = 0 - for entry in @@romanlist - sym, num = entry[0], entry[1] - while sym == ustr[0, sym.length] - sum += num - ustr.slice!(0, sym.length) - end - end - sum - end - -end - - -roman_fifteen = 15.to_roman -puts "Roman for fifteen is #{roman_fifteen}" -i = Integer.from_roman(roman_fifteen) -puts "Converted back, #{roman_fifteen} is #{i}" - -# check -for i in (1..3900) - r = i.to_roman - j = Integer.from_roman(r) - if i != j - puts "error: #{i} : #{r} - #{j}" - end -end - - -# @@PLEAC@@_2.7 -random = rand(y-x+1)+x - -chars = ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!@$%^&*) -password = (1..8).collect { chars[rand(chars.size)] }.pack("C*") - - -# @@PLEAC@@_2.8 -srand # uses a combination of the time, the process id, and a sequence number -srand(val) # for repeatable behaviour - - -# @@PLEAC@@_2.9 -# from the randomr lib: -# http://raa.ruby-lang.org/project/randomr/ -----> http://raa.ruby-lang.org/project/randomr/ - -require 'random/mersenne_twister' -mers = Random::MersenneTwister.new 123456789 -puts mers.rand(0) # 0.550321932544541 -puts mers.rand(10) # 2 - -# using online sources of random data via the realrand package: -# http://raa.ruby-lang.org/project/realrand/ -# **Note** -# The following online services are used in this package: -# http://www.random.org - source: atmospheric noise -# http://www.fourmilab.ch/hotbits - source: radioactive decay timings -# http://random.hd.org - source: entropy from local and network noise -# Please visit the sites and respect the rules of each service. - -require 'random/online' - -generator1 = Random::RandomOrg.new -puts generator1.randbyte(5).join(",") -puts generator1.randnum(10, 1, 6).join(",") # Roll dice 10 times. - -generator2 = Random::FourmiLab.new -puts generator2.randbyte(5).join(",") -# randnum is not supported. - -generator3 = Random::EntropyPool.new -puts generator3.randbyte(5).join(",") -# randnum is not supported. - - -# @@PLEAC@@_2.10 -def gaussian_rand - begin - u1 = 2 * rand() - 1 - u2 = 2 * rand() - 1 - w = u1*u1 + u2*u2 - end while (w >= 1) - w = Math.sqrt((-2*Math.log(w))/w) - [ u2*w, u1*w ] -end - -mean = 25 -sdev = 2 -salary = gaussian_rand[0] * sdev + mean -printf("You have been hired at \$%.2f\n", salary) - - -# @@PLEAC@@_2.11 -def deg2rad(d) - (d/180.0)*Math::PI -end - -def rad2deg(r) - (r/Math::PI)*180 -end - - -# @@PLEAC@@_2.12 -sin_val = Math.sin(angle) -cos_val = Math.cos(angle) -tan_val = Math.tan(angle) - -# AFAIK Ruby's Math module doesn't provide acos/asin -# While we're at it, let's also define missing hyperbolic functions -module Math - def Math.asin(x) - atan2(x, sqrt(1 - x**2)) - end - def Math.acos(x) - atan2(sqrt(1 - x**2), x) - end - def Math.atan(x) - atan2(x, 1) - end - def Math.sinh(x) - (exp(x) - exp(-x)) / 2 - end - def Math.cosh(x) - (exp(x) + exp(-x)) / 2 - end - def Math.tanh(x) - sinh(x) / cosh(x) - end -end - -# The support for Complex numbers is not built-in -y = Math.acos(3.7) -#=> in `sqrt': square root for negative number (ArgumentError) - -# There is an implementation of Complex numbers in 'complex.rb' in current -# Ruby distro, but it doesn't support atan2 with complex args, so it doesn't -# solve this problem. - - -# @@PLEAC@@_2.13 -log_e = Math.log(val) -log_10 = Math.log10(val) - -def log_base(base, val) - Math.log(val)/Math.log(base) -end - -answer = log_base(10, 10_000) -puts "log10(10,000) = #{answer}" - - -# @@PLEAC@@_2.14 -require 'matrix.rb' - -a = Matrix[[3, 2, 3], [5, 9, 8]] -b = Matrix[[4, 7], [9, 3], [8, 1]] -c = a * b - -a.row_size -a.column_size - -c.det -a.transpose - - -# @@PLEAC@@_2.15 -require 'complex.rb' -require 'rational.rb' - -a = Complex(3, 5) # 3 + 5i -b = Complex(2, -2) # 2 - 2i -puts "c = #{a*b}" - -c = a * b -d = 3 + 4*Complex::I - -printf "sqrt(#{d}) = %s\n", Math.sqrt(d) - - -# @@PLEAC@@_2.16 -number = hexadecimal.hex -number = octal.oct - -print "Gimme a number in decimal, octal, or hex: " -num = gets.chomp -exit unless defined?(num) -num = num.oct if num =~ /^0/ # does both oct and hex -printf "%d %x %o\n", num, num, num - -print "Enter file permission in octal: " -permissions = gets.chomp -raise "Exiting ...\n" unless defined?(permissions) -puts "The decimal value is #{permissions.oct}" - - -# @@PLEAC@@_2.17 -def commify(n) - n.to_s =~ /([^\.]*)(\..*)?/ - int, dec = $1.reverse, $2 ? $2 : "" - while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3') - end - int.reverse + dec -end - - -# @@PLEAC@@_2.18 -printf "It took %d hour%s\n", time, time == 1 ? "" : "s" - -# dunno if an equivalent to Lingua::EN::Inflect exists... - - -# @@PLEAC@@_2.19 -#----------------------------- -#!/usr/bin/ruby -# bigfact - calculating prime factors -def factorize(orig) - factors = {} - factors.default = 0 # return 0 instead nil if key not found in hash - n = orig - i = 2 - sqi = 4 # square of i - while sqi <= n do - while n.modulo(i) == 0 do - n /= i - factors[i] += 1 - # puts "Found factor #{i}" - end - # we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1 - sqi += 2 * i + 1 - i += 1 - end - - if (n != 1) && (n != orig) - factors[n] += 1 - end - factors -end - -def printfactorhash(orig, factorcount) - print format("%-10d ", orig) - if factorcount.length == 0 - print "PRIME" - else - # sorts after number, because the hash keys are numbers - factorcount.sort.each { |factor,exponent| - print factor - if exponent > 1 - print "**", exponent - end - print " " - } - end - puts -end - -for arg in ARGV - n = arg.to_i - mfactors = factorize(n) - printfactorhash(n, mfactors) -end -#----------------------------- - - -# @@PLEAC@@_3.0 -puts Time.now - -print "Today is day ", Time.now.yday, " of the current year.\n" -print "Today is day ", Time.now.day, " of the current month.\n" - - -# @@PLEAC@@_3.1 -day, month, year = Time.now.day, Time.now.month, Time.now.year -# or -day, month, year = Time.now.to_a[3..5] - -tl = Time.now.localtime -printf("The current date is %04d %02d %02d\n", tl.year, tl.month, tl.day) - -Time.now.localtime.strftime("%Y-%m-%d") - - -# @@PLEAC@@_3.2 -Time.local(year, month, day, hour, minute, second).tv_sec -Time.gm(year, month, day, hour, minute, second).tv_sec - - -# @@PLEAC@@_3.3 -sec, min, hour, day, month, year, wday, yday, isdst, zone = Time.at(epoch_secs).to_a - - -# @@PLEAC@@_3.4 -when_ = now + difference # now -> Time ; difference -> Numeric (delta in seconds) -then_ = now - difference - - -# @@PLEAC@@_3.5 -bree = 361535725 -nat = 96201950 - -difference = bree - nat -puts "There were #{difference} seconds between Nat and Bree" - -seconds = difference % 60 -difference = (difference - seconds) / 60 -minutes = difference % 60 -difference = (difference - minutes) / 60 -hours = difference % 24 -difference = (difference - hours) / 24 -days = difference % 7 -weeks = (difference - days) / 7 - -puts "(#{weeks} weeks, #{days} days, #{hours}:#{minutes}:#{seconds})" - - -# @@PLEAC@@_3.6 -monthday, weekday, yearday = date.mday, date.wday, date.yday - -# AFAIK the week number is not just a division since week boundaries are on sundays -weeknum = d.strftime("%U").to_i + 1 - -year = 1981 -month = "jun" # or `6' if you want to emulate a broken language -day = 16 -t = Time.mktime(year, month, day) -print "#{month}/#{day}/#{year} was a ", t.strftime("%A"), "\n" - - -# @@PLEAC@@_3.7 -yyyy, mm, dd = $1, $2, $3 if "1998-06-25" =~ /(\d+)-(\d+)-(\d+)/ - -epoch_seconds = Time.mktime(yyyy, mm, dd).tv_sec - -# dunno an equivalent to Date::Manip#ParseDate - - -# @@PLEAC@@_3.8 -string = Time.at(epoch_secs) -Time.at(1234567890).gmtime # gives: Fri Feb 13 23:31:30 UTC 2009 - -time = Time.mktime(1973, "jan", 18, 3, 45, 50) -print "In localtime it gives: ", time.localtime, "\n" - - -# @@PLEAC@@_3.9 -# Ruby provides micro-seconds in Time object -Time.now.usec - -# Ruby gives the seconds in floating format when substracting two Time objects -before = Time.now -line = gets -elapsed = Time.now - before -puts "You took #{elapsed} seconds." - -# On my Celeron-400 with Linux-2.2.19-14mdk, average for three execs are: -# This Ruby version: average 0.00321 sec -# Cookbook's Perl version: average 0.00981 sec -size = 500 -number_of_times = 100 -total_time = 0 -number_of_times.times { - # populate array - array = [] - size.times { array << rand } - # sort it - begin_ = Time.now - array.sort! - time = Time.now - begin_ - total_time += time -} -printf "On average, sorting %d random numbers takes %.5f seconds\n", - size, (total_time/Float(number_of_times)) - - -# @@PLEAC@@_3.10 -sleep(0.005) # Ruby is definitely not as broken as Perl :) -# (may be interrupted by sending the process a SIGALRM) - - -# @@PLEAC@@_3.11 -#!/usr/bin/ruby -w -# hopdelta - feed mail header, produce lines -# showing delay at each hop. -require 'time' -class MailHopDelta - - def initialize(mail) - @head = mail.gsub(/\n\s+/,' ') - @topline = %w-Sender Recipient Time Delta- - @start_from = mail.match(/^From.*\@([^\s>]*)/)[1] - @date = Time.parse(mail.match(/^Date:\s+(.*)/)[1]) - end - - def out(line) - "%-20.20s %-20.20s %-20.20s %s" % line - end - - def hop_date(day) - day.strftime("%I:%M:%S %Y/%m/%d") - end - - def puts_hops - puts out(@topline) - puts out(['Start', @start_from, hop_date(@date),'']) - @head.split(/\n/).reverse.grep(/^Received:/).each do |hop| - hop.gsub!(/\bon (.*?) (id.*)/,'; \1') - whence = hop.match(/;\s+(.*)$/)[1] - unless whence - warn "Bad received line: #{hop}" - next - end - from = $+ if hop =~ /from\s+(\S+)|\((.*?)\)/ - by = $1 if hop =~ /by\s+(\S+\.\S+)/ - next unless now = Time.parse(whence).localtime - delta = now - @date - puts out([from, by, hop_date(now), hop_time(delta)]) - @date = now - end - end - - def hop_time(secs) - sign = secs < 0 ? -1 : 1 - days, secs = secs.abs.divmod(60 * 60 * 24) - hours,secs = secs.abs.divmod(60 * 60) - mins, secs = secs.abs.divmod(60) - rtn = "%3ds" % [secs * sign] - rtn << "%3dm" % [mins * sign] if mins != 0 - rtn << "%3dh" % [hours * sign] if hours != 0 - rtn << "%3dd" % [days * sign] if days != 0 - rtn - end -end - -$/ = "" -mail = MailHopDelta.new(ARGF.gets).puts_hops - - -# @@PLEAC@@_4.0 -single_level = [ "this", "that", "the", "other" ] - -# Ruby directly supports nested arrays -double_level = [ "this", "that", [ "the", "other" ] ] -still_single_level = [ "this", "that", [ "the", "other" ] ].flatten - - -# @@PLEAC@@_4.1 -a = [ "quick", "brown", "fox" ] -a = %w(Why are you teasing me?) - -lines = <<"END_OF_HERE_DOC".gsub(/^\s*(.+)/, '\1') - The boy stood on the burning deck, - It was as hot as glass. -END_OF_HERE_DOC - -bigarray = IO.readlines("mydatafile").collect { |l| l.chomp } - -name = "Gandalf" -banner = %Q(Speak, #{name}, and welcome!) - -host_info = `host #{his_host}` - -%x(ps #{$$}) - -banner = 'Costs only $4.95'.split(' ') - -rax = %w! ( ) < > { } [ ] ! - - -# @@PLEAC@@_4.2 -def commify_series(a) - a.size == 0 ? '' : - a.size == 1 ? a[0] : - a.size == 2 ? a.join(' and ') : - a[0..-2].join(', ') + ', and ' + a[-1] -end - -array = [ "red", "yellow", "green" ] - -print "I have ", array, " marbles\n" -# -> I have redyellowgreen marbles - -# But unlike Perl: -print "I have #{array} marbles\n" -# -> I have redyellowgreen marbles -# So, needs: -print "I have #{array.join(' ')} marbles\n" -# -> I have red yellow green marbles - -def commify_series(a) - sepchar = a.select { |p| p =~ /,/ } != [] ? '; ' : ', ' - a.size == 0 ? '' : - a.size == 1 ? a[0] : - a.size == 2 ? a.join(' and ') : - a[0..-2].join(sepchar) + sepchar + 'and ' + a[-1] -end - - -# @@PLEAC@@_4.3 -# (note: AFAIK Ruby doesn't allow gory change of Array length) -# grow the array by assigning nil to past the end of array -ary[new_size-1] = nil -# shrink the array by slicing it down -ary.slice!(new_size..-1) -# init the array with given size -Array.new(number_of_elems) -# assign to an element past the original end enlarges the array -ary[index_new_last_elem] = value - -def what_about_that_array(a) - print "The array now has ", a.size, " elements.\n" - # Index of last element is not really interesting in Ruby - print "Element #3 is `#{a[3]}'.\n" -end -people = %w(Crosby Stills Nash Young) -what_about_that_array(people) - - -# @@PLEAC@@_4.4 -# OO style -bad_users.each { |user| - complain(user) -} -# or, functional style -for user in bad_users - complain(user) -end - -for var in ENV.keys.sort - puts "#{var}=#{ENV[var]}" -end - -for user in all_users - disk_space = get_usage(user) - if (disk_space > MAX_QUOTA) - complain(user) - end -end - -for l in IO.popen("who").readlines - print l if l =~ /^gc/ -end - -# we can mimic the obfuscated Perl way -while fh.gets # $_ is set to the line just read - chomp # $_ has a trailing \n removed, if it had one - split.each { |w| # $_ is split on whitespace - # but $_ is not set to each chunk as in Perl - print w.reverse - } -end -# ...or use a cleaner way -for l in fh.readlines - l.chomp.split.each { |w| print w.reverse } -end - -# same drawback as in problem 1.4, we can't mutate a Numeric... -array.collect! { |v| v - 1 } - -a = [ .5, 3 ]; b = [ 0, 1 ] -for ary in [ a, b ] - ary.collect! { |v| v * 7 } -end -puts "#{a.join(' ')} #{b.join(' ')}" - -# we can mutate Strings, cool; we need a trick for the scalar -for ary in [ [ scalar ], array, hash.values ] - ary.each { |v| v.strip! } # String#strip rules :) -end - - -# @@PLEAC@@_4.5 -# not relevant in Ruby since we have always references -for item in array - # do somethingh with item -end - - -# @@PLEAC@@_4.6 -unique = list.uniq - -# generate a list of users logged in, removing duplicates -users = `who`.collect { |l| l =~ /(\w+)/; $1 }.sort.uniq -puts("users logged in: #{commify_series(users)}") # see 4.2 for commify_series - - -# @@PLEAC@@_4.7 -a - b -# [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ] - [ 1, 2, 4 ] -> [3, 5] - - -# @@PLEAC@@_4.8 -union = a | b -intersection = a & b -difference = a - b - - -# @@PLEAC@@_4.9 -array1.concat(array2) -# if you will assign to another object, better use: -new_ary = array1 + array2 - -members = [ "Time", "Flies" ] -initiates = [ "An", "Arrow" ] -members += initiates - -members = [ "Time", "Flies" ] -initiates = [ "An", "Arrow" ] -members[2,0] = [ "Like", initiates ].flatten - -members[0] = "Fruit" -members[3,2] = "A", "Banana" - - -# @@PLEAC@@_4.10 -reversed = ary.reverse - -ary.reverse_each { |e| - # do something with e -} - -descending = ary.sort.reverse -descending = ary.sort { |a,b| b <=> a } - - -# @@PLEAC@@_4.11 -# remove n elements from front of ary (shift n) -front = ary.slice!(0, n) - -# remove n elements from the end of ary (pop n) -end_ = ary.slice!(-n .. -1) - -# let's extend the Array class, to make that useful -class Array - def shift2() - slice!(0 .. 1) # more symetric with pop2... - end - def pop2() - slice!(-2 .. -1) - end -end - -friends = %w(Peter Paul Mary Jim Tim) -this, that = friends.shift2 - -beverages = %w(Dew Jolt Cola Sprite Fresca) -pair = beverages.pop2 - - -# @@PLEAC@@_4.12 -# use Enumerable#detect (or the synonym Enumerable#find) -highest_eng = employees.detect { |emp| emp.category == 'engineer' } - - -# @@PLEAC@@_4.13 -# use Enumerable#select (or the synonym Enumerable#find_all) -bigs = nums.select { |i| i > 1_000_000 } -pigs = users.keys.select { |k| users[k] > 1e7 } - -matching = `who`.select { |u| u =~ /^gnat / } - -engineers = employees.select { |e| e.position == 'Engineer' } - -secondary_assistance = applicants.select { |a| - a.income >= 26_000 && a.income < 30_000 -} - - -# @@PLEAC@@_4.14 -# normally you would have an array of Numeric (Float or -# Fixnum or Bignum), so you would use: -sorted = unsorted.sort -# if you have strings representing Integers or Floats -# you may specify another sort method: -sorted = unsorted.sort { |a,b| a.to_f <=> b.to_f } - -# let's use the list of my own PID's -`ps ux`.split("\n")[1..-1]. - select { |i| i =~ /^#{ENV['USER']}/ }. - collect { |i| i.split[1] }. - sort { |a,b| a.to_i <=> b.to_i }.each { |i| puts i } -puts "Select a process ID to kill:" -pid = gets.chomp -raise "Exiting ... \n" unless pid && pid =~ /^\d+$/ -Process.kill('TERM', pid.to_i) -sleep 2 -Process.kill('KILL', pid.to_i) - -descending = unsorted.sort { |a,b| b.to_f <=> a.to_f } - - -# @@PLEAC@@_4.15 -ordered = unordered.sort { |a,b| compare(a,b) } - -precomputed = unordered.collect { |e| [compute, e] } -ordered_precomputed = precomputed.sort { |a,b| a[0] <=> b[0] } -ordered = ordered_precomputed.collect { |e| e[1] } - -ordered = unordered.collect { |e| [compute, e] }. - sort { |a,b| a[0] <=> b[0] }. - collect { |e| e[1] } - -for employee in employees.sort { |a,b| a.name <=> b.name } - print employee.name, " earns \$ ", employee.salary, "\n" -end - -# Beware! `0' is true in Ruby. -# For chaining comparisons, you may use Numeric#nonzero?, which -# returns num if num is not zero, nil otherwise -sorted = employees.sort { |a,b| (a.name <=> b.name).nonzero? || b.age <=> a.age } - -users = [] -# getpwent is not wrapped in Ruby... let's fallback -IO.readlines('/etc/passwd').each { |u| users << u.split(':') } -users.sort! { |a,b| a[0] <=> b[0] } -for user in users - puts user[0] -end - -sorted = names.sort { |a,b| a[1, 1] <=> b[1, 1] } -sorted = strings.sort { |a,b| a.length <=> b.length } - -# let's show only the compact version -ordered = strings.collect { |e| [e.length, e] }. - sort { |a,b| a[0] <=> b[0] }. - collect { |e| e[1] } - -ordered = strings.collect { |e| [/\d+/.match(e)[0].to_i, e] }. - sort { |a,b| a[0] <=> b[0] }. - collect { |e| e[1] } - -print `cat /etc/passwd`.collect { |e| [e, e.split(':').indexes(3,2,0)].flatten }. - sort { |a,b| (a[1] <=> b[1]).nonzero? || (a[2] <=> b[2]).nonzero? || a[3] <=> b[3] }. - collect { |e| e[0] } - - -# @@PLEAC@@_4.16 -circular.unshift(circular.pop) # the last shall be first -circular.push(circular.shift) # and vice versa - -def grab_and_rotate(l) - l.push(ret = l.shift) - ret -end - -processes = [1, 2, 3, 4, 5] -while (1) - process = grab_and_rotate(processes) - puts "Handling process #{process}" - sleep 1 -end - - -# @@PLEAC@@_4.17 -def fisher_yates_shuffle(a) - (a.size-1).downto(1) { |i| - j = rand(i+1) - a[i], a[j] = a[j], a[i] if i != j - } -end - -def naive_shuffle(a) - for i in 0...a.size - j = rand(a.size) - a[i], a[j] = a[j], a[i] - end -end - - -# @@PLEAC@@_4.18 -#!/usr/bin/env ruby -# example 4-2 words -# words - gather lines, present in colums - -# class to encapsulate the word formatting from the input -class WordFormatter - def initialize(cols) - @cols = cols - end - - # helper to return the length of the longest word in the wordlist - def maxlen(wordlist) - max = 1 - for word in wordlist - if word.length > max - max = word.length - end - end - max - end - - # process the wordlist and print it formmated into columns - def output(wordlist) - collen = maxlen(wordlist) + 1 - columns = @cols / collen - columns = 1 if columns == 0 - rows = (wordlist.length + columns - 1) / columns - # now process each item, picking out proper piece for this position - 0.upto(rows * columns - 1) { |item| - target = (item % columns) * rows + (item / columns) - eol = ((item+1) % columns == 0) - piece = wordlist[target] || "" - piece = piece.ljust(collen) unless eol - print piece - puts if eol - } - # no need to finish it up, because eol is always true for the last element - end -end - -# get nr of chars that fit in window or console, see PLEAC 15.4 -# not portable -- linux only (?) -def getWinCharWidth() - buf = "\0" * 8 - $stdout.ioctl(0x5413, buf) - ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("$4") - ws_col || 80 -rescue - 80 -end - -# main program -cols = getWinCharWidth() -formatter = WordFormatter.new(cols) -words = readlines() -words.collect! { |line| - line.chomp -} -formatter.output(words) - - -# @@PLEAC@@_4.19 -# In ruby, Fixnum's are automatically converted to Bignum's when -# needed, so there is no need for an extra module -def factorial(n) - s = 1 - while n > 0 - s *= n - n -= 1 - end - s -end - -puts factorial(500) - -#--------------------------------------------------------- -# Example 4-3. tsc-permute -# tsc_permute: permute each word of input -def permute(items, perms) - unless items.length > 0 - puts perms.join(" ") - else - for i in items - newitems = items.dup - newperms = perms.dup - newperms.unshift(newitems.delete(i)) - permute(newitems, newperms) - end - end -end -# In ruby the main program must be after all definitions it is using -permute(ARGV, []) - -#--------------------------------------------------------- -# mjd_permute: permute each word of input - -def factorial(n) - s = 1 - while n > 0 - s *= n - n -= 1 - end - s -end - -# we use a class with a class variable store the private cache -# for the results of the factorial function. -class Factorial - @@fact = [ 1 ] - def Factorial.compute(n) - if @@fact[n] - @@fact[n] - else - @@fact[n] = n * Factorial.compute(n - 1) - end - end -end - -#--------------------------------------------------------- -# Example 4-4- mjd-permute -# n2pat(n, len): produce the N-th pattern of length len - -# We must use a lower case letter as parameter N, otherwise it is -# handled as constant Length is the length of the resulting -# array, not the index of the last element (length -1) like in -# the perl example. -def n2pat(n, length) - pat = [] - i = 1 - while i <= length - pat.push(n % i) - n /= i - i += 1 - end - pat -end - -# pat2perm(pat): turn pattern returned by n2pat() into -# permutation of integers. -def pat2perm(pat) - source = (0 .. pat.length - 1).to_a - perm = [] - perm.push(source.slice!(pat.pop)) while pat.length > 0 - perm -end - -def n2perm(n, len) - pat2perm(n2pat(n,len)) -end - -# In ruby the main program must be after all definitions -while gets - data = split - # the perl solution has used $#data, which is length-1 - num_permutations = Factorial.compute(data.length()) - 0.upto(num_permutations - 1) do |i| - # in ruby we can not use an array as selector for an array - # but by exchanging the two arrays, we can use the collect method - # which returns an array with the result of all block invocations - permutation = n2perm(i, data.length).collect { - |j| data[j] - } - puts permutation.join(" ") - end -end - - -# @@PLEAC@@_5.0 -age = { "Nat", 24, - "Jules", 25, - "Josh", 17 } - -age["Nat"] = 24 -age["Jules"] = 25 -age["Josh"] = 17 - -food_color = { - "Apple" => "red", - "Banana" => "yellow", - "Lemon" => "yellow", - "Carrot" => "orange" - } - -# In Ruby, you cannot avoid the double or simple quoting -# while manipulatin hashes - - -# @@PLEAC@@_5.1 -hash[key] = value - -food_color["Raspberry"] = "pink" -puts "Known foods:", food_color.keys - - -# @@PLEAC@@_5.2 -# does hash have a value for key ? -if (hash.has_key?(key)) - # it exists -else - # it doesn't -end - -[ "Banana", "Martini" ].each { |name| - print name, " is a ", food_color.has_key?(name) ? "food" : "drink", "\n" -} - -age = {} -age['Toddler'] = 3 -age['Unborn'] = 0 -age['Phantasm'] = nil - -for thing in ['Toddler', 'Unborn', 'Phantasm', 'Relic'] - print "#{thing}: " - print "Has-key " if age.has_key?(thing) - print "True " if age[thing] - print "Nonzero " if age[thing] && age[thing].nonzero? - print "\n" -end - -#=> -# Toddler: Has-key True Nonzero -# Unborn: Has-key True -# Phantasm: Has-key -# Relic: - -# You use Hash#has_key? when you use Perl's exists -> it checks -# for existence of a key in a hash. -# All Numeric are "True" in ruby, so the test doesn't have the -# same semantics as in Perl; you would use Numeric#nonzero? to -# achieve the same semantics (false if 0, true otherwise). - - -# @@PLEAC@@_5.3 -food_color.delete("Banana") - - -# @@PLEAC@@_5.4 -hash.each { |key, value| - # do something with key and value -} - -hash.each_key { |key| - # do something with key -} - -food_color.each { |food, color| - puts "#{food} is #{color}" -} - -food_color.each_key { |food| - puts "#{food} is #{food_color[food]}" -} - -# IMO this demonstrates that OO style is by far more readable -food_color.keys.sort.each { |food| - puts "#{food} is #{food_color[food]}." -} - -#----------------------------- -#!/usr/bin/ruby -# countfrom - count number of messages from each sender - -# Default value is 0 -from = Hash.new(0) -while gets - /^From: (.*)/ and from[$1] += 1 -end - -# More useful to sort by number of received mail by person -from.sort {|a,b| b[1]<=>a[1]}.each { |v| - puts "#{v[1]}: #{v[0]}" -} -#----------------------------- - - -# @@PLEAC@@_5.5 -# You may use the built-in 'inspect' method this way: -p hash - -# Or do it the Cookbook way: -hash.each { |k,v| puts "#{k} => #{v}" } - -# Sorted by keys -hash.sort.each { |e| puts "#{e[0]} => #{e[1]}" } -# Sorted by values -hash.sort{|a,b| a[1]<=>b[1]}.each { |e| puts "#{e[0]} => #{e[1]}" } - - -# @@PLEAC@@_5.7 -ttys = Hash.new -for i in `who` - user, tty = i.split - (ttys[user] ||= []) << tty # see problems_ruby for more infos -end -ttys.keys.sort.each { |k| - puts "#{k}: #{commify_series(ttys[k])}" # from 4.2 -} - - -# @@PLEAC@@_5.8 -surname = { "Mickey" => "Mantle", "Babe" => "Ruth" } -puts surname.index("Mantle") - -# If you really needed to 'invert' the whole hash, use Hash#invert - -#----------------------------- -#!/usr/bin/ruby -w -# foodfind - find match for food or color - -given = ARGV.shift or raise "usage: foodfind food_or_color" - -color = { - "Apple" => "red", - "Banana" => "yellow", - "Lemon" => "yellow", - "Carrot" => "orange", -} - -if (color.has_key?(given)) - puts "#{given} is a food with color #{color[given]}." -end -if (color.has_value?(given)) - puts "#{color.index(given)} is a food with color #{given}." -end -#----------------------------- - - -# @@PLEAC@@_5.9 -# Sorted by keys (Hash#sort gives an Array of pairs made of each key,value) -food_color.sort.each { |f| - puts "#{f[0]} is #{f[1]}." -} - -# Sorted by values -food_color.sort { |a,b| a[1] <=> b[1] }.each { |f| - puts "#{f[0]} is #{f[1]}." -} - -# Sorted by length of values -food_color.sort { |a,b| a[1].length <=> b[1].length }.each { |f| - puts "#{f[0]} is #{f[1]}." -} - - -# @@PLEAC@@_5.10 -merged = a.clone.update(b) # because Hash#update changes object in place - -drink_color = { "Galliano" => "yellow", "Mai Tai" => "blue" } -ingested_color = drink_color.clone.update(food_color) - -substance_color = {} -for i in [ food_color, drink_color ] - i.each_key { |k| - if substance_color.has_key?(k) - puts "Warning: #{k} seen twice. Using the first definition." - next - end - substance_color[k] = 1 - } -end - - -# @@PLEAC@@_5.11 -common = hash1.keys & hash2.keys - -this_not_that = hash1.keys - hash2.keys - - -# @@PLEAC@@_5.12 -# no problem here, Ruby handles any kind of object for key-ing -# (it takes Object#hash, which defaults to Object#id) - - -# @@PLEAC@@_5.13 -# AFAIK, not possible in Ruby - - -# @@PLEAC@@_5.14 -# Be careful, the following is possible only because Fixnum objects are -# special (documentation says: there is effectively only one Fixnum object -# instance for any given integer value). -count = Hash.new(0) -array.each { |e| - count[e] += 1 -} - - -# @@PLEAC@@_5.15 -father = { - "Cain" , "Adam", - "Abel" , "Adam", - "Seth" , "Adam", - "Enoch" , "Cain", - "Irad" , "Enoch", - "Mehujael" , "Irad", - "Methusael" , "Mehujael", - "Lamech" , "Methusael", - "Jabal" , "Lamech", - "Jubal" , "Lamech", - "Tubalcain" , "Lamech", - "Enos" , "Seth", -} - -while gets - chomp - begin - print $_, " " - end while $_ = father[$_] - puts -end - -children = {} -father.each { |k,v| - (children[v] ||= []) << k -} -while gets - chomp - puts "#{$_} begat #{(children[$_] || ['Nobody']).join(', ')}.\n" -end - -includes = {} -files.each { |f| - begin - for l in IO.readlines(f) - next unless l =~ /^\s*#\s*include\s*<([^>]+)>/ - (includes[$1] ||= []) << f - end - rescue SystemCallError - $stderr.puts "#$! (skipping)" - end -} - -include_free = includes.values.flatten.uniq - includes.keys - - -# @@PLEAC@@_5.16 -# dutree - print sorted intented rendition of du output -#% dutree -#% dutree /usr -#% dutree -a -#% dutree -a /bin - -# The DuNode class collects all information about a directory, -# and provides some convenience methods -class DuNode - - attr_reader :name - attr_accessor :size - attr_accessor :kids - - def initialize(name) - @name = name - @kids = [] - @size = 0 - end - - # support for sorting nodes with side - def size_compare(node2) - @size <=> node2.size - end - - def basename - @name.sub(/.*\//, "") - end - - #returns substring before last "/", nil if not there - def parent - p = @name.sub(/\/[^\/]+$/,"") - if p == @name - nil - else - p - end - end - -end - -# The DuTree does the acdtual work of -# getting the input, parsing it, builging up a tree -# and format it for output -class Dutree - - attr_reader :topdir - - def initialize - @nodes = Hash.new - @dirsizes = Hash.new(0) - @kids = Hash.new([]) - end - - # get a node by name, create it if it does not exist yet - def get_create_node(name) - if @nodes.has_key?(name) - @nodes[name] - else - node = DuNode.new(name) - @nodes[name] = node - node - end - end - - # run du, read in input, save sizes and kids - # stores last directory read in instance variable topdir - def input(arguments) - name = "" - cmd = "du " + arguments.join(" ") - IO.popen(cmd) { |pipe| - pipe.each { |line| - size, name = line.chomp.split(/\s+/, 2) - node = get_create_node(name) - node.size = size.to_i - @nodes[name] = node - parent = node.parent - if parent - get_create_node(parent).kids.push(node) - end - } - } - @topdir = @nodes[name] - end - - # figure out how much is taken in each directory - # that isn't stored in the subdirectories. Add a new - # fake kid called "." containing that much. - def get_dots(node) - cursize = node.size - for kid in node.kids - cursize -= kid.size - get_dots(kid) - end - if node.size != cursize - newnode = get_create_node(node.name + "/.") - newnode.size = cursize - node.kids.push(newnode) - end - end - - # recursively output everything - # passing padding and number width as well - # on recursive calls - def output(node, prefix="", width=0) - line = sprintf("%#{width}d %s", node.size, node.basename) - puts(prefix + line) - prefix += line.sub(/\d /, "| ") - prefix.gsub!(/[^|]/, " ") - if node.kids.length > 0 # not a bachelor node - kids = node.kids - kids.sort! { |a,b| - b.size_compare(a) - } - width = kids[0].size.to_s.length - for kid in kids - output(kid, prefix, width) - end - end - end - -end - -tree = Dutree.new -tree.input(ARGV) -tree.get_dots(tree.topdir) -tree.output(tree.topdir) - - -# @@PLEAC@@_6.0 -# The verbose version are match, sub, gsub, sub! and gsub!; -# pattern needs to be a Regexp object; it yields a MatchData -# object. -pattern.match(string) -string.sub(pattern, replacement) -string.gsub(pattern, replacement) -# As usual in Ruby, sub! does the same as sub but also modifies -# the object, the same for gsub!/gsub. - -# Sugared syntax yields the position of the match (or nil if no -# match). Note that the object at the right of the operator needs -# not to be a Regexp object (it can be a String). The "dont -# match" operator yields true or false. -meadow =~ /sheep/ # position of the match, nil if no match -meadow !~ /sheep/ # true if doesn't match, false if it does -# There is no sugared version for the substitution - -meadow =~ /\bovines?\b/i and print "Here be sheep!" - -string = "good food" -string.sub!(/o*/, 'e') - -# % echo ababacaca | ruby -ne 'puts $& if /(a|ba|b)+(a|ac)+/' -# ababa - -# The "global" (or "multiple") match is handled by String#scan -scan (/(\d+)/) { - puts "Found number #{$1}" -} - -# String#scan yields an Array if not used with a block -numbers = scan(/\d+/) - -digits = "123456789" -nonlap = digits.scan(/(\d\d\d)/) -yeslap = digits.scan(/(?=(\d\d\d))/) -puts "Non-overlapping: #{nonlap.join(' ')}" -puts "Overlapping: #{yeslap.join(' ')}"; -# Non-overlapping: 123 456 789 -# Overlapping: 123 234 345 456 567 678 789 - -string = "And little lambs eat ivy" -string =~ /l[^s]*s/ -puts "(#$`) (#$&) (#$')" -# (And ) (little lambs) ( eat ivy) - - -# @@PLEAC@@_6.1 -# Ruby doesn't have the same problem: -dst = src.sub('this', 'that') - -progname = $0.sub('^.*/', '') - -bindirs = %w(/usr/bin /bin /usr/local/bin) -libdirs = bindirs.map { |l| l.sub('bin', 'lib') } - - -# @@PLEAC@@_6.3 -/\S+/ # as many non-whitespace bytes as possible -/[A-Za-z'-]+/ # as many letters, apostrophes, and hyphens - -/\b([A-Za-z]+)\b/ # usually best -/\s([A-Za-z]+)\s/ # fails at ends or w/ punctuation - - -# @@PLEAC@@_6.4 -require 'socket' -str = 'www.ruby-lang.org and www.rubygarden.org' -re = / - ( # capture the hostname in $1 - (?: # these parens for grouping only - (?! [-_] ) # lookahead for neither underscore nor dash - [\w-] + # hostname component - \. # and the domain dot - ) + # now repeat that whole thing a bunch of times - [A-Za-z] # next must be a letter - [\w-] + # now trailing domain part - ) # end of $1 capture - /x # /x for nice formatting - -str.gsub! re do # pass a block to execute replacement - host = TCPsocket.gethostbyname($1) - "#{$1} [#{host[3]}]" -end - -puts str -#----------------------------- -# to match whitespace or #-characters in an extended re you need to escape -# them. - -foo = 42 -str = 'blah #foo# blah' -str.gsub! %r/ # replace - \# # a pound sign - (\w+) # the variable name - \# # another pound sign - /x do - eval $1 # with the value of a local variable - end -puts str # => blah 42 blah - - -# @@PLEAC@@_6.5 -# The 'g' modifier doesn't exist in Ruby, a regexp can't be used -# directly in a while loop; instead, use String#scan { |match| .. } -fish = 'One fish two fish red fish blue fish' -WANT = 3 -count = 0 -fish.scan(/(\w+)\s+fish\b/i) { - if (count += 1) == WANT - puts "The third fish is a #{$1} one." - end -} - -if fish =~ /(?:\w+\s+fish\s+){2}(\w+)\s+fish/i - puts "The third fish is a #{$1} one." -end - -pond = 'One fish two fish red fish blue fish' -# String#scan without a block gives an array of matches, each match -# being an array of all the specified groups -colors = pond.scan(/(\w+)\s+fish\b/i).flatten # get all matches -color = colors[2] # then the one we want -# or without a temporary array -color = pond.scan(/(\w+)\s+fish\b/i).flatten[2] # just grab element 3 -puts "The third fish in the pond is #{color}." - -count = 0 -fishes = 'One fish two fish red fish blue fish' -evens = fishes.scan(/(\w+)\s+fish\b/i).select { (count+=1) % 2 == 0 } -print "Even numbered fish are #{evens.join(' ')}." - -count = 0 -fishes.gsub(/ - \b # makes next \w more efficient - ( \w+ ) # this is what we\'ll be changing - ( - \s+ fish \b - ) - /x) { - if (count += 1) == 4 - 'sushi' + $2 - else - $1 + $2 - end -} - -pond = 'One fish two fish red fish blue fish swim here.' -puts "Last fish is #{pond.scan(/\b(\w+)\s+fish\b/i).flatten[-1]}" - -/ - A # find some pattern A - (?! # mustn\'t be able to find - .* # something - A # and A - ) - $ # through the end of the string -/x - -# The "s" perl modifier is "m" in Ruby (not very nice since there is -# also an "m" in perl..) -pond = "One fish two fish red fish blue fish swim here." -if (pond =~ / - \b ( \w+) \s+ fish \b - (?! .* \b fish \b ) - /mix) - puts "Last fish is #{$1}." -else - puts "Failed!" -end - - -# @@PLEAC@@_6.6 -#----------------------------- -#!/usr/bin/ruby -w -# killtags - very bad html killer -$/ = nil; # each read is whole file -while file = gets() do - file.gsub!(/<.*?>/m,''); # strip tags (terribly) - puts file # print file to STDOUT -end -#----------------------------- -#!/usr/bin/ruby -w -#headerfy - change certain chapter headers to html -$/ = '' -while file = gets() do - pattern = / - \A # start of record - ( # capture in $1 - Chapter # text string - \s+ # mandatory whitespace - \d+ # decimal number - \s* # optional whitespace - : # a real colon - . * # anything not a newline till end of line - ) - /x - puts file.gsub(pattern,'<H1>\1</H1>') -end -#----------------------------- -#% ruby -00pe "gsub!(/\A(Chapter\s+\d+\s*:.*)/,'<H1>\1</H1>')" datafile - -#!/usr/bin/ruby -w -#----------------------------- -for file in ARGV - file = File.open(ARGV.shift) - while file.gets('') do # each read is a paragraph - print "chunk #{$.} in $ARGV has <<#{$1}>>\n" while /^START(.*?)^END/m - end # /m activates the multiline mode -end -#----------------------------- - -# @@PLEAC@@_6.7 -#----------------------------- -$/ = nil; -file = File.open("datafile") -chunks = file.gets.split(/pattern/) -#----------------------------- -# .Ch, .Se and .Ss divide chunks of STDIN -chunks = gets(nil).split(/^\.(Ch|Se|Ss)$/) -print "I read #{chunks.size} chunks.\n" -#----------------------------- - - -# @@PLEAC@@_6.8 -while gets - if ~/BEGIN/ .. ~/END/ - # line falls between BEGIN and END inclusive - end -end - -while gets - if ($. == firstnum) .. ($. == lastnum) - # operate between firstnum and lastnum line number - end -end - -# in ruby versions prior to 1.8, the above two conditional -# expressions could be shortened to: -# if /BEGIN/ .. /END/ -# and -# if firstnum .. lastnum -# but these now only work this way from the command line - -#----------------------------- - -while gets - if ~/BEGIN/ ... ~/END/ - # line falls between BEGIN and END on different lines - end -end - -while gets - if ($. == first) ... ($. == last) - # operate between first and last line number on different lines - end -end - -#----------------------------- -# command-line to print lines 15 through 17 inclusive (see below) -ruby -ne 'print if 15 .. 17' datafile - -# print out all <XMP> .. </XMP> displays from HTML doc -while gets - print if ~%r#<XMP>#i .. ~%r#</XMP>#i; -end - -# same, but as shell command -# ruby -ne 'print if %r#<XMP>#i .. %r#</XMP>#i' document.html -#----------------------------- -# ruby -ne 'BEGIN { $top=3; $bottom=5 }; \ -# print if $top .. $bottom' /etc/passwd # FAILS -# ruby -ne 'BEGIN { $top=3; $bottom=5 }; \ -# print if $. == $top .. $. == $bottom' /etc/passwd # works -# ruby -ne 'print if 3 .. 5' /etc/passwd # also works -#----------------------------- -print if ~/begin/ .. ~/end/; -print if ~/begin/ ... ~/end/; -#----------------------------- -while gets - $in_header = $. == 1 .. ~/^$/ ? true : false - $in_body = ~/^$/ .. ARGF.eof ? true : false -end -#----------------------------- -seen = {} -ARGF.each do |line| - next unless line =~ /^From:?\s/i .. line =~ /^$/; - line.scan(%r/([^<>(),;\s]+\@[^<>(),;\s]+)/).each do |addr| - puts addr unless seen[addr] - seen[addr] ||= 1 - end -end - - -# @@PLEAC@@_6.9 -def glob2pat(globstr) - patmap = { - '*' => '.*', - '?' => '.', - '[' => '[', - ']' => ']', - } - globstr.gsub!(/(.)/) { |c| patmap[c] || Regexp::escape(c) } - '^' + globstr + '$' -end - - -# @@PLEAC@@_6.10 -# avoid interpolating patterns like this if the pattern -# isn't going to change: -pattern = ARGV.shift -ARGF.each do |line| - print line if line =~ /#{pattern}/ -end - -# the above creates a new regex each iteration. Instead, -# use the /o modifier so the regex is compiled only once - -pattern = ARGV.shift -ARGF.each do |line| - print line if line =~ /#{pattern}/o -end - -#----------------------------- - -#!/usr/bin/ruby -# popgrep1 - grep for abbreviations of places that say "pop" -# version 1: slow but obvious way -popstates = %w(CO ON MI WI MN) -ARGF.each do |line| - popstates.each do |state| - if line =~ /\b#{state}\b/ - print line - last - end - end -end - -#----------------------------- -#!/usr/bin/ruby -# popgrep2 - grep for abbreviations of places that say "pop" -# version 2: eval strings; fast but hard to quote -popstates = %w(CO ON MI WI MN) -code = "ARGF.each do |line|\n" -popstates.each do |state| - code += "\tif line =~ /\\b#{state}\\b/; print(line); next; end\n" -end -code += "end\n" -print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging -eval code - -# CODE IS -# --- -# ARGF.each do |line| -# if line =~ /\bCO\b/; print(line); next; end -# if line =~ /\bON\b/; print(line); next; end -# if line =~ /\bMI\b/; print(line); next; end -# if line =~ /\bWI\b/; print(line); next; end -# if line =~ /\bMN\b/; print(line); next; end -# end -# -# --- - -## alternatively, the same idea as above but compiling -## to a case statement: (not in perlcookbook) -#!/usr/bin/ruby -w -# popgrep2.5 - grep for abbreviations of places that say "pop" -# version 2.5: eval strings; fast but hard to quote -popstates = %w(CO ON MI WI MN) -code = "ARGF.each do |line|\n case line\n" -popstates.each do |state| - code += " when /\\b#{state}\\b/ : print line\n" -end -code += " end\nend\n" -print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging -eval code - -# CODE IS -# --- -# ARGF.each do |line| -# case line -# when /\bCO\b/ : print line -# when /\bON\b/ : print line -# when /\bMI\b/ : print line -# when /\bWI\b/ : print line -# when /\bMN\b/ : print line -# end -# end -# -# --- - -# Note: (above) Ruby 1.8+ allows the 'when EXP : EXPR' on one line -# with the colon separator. - -#----------------------------- -#!/usr/bin/ruby -# popgrep3 - grep for abbreviations of places that say "pop" -# version3: build a match_any function -popstates = %w(CO ON MI WI MN) -expr = popstates.map{|e|"line =~ /\\b#{e}\\b/"}.join('||') -eval "def match_any(line); #{expr};end" -ARGF.each do |line| - print line if match_any(line) -end -#----------------------------- - -## building a match_all function is a trivial -## substitution of && for || -## here is a generalized example: -#!/usr/bin/ruby -w -## grepauth - print lines that mention both foo and bar -class MultiMatch - def initialize(*patterns) - _any = build_match('||',patterns) - _all = build_match('&&',patterns) - eval "def match_any(line);#{_any};end\n" - eval "def match_all(line);#{_all};end\n" - end - def build_match(sym,args) - args.map{|e|"line =~ /#{e}/"}.join(sym) - end -end - -mm = MultiMatch.new('foo','bar') -ARGF.each do |line| - print line if mm.match_all(line) -end -#----------------------------- - -#!/usr/bin/ruby -# popgrep4 - grep for abbreviations of places that say "pop" -# version4: pretty fast, but simple: compile all re's first: -popstates = %w(CO ON MI WI MN) -popstates = popstates.map{|re| %r/\b#{re}\b/} -ARGF.each do |line| - popstates.each do |state_re| - if line =~ state_re - print line - break - end - end -end - -## speeds trials on the jargon file(412): 26006 lines, 1.3MB -## popgrep1 => 7.040s -## popgrep2 => 0.656s -## popgrep2.5 => 0.633s -## popgrep3 => 0.675s -## popgrep4 => 1.027s - -# unless speed is criticial, the technique in popgrep4 is a -# reasonable balance between speed and logical simplicity. - - -# @@PLEAC@@_6.11 -begin - print "Pattern? " - pat = $stdin.gets.chomp - Regexp.new(pat) -rescue - warn "Invalid Pattern" - retry -end - - -# @@PLEAC@@_6.13 -# uses the 'amatch' extension found on: -# http://raa.ruby-lang.org/project/amatch/ -require 'amatch' -matcher = Amatch.new('balast') -#$relative, $distance = 0, 1 -File.open('/usr/share/dict/words').each_line do |line| - print line if matcher.search(line) <= 1 -end -__END__ -#CODE -ballast -ballasts -balustrade -balustrades -blast -blasted -blaster -blasters -blasting -blasts - - -# @@PLEAC@@_6.14 -str.scan(/\G(\d)/).each do |token| - puts "found #{token}" -end -#----------------------------- -n = " 49 here" -n.gsub!(/\G /,'0') -puts n -#----------------------------- -str = "3,4,5,9,120" -str.scan(/\G,?(\d+)/).each do |num| - puts "Found number: #{num}" -end -#----------------------------- -# Ruby doesn't have the String.pos or a /c re modifier like Perl -# But it does have StringScanner in the standard library (strscn) -# which allows similar functionality: - -require 'strscan' -text = 'the year 1752 lost 10 days on the 3rd of September' -sc = StringScanner.new(text) -while sc.scan(/.*?(\d+)/) - print "found: #{sc[1]}\n" -end -if sc.scan(/\S+/) - puts "Found #{sc[0]} after last number" -end -#----------------------------- -# assuming continuing from above: -puts "The position in 'text' is: #{sc.pos}" -sc.pos = 30 -puts "The position in 'text' is: #{sc.pos}" - - -# @@PLEAC@@_6.15 -#----------------------------- -# greedy pattern -str.gsub!(/<.*>/m,'') # not good - -# non-greedy (minimal) pattern -str.gsub!(/<.*?>/m,'') # not great - - -#----------------------------- -#<b><i>this</i> and <i>that</i> are important</b> Oh, <b><i>me too!</i></b> -#----------------------------- -%r{ <b><i>(.*?)</i></b> }mx -#----------------------------- -%r/BEGIN((?:(?!BEGIN).)*)END/ -#----------------------------- -%r{ <b><i>( (?: (?!</b>|</i>). )* ) </i></b> }mx -#----------------------------- -%r{ <b><i>( (?: (?!</[ib]>). )* ) </i></b> }mx -#----------------------------- -%r{ - <b><i> - [^<]* # stuff not possibly bad, and not possibly the end. - (?: - # at this point, we can have '<' if not part of something bad - (?! </?[ib]> ) # what we can't have - < # okay, so match the '<' - [^<]* # and continue with more safe stuff - ) * - </i></b> - }mx - - -# @@PLEAC@@_6.16 -#----------------------------- -$/ = "" -ARGF.each do |para| - para.scan %r/ - \b # start at word boundary - (\S+) # find chunk of non-whitespace - \b # until a word boundary - ( - \s+ # followed by whitespace - \1 # and that same chunk again - \b # and a word boundary - ) + # one or more times - /xi do - puts "dup word '#{$1}' at paragraph #{$.}" - end -end -#----------------------------- -astr = 'nobody' -bstr = 'bodysnatcher' -if "#{astr} #{bstr}" =~ /^(\w+)(\w+) \2(\w+)$/ - print "#{$2} overlaps in #{$1}-#{$2}-#{$3}" -end -#----------------------------- -#!/usr/bin/ruby -w -# prime_pattern -- find prime factors of argument using patterns -ARGV << 180 -cap = 'o' * ARGV.shift -while cap =~ /^(oo+?)\1+$/ - print $1.size, " " - cap.gsub!(/#{$1}/,'o') -end -puts cap.size -#----------------------------- -#diophantine -# solve for 12x + 15y + 16z = 281, maximizing x -if ('o' * 281).match(/^(o*)\1{11}(o*)\2{14}(o*)\3{15}$/) - x, y, z = $1.size, $2.size, $3.size - puts "One solution is: x=#{x}; y=#{y}; z=#{z}" -else - puts "No solution." -end -# => One solution is: x=17; y=3; z=2 - -#----------------------------- -# using different quantifiers: -('o' * 281).match(/^(o+)\1{11}(o+)\2{14}(o+)\3{15}$/) -# => One solution is: x=17; y=3; z=2 - -('o' * 281).match(/^(o*?)\1{11}(o*)\2{14}(o*)\3{15}$/) -# => One solution is: x=0; y=7; z=11 - -('o' * 281).match(/^(o+?)\1{11}(o*)\2{14}(o*)\3{15}$/) -# => One solution is: x=1; y=3; z=14 - - -# @@PLEAC@@_6.17 -# alpha OR beta -%r/alpha|beta/ - -# alpha AND beta -%r/(?=.*alpha)(?=.*beta)/m - -# alpha AND beta, no overlap -%r/alpha.*beta|beta.*alpha/m - -# NOT beta -%r/^(?:(?!beta).)*$/m - -# NOT bad BUT good -%r/(?=(?:(?!BAD).)*$)GOOD/m -#----------------------------- - -if !(string =~ /pattern/) # ugly - something() -end - -if string !~ /pattern/ # preferred - something() -end - - -#----------------------------- -if string =~ /pat1/ && string =~ /pat2/ - something() -end -#----------------------------- -if string =~ /pat1/ || string =~ /pat2/ - something() -end -#----------------------------- -#!/usr/bin/ruby -w -# minigrep - trivial grep -pat = ARGV.shift -ARGF.each do |line| - print line if line =~ /#{pat}/o -end -#----------------------------- - "labelled" =~ /^(?=.*bell)(?=.*lab)/m -#----------------------------- -$string =~ /bell/ && $string =~ /lab/ -#----------------------------- -$murray_hill = "blah bell blah " -if $murray_hill =~ %r{ - ^ # start of string - (?= # zero-width lookahead - .* # any amount of intervening stuff - bell # the desired bell string - ) # rewind, since we were only looking - (?= # and do the same thing - .* # any amount of intervening stuff - lab # and the lab part - ) - }mx # /m means . can match newline - - print "Looks like Bell Labs might be in Murray Hill!\n"; -end -#----------------------------- -"labelled" =~ /(?:^.*bell.*lab)|(?:^.*lab.*bell)/ -#----------------------------- -$brand = "labelled"; -if $brand =~ %r{ - (?: # non-capturing grouper - ^ .*? # any amount of stuff at the front - bell # look for a bell - .*? # followed by any amount of anything - lab # look for a lab - ) # end grouper - | # otherwise, try the other direction - (?: # non-capturing grouper - ^ .*? # any amount of stuff at the front - lab # look for a lab - .*? # followed by any amount of anything - bell # followed by a bell - ) # end grouper - }mx # /m means . can match newline - print "Our brand has bell and lab separate.\n"; -end -#----------------------------- -$map =~ /^(?:(?!waldo).)*$/s -#----------------------------- -$map = "the great baldo" -if $map =~ %r{ - ^ # start of string - (?: # non-capturing grouper - (?! # look ahead negation - waldo # is he ahead of us now? - ) # is so, the negation failed - . # any character (cuzza /s) - ) * # repeat that grouping 0 or more - $ # through the end of the string - }mx # /m means . can match newline - print "There's no waldo here!\n"; -end -=begin - 7:15am up 206 days, 13:30, 4 users, load average: 1.04, 1.07, 1.04 - -USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT - -tchrist tty1 5:16pm 36days 24:43 0.03s xinit - -tchrist tty2 5:19pm 6days 0.43s 0.43s -tcsh - -tchrist ttyp0 chthon 7:58am 3days 23.44s 0.44s -tcsh - -gnat ttyS4 coprolith 2:01pm 13:36m 0.30s 0.30s -tcsh -=end -#% w | minigrep '^(?!.*ttyp).*tchrist' -#----------------------------- -%r{ - ^ # anchored to the start - (?! # zero-width look-ahead assertion - .* # any amount of anything (faster than .*?) - ttyp # the string you don't want to find - ) # end look-ahead negation; rewind to start - .* # any amount of anything (faster than .*?) - tchrist # now try to find Tom -}x -#----------------------------- -#% w | grep tchrist | grep -v ttyp -#----------------------------- -#% grep -i 'pattern' files -#% minigrep '(?i)pattern' files -#----------------------------- - - -# @@PLEAC@@_6.20 -ans = $stdin.gets.chomp -re = %r/^#{Regexp.quote(ans)}/ -case - when "SEND" =~ re : puts "Action is send" - when "STOP" =~ re : puts "Action is stop" - when "ABORT" =~ re : puts "Action is abort" - when "EDIT" =~ re : puts "Action is edit" -end -#----------------------------- -require 'abbrev' -table = Abbrev.abbrev %w-send stop abort edit- -loop do - print "Action: " - ans = $stdin.gets.chomp - puts "Action for #{ans} is #{table[ans.downcase]}" -end - - -#----------------------------- -# dummy values are defined for 'file', 'PAGER', and -# the 'invoke_editor' and 'deliver_message' methods -# do not do anything interesting in this example. -#!/usr/bin/ruby -w -require 'abbrev' - -file = 'pleac_ruby.data' -PAGER = 'less' - -def invoke_editor - puts "invoking editor" -end - -def deliver_message - puts "delivering message" -end - -actions = { - 'edit' => self.method(:invoke_editor), - 'send' => self.method(:deliver_message), - 'list' => proc {system(PAGER, file)}, - 'abort' => proc {puts "See ya!"; exit}, - "" => proc {puts "Unknown Command"} -} - -dtable = Abbrev.abbrev(actions.keys) -loop do - print "Action: " - ans = $stdin.gets.chomp.delete(" \t") - actions[ dtable[ans.downcase] || "" ].call -end - - -# @@PLEAC@@_6.19 -#----------------------------- -# basically, the Perl Cookbook categorizes this as an -# unsolvable problem ... -#----------------------------- -1 while addr.gsub!(/\([^()]*\)/,'') -#----------------------------- -Dear someuser@host.com, - -Please confirm the mail address you gave us Wed May 6 09:38:41 -MDT 1998 by replying to this message. Include the string -"Rumpelstiltskin" in that reply, but spelled in reverse; that is, -start with "Nik...". Once this is done, your confirmed address will -be entered into our records. - - -# @@PLEAC@@_6.21 -#----------------------------- -#% gunzip -c ~/mail/archive.gz | urlify > archive.urlified -#----------------------------- -#% urlify ~/mail/*.inbox > ~/allmail.urlified -#----------------------------- -#!/usr/bin/ruby -w -# urlify - wrap HTML links around URL-like constructs - -urls = '(https?|telnet|gopher|file|wais|ftp)'; -ltrs = '\w'; -gunk = '/#~:.?+=&%@!\-'; -punc = '.:?\-'; -any = "#{ltrs}#{gunk}#{punc}"; - -ARGF.each do |line| - line.gsub! %r/ - \b # start at word boundary - ( # begin $1 { - #{urls} : # need resource and a colon - [#{any}] +? # followed by on or more - # of any valid character, but - # be conservative and take only - # what you need to.... - ) # end $1 } - (?= # look-ahead non-consumptive assertion - [#{punc}]* # either 0 or more punctuation - [^#{any}] # followed by a non-url char - | # or else - $ # then end of the string - ) - /iox do - %Q|<A HREF="#{$1}">#{$1}</A>| - end - print line -end - - -# @@PLEAC@@_6.23 -%r/^m*(d?c{0,3}|c[dm])(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$/i -#----------------------------- -str.sub!(/(\S+)(\s+)(\S+)/, '\3\2\1') -#----------------------------- -%r/(\w+)\s*=\s*(.*)\s*$/ # keyword is $1, value is $2 -#----------------------------- -%r/.{80,}/ -#----------------------------- -%r|(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)| -#----------------------------- -str.gsub!(%r|/usr/bin|,'/usr/local/bin') -#----------------------------- -str.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/){ $1.hex.chr } -#----------------------------- -str.gsub!(%r{ - /\* # Match the opening delimiter - .*? # Match a minimal number of characters - \*/ # Match the closing delimiter -}xm,'') -#----------------------------- -str.sub!(/^\s+/, '') -str.sub!(/\s+$/, '') - -# but really, in Ruby we'd just do: -str.strip! -#----------------------------- -str.gsub!(/\\n/,"\n") -#----------------------------- -str.sub!(/^.*::/, '') -#----------------------------- -%r/^([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])\. - ([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])$/x -#----------------------------- -str.sub!(%r|^.*/|, '') -#----------------------------- -cols = ( (ENV['TERMCAP'] || " ") =~ /:co#(\d+):/ ) ? $1 : 80; -#----------------------------- -name = " #{$0} #{ARGV}".gsub(%r| /\S+/|, ' ') -#----------------------------- -require 'rbconfig' -include Config -raise "This isn't Linux" unless CONFIG['target_os'] =~ /linux/i; -#----------------------------- -str.gsub!(%r/\n\s+/, ' ') -#----------------------------- -nums = str.scan(/(\d+\.?\d*|\.\d+)/) -#----------------------------- -capwords = str.scan(%r/(\b[^\Wa-z0-9_]+\b)/) -#----------------------------- -lowords = str.scan(%r/(\b[^\WA-Z0-9_]+\b)/) -#----------------------------- -icwords = str.scan(%r/(\b[^\Wa-z0-9_][^\WA-Z0-9_]*\b)/) -#----------------------------- -links = str.scan(%r/<A[^>]+?HREF\s*=\s*["']?([^'" >]+?)[ '"]?>/mi) -#----------------------------- -initial = str =~ /^\S+\s+(\S)\S*\s+\S/ ? $1 : "" -#----------------------------- -str.gsub!(%r/"([^"]*)"/, %q-``\1''-) -#----------------------------- - -$/ = "" -sentences = [] -ARGF.each do |para| - para.gsub!(/\n/, ' ') - para.gsub!(/ {3,}/,' ') - sentences << para.scan(/(\S.*?[!?.])(?= |\Z)/) -end - -#----------------------------- -%r/(\d{4})-(\d\d)-(\d\d)/ # YYYY in $1, MM in $2, DD in $3 -#----------------------------- -%r/ ^ - (?: - 1 \s (?: \d\d\d \s)? # 1, or 1 and area code - | # ... or ... - \(\d\d\d\) \s # area code with parens - | # ... or ... - (?: \+\d\d?\d? \s)? # optional +country code - \d\d\d ([\s\-]) # and area code - ) - \d\d\d (\s|\1) # prefix (and area code separator) - \d\d\d\d # exchange - $ - /x -#----------------------------- -%r/\boh\s+my\s+gh?o(d(dess(es)?|s?)|odness|sh)\b/i -#----------------------------- -lines = [] -lines << $1 while input.sub!(/^([^\012\015]*)(\012\015?|\015\012?)/,'') - - -# @@PLEAC@@_7.0 -# An IO object being Enumerable, we can use 'each' directly on it -File.open("/usr/local/widgets/data").each { |line| - puts line if line =~ /blue/ -} - -logfile = File.new("/var/log/rubylog.txt", "w") -mysub($stdin, logfile) - -# The method IO#readline is similar to IO#gets -# but throws an exception when it reaches EOF -f = File.new("bla.txt") -begin - while (line = f.readline) - line.chomp - $stdout.print line if line =~ /blue/ - end -rescue EOFError - f.close -end - -while $stdin.gets # reads from STDIN - unless (/\d/) - $stderr.puts "No digit found." # writes to STDERR - end - puts "Read: #{$_}" # writes to STDOUT -end - -logfile = File.new("/tmp/log", "w") - -logfile.close - -# $defout (or its synonym '$>') is the destination of output -# for Kernel#print, Kernel#puts, and family functions -logfile = File.new("log.txt", "w") -old = $defout -$defout = logfile # switch to logfile for output -puts "Countdown initiated ..." -$defout = old # return to original output -puts "You have 30 seconds to reach minimum safety distance." - - -# @@PLEAC@@_7.1 -source = File.new(path, "r") # open file "path" for reading only -sink = File.new(path, "w") # open file "path" for writing only - -source = File.open(path, File::RDONLY) # open file "path" for reading only -sink = File.open(path, File::WRONLY) # open file "path" for writing only - -file = File.open(path, "r+") # open "path" for reading and writing -file = File.open(path, flags) # open "path" with the flags "flags" (see examples below for flags) - -# open file "path" read only -file = File.open(path, "r") -file = File.open(path, File::RDONLY) - -# open file "path" write only, create it if it does not exist -# truncate it to zero length if it exists -file = File.open(path, "w") -file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT) -file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT, 0666) # with permission 0666 - -# open file "path" write only, fails if file exists -file = File.open(path, File::WRONLY|File::EXCL|File::CREAT) -file = File.open(path, File::WRONLY|File::EXCL|File::CREAT, 0666) - -# open file "path" for appending -file = File.open(path, "a") -file = File.open(path, File::WRONLY|File::APPEND|File::CREAT) -file = File.open(path, File::WRONLY|File::APPEND|File::CREAT, 0666) - -# open file "path" for appending only when file exists -file = File.open(path, File::WRONLY|File::APPEND) - -# open file "path" for reading and writing -file = File.open(path, "r+") -file = File.open(path, File::RDWR) - -# open file for reading and writing, create a new file if it does not exist -file = File.open(path, File::RDWR|File::CREAT) -file = File.open(path, File::RDWR|File::CREAT, 0600) - -# open file "path" reading and writing, fails if file exists -file = File.open(path, File::RDWR|File::EXCL|File::CREAT) -file = File.open(path, File::RDWR|File::EXCL|File::CREAT, 0600) - - -# @@PLEAC@@_7.2 -# No problem with Ruby since the filename doesn't contain characters with -# special meaning; like Perl's sysopen -File.open(filename, 'r') - - -# @@PLEAC@@_7.3 -File.expand_path('~root/tmp') -#=> "/root/tmp" -File.expand_path('~rpcuser') -#=> "/var/lib/nfs" - -# To expand ~/.. it explicitely needs the environment variable HOME -File.expand_path('~/tmp') -#=> "/home/gc/tmp" - - -# @@PLEAC@@_7.4 -# The exception raised in Ruby reports the filename -File.open('afile') - - -# @@PLEAC@@_7.5 -# Standard Ruby distribution provides the following useful extension -require 'tempfile' -# With the Tempfile class, the file is automatically deleted on garbage -# collection, so you won't need to remove it, later on. -tf = Tempfile.new('tmp') # a name is required to create the filename - -# If you need to pass the filename to an external program you can use -# File#path, but don't forget to File#flush in order to flush anything -# living in some buffer somewhere. -tf.flush -system("/usr/bin/dowhatever #{tf.path}") - -fh = Tempfile.new('tmp') -fh.sync = true # autoflushes -10.times { |i| fh.puts i } -fh.rewind -puts 'Tmp file has: ', fh.readlines - - -# @@PLEAC@@_7.6 -while (DATA.gets) do - # process the line -end -__END__ -# your data goes here -# __DATA__ doesn't exist in Ruby - -#CODE -# get info about the script (size, date of last modification) -kilosize = DATA.stat.size / 1024 -last_modif = DATA.stat.mtime -puts "<P>Script size is #{kilosize}" -puts "<P>Last script update: #{last_modif}" -__END__ -# DO NOT REMOVE THE PRECEEDING LINE. -# Everything else in this file will be ignored. -#CODE - - -# @@PLEAC@@_7.7 -while line = gets do - # do something with line. -end - -# or -while gets do - # do something with $_ -end - -# or more rubyish -$stdun.each do |line| - # do stuff with line -end - - -# ARGF may makes this more easy -# this is skipped if ARGV.size==0 -ARGV.each do |filename| - # closing and exception handling are done by the block - open(filename) do |fd| - fd.each do |line| - # do stuff with line - end - end rescue abort("can't open %s" % filename) -end - -# globbing is done in the Dir module -ARGV = Dir["*.[Cch]"] if ARGV.empty? - -# note: optparse is the preferred way to handle this -if (ARGV[0] == '-c') - chop_first += 1 - ARGV.shift -end - - -# processing numerical options -if ARGV[0] =~ /^-(\d+)$/ - columns = $1 - ARGV.shift -end - -# again, better to use optparse: -require 'optparse' -nostdout = 0 -append = 0 -unbuffer = 0 -ignore_ints = 0 -ARGV.options do |opt| - opt.on('-n') { nostdout +=1 } - opt.on('-a') { append +=1 } - opt.on('-u') { unbuffer +=1 } - opt.on('-i') { ignore_ints +=1 } - opt.parse! -end or abort("usage: " + __FILE__ + " [-ainu] [filenames]") - -# no need to do undef $/, we have File.read -str = File.read(ARGV[0]) - -# again we have File.read -str = File.read(ARGV[0]) - -# not sure what this should do: -# I believe open the file, print filename, lineno and line: -ARGF.each_with_index do |line, idx| - print ARGF.filename, ":", idx, ";", line -end - -# print all the lines in every file passed via command line that contains login -ARGF.each do |line| - puts line if line =~ /login/ -end -# -# even this would fit -#%ruby -ne "print if /f/" 2.log -# - -ARGF.each { |l| puts l.downcase! } - -#------------------ -#!/usr/bin/ruby -p -# just like perl's -p -$_.downcase! -# - -# I don't know who should I trust. -# perl's version splits on \w+ while python's on \w. - -chunks = 0 - -File.read(ARGV[0]).split.each do |word| - next if word =~ /^#/ - break if ["__DATA__", "__END__"].member? word - chunks += 1 -end - -print "Found ", chunks, " chunks\n" - - -# @@PLEAC@@_7.8 -old = File.open(old_file) -new = File.open(new_file, "w") -while old.gets do - # change $_, then... - new.print $_ -end -old.close -new.close -File.rename(old_file, "old.orig") -File.rename(new_file, old_file) - -while old.gets do - if $. == 20 then # we are at the 20th line - new.puts "Extra line 1" - new.puts "Extra line 2" - end - new.print $_ -end - -while old.gets do - next if 20..30 # skip the 20th line to the 30th - # Ruby (and Perl) permit to write if 20..30 - # instead of if (20 <= $.) and ($. <= 30) - new.print $_ -end - - -# @@PLEAC@@_7.9 -#% ruby -i.orig -pe 'FILTER COMMAND' file1 file2 file3 ... -# -#----------------------------- -##!/usr/bin/ruby -i.orig -p -# filter commands go here -#----------------------------- - -#% ruby -pi.orig -e 'gsub!(/DATE/){Time.now)' - -# effectively becomes: -ARGV << 'I' -oldfile = "" -while gets - if ARGF.filename != oldfile - newfile = ARGF.filename - File.rename(newfile, newfile + ".orig") - $stdout = File.open(newfile,'w') - oldfile = newfile - end - gsub!(/DATE/){Time.now} - print -end -$stdout = STDOUT -#----------------------------- -#% ruby -i.old -pe 'gsub!(%r{\bhisvar\b}, 'hervar')' *.[Cchy] - -#----------------------------- -# set up to iterate over the *.c files in the current directory, -# editing in place and saving the old file with a .orig extension -$-i = '.orig' # set up -i mode -ARGV.replace(Dir['*.[Cchy]']) -while gets - if $. == 1 - print "This line should appear at the top of each file\n" - end - gsub!(/\b(p)earl\b/i, '\1erl') # Correct typos, preserving case - print - ARGF.close if ARGF.eof -end - - -# @@PLEAC@@_7.10 -File.open('itest', 'r+') do |f| # open file for update - lines = f.readlines # read into array of lines - lines.each do |it| # modify lines - it.gsub!(/foo/, 'QQQ') - end - f.pos = 0 # back to start - f.print lines # write out modified lines - f.truncate(f.pos) # truncate to new length -end # file is automatically closed -#----------------------------- -File.open('itest', 'r+') do |f| - out = "" - f.each do |line| - out << line.gsub(/DATE/) {Time.now} - end - f.pos = 0 - f.print out - f.truncate(f.pos) -end - -# @@PLEAC@@_7.11 -File.open('infile', 'r+') do |f| - f.flock File::LOCK_EX - # update file -end -#----------------------------- -File::LOCK_SH # shared lock (for reading) -File::LOCK_EX # exclusive lock (for writing) -File::LOCK_NB # non-blocking request -File::LOCK_UN # free lock -#----------------------------- -unless f.flock File::LOCK_EX | File::LOCK_NB - warn "can't get immediate lock: blocking ..." - f.flock File::LOCK_EX -end -#----------------------------- -File.open('numfile', File::RDWR|File::CREAT) do |f| - f.flock(File::LOCK_EX) - num = f.gets.to_i || 0 - f.pos = 0 - f.truncate 0 - f.puts num + 1q -end - - -# @@PLEAC@@_7.12 -output_handle.sync = true -# Please note that like in Perl, $stderr is already unbuffered -#----------------------------- -#!/usr/bin/ruby -w -# seeme - demo stdio output buffering -$stdout.sync = ARGV.size > 0 -print "Now you don't see it..." -sleep 2 -puts "now you do" -#----------------------------- -$stderr.sync = true -afile.sync = false -#----------------------------- -# assume 'remote_con' is an interactive socket handle, -# but 'disk_file' is a handle to a regular file. -remote_con.sync = true # unbuffer for clarity -disk_file.sync = false # buffered for speed -#----------------------------- -require 'socket' -sock = TCPSocket.new('www.ruby-lang.org', 80) -sock.sync = true -sock.puts "GET /en/ HTTP/1.0 \n\n" -resp = sock.read -print "DOC IS: #{resp}\n" - - -# @@PLEAC@@_7.13 -#----------------------------- -# assumes fh1, fh2, fh2 are oen IO objects -nfound = select([$stdin, fh1, fh2, fh3], nil, nil, 0) -nfound[0].each do |file| - case file - when fh1 - # do something with fh1 - when fh2 - # do something with fh2 - when fh3 - # do something with fh3 - end -end -#----------------------------- -input_files = [] -# repeat next line for all in-files to poll -input_files << fh1 -if nfound = select(input_files, nil, nil, 0) - # input ready on files in nfound[0] -end - - -# @@PLEAC@@_8.0 -#----------------------------- -# datafile is a file or IO object -datafile.readlines.each { |line| - line.chomp! - size = line.length - puts size -} -#----------------------------- -datafile.readlines.each { |line| - puts line.chomp!.length -} -#----------------------------- -lines = datafile.readlines -#----------------------------- -whole_file = file.read -#----------------------------- -# ruby -040 -e 'word = gets; puts "First word is #{word}"' -#----------------------------- -# ruby -ne 'BEGIN { $/="%%\n" }; $_.chomp; puts $_ if( $_=~/Unix/i)' fortune.dat -#----------------------------- -handle.print "one", "two", "three" # "onetwothree" -puts "Baa baa black sheep." # sent to $stdout -#----------------------------- -buffer = handle.read(4096) -rv = buffer.length -#----------------------------- -handle.truncate(length) -open("/tmp#{$$}.pid", 'w') { |handle| handle.truncate(length) } -#----------------------------- -pos = datafile.pos # tell is an alias of pos -puts "I'm #{pos} bytes from the start of datafile" -#----------------------------- -logfile.seek(0, IO::SEEK_END) -datafile.seek(pos) # IO::SEEK_SET is the default -out.seek(-20, IO::SEEK_CUR) -#----------------------------- -written = datafile.syswrite(mystring) -raise RunTimeError unless written == mystring.length -block = infile.sysread(256) # no equivalent to perl offset parameter in sysread -puts "only read #{block.length} bytes" if 256 != block.length -#----------------------------- -pos = handle.sysseek(0, IO::SEEK_CUR) # don't change position - - -# @@PLEAC@@_8.1 -while (line = fh.gets) - line.chomp! - nextline = nil - line.gsub!(/\\$/) { |match| nextline = fh.gets; '' } - if (nextline != nil) - line += nextline - redo - end - # process full record in line here -end -#----------------------------- -# DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \ -# $(TEXINFOS) $(INFOS) $(MANS) $(DATA) -# DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \ -# $(TEXINFOS) $(INFO_DEPS) $(MANS) $(DATA) \ -# $(EXTRA_DIST) -#----------------------------- -line.gsub!(/\\\s*$/, '') { - # as before -} - - -# @@PLEAC@@_8.2 -#----------------------------- -count = `wc -l < #{filename}` -fail "wc failed: #{$?}" if $? != 0 -count.chomp! -#----------------------------- -count = 0 -File.open(file, 'r') { |fh| - count += 1 while fh.gets -} -# count now holds the number of lines read -#----------------------------- -count = 0 -while (chunk = file.sysread(2**16)) - count += chunk.count("\n") -end rescue EOFError -#----------------------------- -File.open(filename,'r') { |fh| - count += 1 while fh.gets -} -# count now holds the number of lines read -#----------------------------- -# As ruby doesn't quite have an equivalent to using a for -# statement as in perl, I threw this in -count = File.readlines(filename).size -#----------------------------- -1 while file.gets -count = $. -#----------------------------- -$/ = '' -open(filename, 'r') { |fh| - 1 while fh.gets - para_count = $. -} rescue fail("can't open #{filename}: $!") -#----------------------------- - - -# ^^PLEAC^^_8.3 -#----------------------------- -while (gets) - split.each { |chunk| - # do something with chunk - } -end -#----------------------------- -while (gets) - gsub(/(\w[\w'-]*)/) { |word| - # do something with word - } -end -#----------------------------- -# Make a word frequency count -# normally hashes can be created using {} or just Hash.new -# but we want the default value of an entry to be 0 instead -# of nil. (nil can't be incremented) -seen = Hash.new(0) -while (gets) - gsub(/(\w[\w'-]*)/) { |word| - seen[word.downcase] += 1 - } -end -# output hash in a descending numeric sort of its values -seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v| - printf("%5d %s\n", v, k ) -end - -#----------------------------- -# Line frequency count -seen = Hash.new(0) -while (gets) - seen[$_.downcase] += 1 -end -seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v| - printf("%5d %s\n", v, k ) -end -#----------------------------- - - -# @@PLEAC@@_8.4 -#----------------------------- -# instead of file handle FILE, we can just -# use a string containing the filename -File.readlines(file).each { |line| - # do something with line -} -#----------------------------- -File.readlines(file).reverse_each { |line| - # do something with line -} -#----------------------------- -# the variable lines might have been created -# this way -# lines = File.readlines(file) -# -# normally one would use the reverse_each, but -# if you insist on using a numerical index to -# iterate over the lines array... -(lines.size - 1).downto(0) { |i| - line = lines[i] -} -#----------------------------- -# the second readlines argument is a the -# record separator $/, just like perl, a blank -# separator splits the records into paragraphs -File.readlines(file, '').each { |paragraph| - # do something with paragraph - puts "->Paragraph #{paragraph}" -} -#----------------------------- - - -# @@PLEAC@@_8.6 - -$/ = "%\n"; -srand; - -File.open('/usr/share/fortune/humorists').each do |line| - adage = line if rand($.) < 1 -end - -puts adage; - - -# @@PLEAC@@_8.10 -begin - fh = File.open(file, "r+") - addr = fh.tell unless fh.eof while fh.gets - fh.truncate(addr) -rescue SystemCallError - $stderr.puts "#$!" -end - - -# @@PLEAC@@_9.0 -entry = File.stat("/usr/bin/vi") -entry = File.stat("/usr/bin") -entry = File.stat(INFILE) - -entry = File.stat("/usr/bin/vi") -ctime = entry.ctime -size = entry.size - -f = File.open(filename, "r") - -## There is no -T equivalent in Ruby, but we can still test emptiness -if test(?s, filename) - puts "#{filename} doesn't have text in it." - exit -end - -Dir.new("/usr/bin").each do |filename| - puts "Inside /usr/bin is something called #{filename}" -end - - -# @@PLEAC@@_9.1 -file = File.stat("filename") -readtime, writetime = file.atime, file.mtime -file.utime(readtime, writetime) - -SECONDS_PER_DAY = 60 * 60 * 24 -file = File.stat("filename") -atime, mtime = file.atime, file.mtime - -atime -= 7 * SECONDS_PER_DAY -mtime -= 7 * SECONDS_PER_DAY - -File.utime(atime, mtime, file) -mtime = File.stat(file).mtime -File.utime(Time.new, mtime, file) -File.utime(Time.new, File.stat("testfile").mtime, file) - -#----------------------------- -#!/usr/bin/ruby -w -## uvi - vi a file without changing it's access times - -if ARGV.length != 1 - puts "usage: uvi filename" - exit -end -file = ARGV[0] -atime, mtime = File.stat(file).atime, File.stat(file).mtime -system(ENV["EDITOR"] || "vi", file) -File.utime(atime, mtime, file) -#----------------------------- - - -# @@PLEAC@@_9.2 -File.unlink(FILENAME) - -err_flg = false -filenames.each do |file| - begin - File.unlink(file) - rescue - err_flg = $! - end -end -err_flg and raise "Couldn't unlink all of #{filenames.join(" ")}: #{err_flg}" - -File.unlink(file) - -count = filenames.length -filenames.each do |file| - begin - File.unlink(file) - rescue - count -= 1 - end -end -if count != filenames.length - STDERR.puts "could only delete #{count} of #{filenames.length} files" -end - - -# @@PLEAC@@_9.3 -require "ftools" -File.copy(oldfile, newfile) - -infile = File.open(oldfile, "r") -outfile = File.open(newfile, "w") - -blksize = infile.stat.blksize -# This doesn't handle partial writes or ^Z -# like the Perl version does. -while (line = infile.read(blksize)) - outfile.write(line) -end - -infile.close -outfile.close - -system("cp #{oldfile} #{newfile}") # unix -system("copy #{oldfile} #{newfile}") # dos, vms - -require "ftools" -File.copy("datafile.dat", "datafile.bak") -File.move("datafile.new", "datafile.dat") - - -# @@PLEAC@@_9.4 -$seen = {} # must use global var to be seen inside of method below - -def do_my_thing(filename) - dev, ino = File.stat(filename).dev, File.stat(filename).ino - unless $seen[[dev, ino]] - # do something with $filename because we haven't - # seen it before - end - $seen[[dev, ino]] = $seen[[dev, ino]].to_i + 1 -end - -files.each do |filename| - dev, ino = File.stat(filename).dev, File.stat(filename).ino - if !$seen.has_key?([dev, ino]) - $seen[[dev, ino]] = [] - end - $seen[[dev, ino]].push(filename) -end - -$seen.keys.sort.each do |devino| - ino, dev = devino - if $seen[devino].length > 1 - # $seen[devino] is a list of filenames for the same file - end -end - - -# @@PLEAC@@_9.5 -Dir.open(dirname) do |dir| - dir.each do |file| - # do something with dirname/file - puts file - end -end -# Dir.close is automatic - -# No -T equivalent in Ruby - -dir.each do |file| - next if file =~ /^\.\.?$/ - # ... -end - -def plainfiles(dir) - dh = Dir.open(dir) - dh.entries.grep(/^[^.]/). - map {|file| "#{dir}/#{file}"}. - find_all {|file| test(?f, file)}. - sort -end - - -# @@PLEAC@@_9.6 -list = Dir.glob("*.c") - -dir = Dir.open(path) -files = dir.entries.grep(/\.c$/) -dir.close - -files = Dir.glob("*.c") -files = Dir.open(path).entries.grep(/\.[ch]$/i) - -dir = Dir.new(path) -files = dir.entries.grep(/\.[ch]$/i) - -begin - d = Dir.open(dir) -rescue Errno::ENOENT - raise "Couldn't open #{dir} for reading: #{$!}" -end - -files = [] -d.each do |file| - puts file - next unless file =~ /\.[ch]$/i - - filename = "#{dir}/#{file}" - # There is no -T equivalent in Ruby, but we can still test emptiness - files.push(filename) if test(?s, filename) -end - -dirs.entries.grep(/^\d+$/). - map { |file| [file, "#{path}/#{file}"]} . - select { |file| test(?d, file[1]) }. - sort { |a,b| a[0] <=> b[0] }. - map { |file| file[1] } - - -# @@PLEAC@@_9.7 -require 'find' -Find.find(dirlist) do |file| - # do whatever -end - -require 'find' -argv = ARGV.empty? ? %w{.} : ARGV -Find.find(*argv) do |file| - print file, (test(?d, file) ? "/\n" : "\n") -end - -require 'find' -argv = ARGV.empty? ? %w{.} : ARGV -sum = 0 -Find.find(*argv) do |file| - size = test(?s, file) || 0 - sum += size -end -puts "#{argv.join(' ')} contains #{sum} bytes" - -require 'find' -argv = ARGV.empty? ? %w{.} : ARGV -saved_size, saved_name = -1, "" -Find.find(*argv) do |file| - size = test(?s, file) || 0 - next unless test(?f, file) && size > saved_size - saved_size = size - saved_name = file -end -puts "Biggest file #{saved_name} in #{argv.join(' ')} is #{saved_size}" - -require 'find' -argv = ARGV.empty? ? %w{.} : ARGV -age, name = nil -Find.find(*argv) do |file| - mtime = File.stat(file).mtime - next if age && age > mtime - age = mtime - name = file -end -puts "#{name} #{age}" - -#----------------------------- -#!/usr/bin/ruby -w -# fdirs - find all directories -require 'find' -argv = ARGV.empty? ? %w{.} : ARGV -File.find(*argv) { |file| puts file if test(?d, file) } -#----------------------------- - - -# @@PLEAC@@_9.8 -require 'fileutils' - -puts "Usage #{$0} dir ..." if ARGV.empty? -ARGV.each do |dir| - FileUtils.rmtree(dir) -end - - -# @@PLEAC@@_9.9 -require 'ftools' -names.each do |file| - newname = file - begin - File.move(file, newname) - rescue Errno::EPERM - $stderr.puts "Couldn't rename #{file} to #{newname}: #{$!}" - end -end - -require 'ftools' -op = ARGV.empty? ? (raise "Usage: rename expr [files]\n") : ARGV.shift -argv = ARGV.empty? ? $stdin.readlines.map { |f| f.chomp } : ARGV -argv.each do |file| - was = file - file = eval("file.#{op}") - File.move(was, file) unless was == file -end - - -# @@PLEAC@@_9.10 -base = File.basename(path) -dir = File.dirname(path) -# ruby has no fileparse equivalent -dir, base = File.split(path) -ext = base.scan(/\..*$/).to_s - -path = '/usr/lib/libc.a' -file = File.basename(path) -dir = File.dirname(path) - -puts "dir is #{dir}, file is #{file}" -# dir is /usr/lib, file is libc.a - -path = '/usr/lib/libc.a' -dir, filename = File.split(path) -name, ext = filename.split(/(?=\.)/) -puts "dir is #{dir}, name is #{name}, ext is #{ext}" -# NOTE: The Ruby code prints -# dir is /usr/lib, name is libc, extension is .a -# while the Perl code prints a '/' after the directory name -# dir is /usr/lib/, name is libc, extension is .a - -# No fileparse_set_fstype() equivalent in ruby - -def extension(path) - ext = path.scan(/\..*$/).to_s - ext.sub(/^\./, "") -end - - -# @@PLEAC@@_9.11 -#----------------------------- -#!/usr/bin/ruby -w -# symirror - build spectral forest of symlinks - -require 'find' -require 'fileutils' - -raise "usage: #{$0} realdir mirrordir" unless ARGV.size == 2 - -srcdir,dstdir = ARGV -srcmode = File::stat(srcdir).mode -Dir.mkdir(dstdir, srcmode & 07777) unless test(?d, dstdir) - -# fix relative paths -Dir.chdir(srcdir) {srcdir = Dir.pwd} -Dir.chdir(dstdir) {dstdir = Dir.pwd} - -Find.find(srcdir) do |srcfile| - if test(?d, srcfile) - dest = srcfile.sub(/^#{srcdir}/, dstdir) - dmode = File::stat(srcfile).mode & 07777 - Dir.mkdir(dest, dmode) unless test(?d, dest) - a = Dir["#{srcfile}/*"].reject{|f| test(?d, f)} - FileUtils.ln_s(a, dest) - end -end - - -# @@PLEAC@@_9.12 -# we use the Getopt/Declare library here for convenience: -# http://raa.ruby-lang.org/project/getoptdeclare/ -#----------------------------- -#!/usr/bin/ruby -w -# lst - list sorted directory contents (depth first) - -require 'find' -require 'etc' -require "Getopt/Declare" - -# Note: in the option-spec below there must by at least one hard -# tab in between each -option and its description. For example -# -i <tab> read from stdin - -opts = Getopt::Declare.new(<<'EOPARAM') - ============ - Input Format: - -i read from stdin - ============ - Output Format: - -l long listing - -r reverse listing - ============ - Sort on: (one of) - -m mtime (modify time - default) - {$sort_criteria = :mtime} - -u atime (access time) - {$sort_criteria = :atime} - -c ctime (inode change time) - {$sort_criteria = :ctime} - -s size - {$sort_criteria = :size} - [mutex: -m -u -c -s] - -EOPARAM - -$sort_criteria ||= :mtime -files = {} -DIRS = opts['-i'] ? $stdin.readlines.map{|f|f.chomp!} : ARGV -DIRS.each do |dir| - Find.find(dir) do |ent| - files[ent] = File::stat(ent) - end -end -entries = files.keys.sort_by{|f| files[f].send($sort_criteria)} -entries = entries.reverse unless opts['-r'] - -entries.each do |ent| - unless opts['-l'] - puts ent - next - end - stats = files[ent] - ftime = stats.send($sort_criteria == :size ? :mtime : $sort_criteria) - printf "%6d %04o %6d %8s %8s %8d %s %s\n", - stats.ino, - stats.mode & 07777, - stats.nlink, - ETC::PASSWD[stats.uid].name, - ETC::GROUP[stats.gid].name, - stats.size, - ftime.strftime("%a %b %d %H:%M:%S %Y"), - ent -end - - -# @@PLEAC@@_10.0 -def hello - $greeted += 1 # in Ruby, a variable beginning with $ is global (can be any type of course) - puts "hi there!" -end - -# We need to initialize $greeted before it can be used, because "+=" is waiting a Numeric object -$greeted = 0 -hello # note that appending () is optional to function calls with no parameters - - -# @@PLEAC@@_10.1 -# In Ruby, parameters are named anyway -def hypotenuse(side1, side2) - Math.sqrt(side1**2 + side2**2) # the sqrt function comes from the Math module -end -diag = hypotenuse(3, 4) - -puts hypotenuse(3, 4) - -a = [3, 4] -print hypotenuse(*a) # the star operator will magically convert an Array into a "tuple" - -both = men + women - -# In Ruby, all objects are references, so the same problem arises; we then return a new object -nums = [1.4, 3.5, 6.7] -def int_all(n) - n.collect { |v| v.to_i } -end -ints = int_all(nums) - -nums = [1.4, 3.5, 6.7] -def trunc_em(n) - n.collect! { |v| v.to_i } # the bang-version of collect modifies the object -end -trunc_em(nums) - -# Ruby has two chomp version: -# ``chomp'' chomps the record separator and returns what's expected -# ``chomp!'' does the same but also modifies the parameter object - - -# @@PLEAC@@_10.2 -def somefunc - variable = something # variable is local by default -end - -name, age = ARGV -start = fetch_time - -a, b = pair # will succeed if pair is an Array object (like ARGV is) -c = fetch_time - -# In ruby, run_check can't access a, b, or c until they are -# explicitely defined global (using leading $), even if they are -# both defined in the same scope - -def check_x(x) - y = "whatever" - run_check - if $condition - puts "got $x" - end -end - -# The following will keep a reference to the array, though the -# results will be slightly different from perl: the last element -# of $global_array will be itself an array -def save_array(ary) - $global_array << ary -end - -# The following gives the same results as in Perl for $global_array, -# though it doesn't illustrate anymore the way to keep a reference -# to an object: $global_array is extended with the elements of ary -def save_array(ary) - $global_array += ary -end - - -# @@PLEAC@@_10.3 -# In Ruby, AFAIK a method cannot access "local variables" defined -# upper scope; mostly because everything is an object, so you'll -# do the same by defining an attribute or a static attribute - -# In Ruby the BEGIN also exists: -BEGIN { puts "hello from BEGIN" } -puts "hello from main" -BEGIN { puts "hello from 2nd BEGIN" } -# gives: -# hello from BEGIN -# hello from 2nd BEGIN -# hello from main - -# In Ruby, it can be written as a static method and a static -# variable -class Counter - @@counter = 0 - def Counter.next_counter; @@counter += 1; end -end - -# There is no need of BEGIN since the variable will get -# initialized when parsing -class Counter - @@counter = 42 - def Counter.next_counter; @@counter += 1; end - def Counter.prev_counter; @@counter -= 1; end -end - - -# @@PLEAC@@_10.4 -# You can either get the whole trace as an array of strings, each -# string telling which file, line and method is calling: -caller - -# ...or only the last caller -caller[0] - -# We need to extract just the method name of the backtrace: -def whoami; caller()[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end -def whowasi; caller()[1] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end - - -# @@PLEAC@@_10.5 -# In Ruby, every value is a reference on an object, thus there is -# no such problem -array_diff(array1, array2) - -def add_vecpair(a1, a2) - results = [] - a1.each_index { |i| results << (a1[i] + a2[i]) } - results -end -a = [1, 2] -b = [5, 8] -c = add_vecpair(a, b) -p c - -# Add this to the beginning of the function to check if we were -# given two arrays -a1.type == Array && a2.type == Array or - raise "usage: add_vecpair array1 array2 (was used with: #{a1.type} #{a2.type})" - - -# @@PLEAC@@_10.6 -# There is no return context in Ruby - - -# @@PLEAC@@_10.7 -# Like in Perl, we need to fake with a hash, but it's dirty :-( -def thefunc(param_args) - args = { 'INCREMENT' => '10s', 'FINISH' => '0', 'START' => 0 } - args.update(param_args) - if (args['INCREMENT'] =~ /m$/ ) - # ..... - end -end - -thefunc({ 'INCREMENT' => '20s', 'START' => '+5m', 'FINISH' => '+30m' }) -thefunc({}) - - -# @@PLEAC@@_10.8 -# there is no "undef" direct equivalent but there is the slice equiv: -a, c = func.indexes(0, 2) - - -# @@PLEAC@@_10.9 -# Ruby has no such limitation: -def somefunc - ary = [] - hash = {} - # ... - return ary, hash -end -arr, dict = somefunc - -array_of_hashes = fn -h1, h2, h3 = fn - - -# @@PLEAC@@_10.10 -return -# or (equivalent) -return nil - - -# @@PLEAC@@_10.11 -# You can't prototype in Ruby regarding types :-( -# Though, you can force the number of arguments: -def func_with_no_arg; end -def func_with_no_arg(); end -def func_with_one_arg(a1); end -def func_with_two_args(a1, a2); end -def func_with_any_number_of_args(*args); end - - -# @@PLEAC@@_10.12 -raise "some message" # raise exception - -begin - val = func -rescue Exception => msg - $stderr.puts "func raised an exception: #{msg}" -end - -# In Ruby the rescue statement uses an exception class, every -# exception which is not matched is still continuing -begin - val = func -rescue FullMoonError - ... -end - - -# @@PLEAC@@_10.13 -# Saving Global Values -# Of course we can just save the value and restore it later: -def print_age - puts "Age is #{$age}" -end - -$age = 18 # global variable -print_age() -if condition - safeage = $age - $age = 23 - print_age() - $age = safeage -end - -# We can also use a method that saves the global variable and -# restores it automatically when the block is left: - -def local(var) - eval("save = #{var.id2name}") - begin - result = yield - ensure - # we want to call this even if we got an exception - eval("#{var.id2name} = save") - end - result -end - -condition = true -$age = 18 -print_age() -if condition - local(:$age) { - $age = 23 - print_age() - } -end -print_age() - -# There is no need to use local() for filehandles or directory -# handles in ruby because filehandles are normal objects. - - -# @@PLEAC@@_10.14 -# In Ruby you may redefine a method [but not overload it :-(] -# just by defining again with the same name. -def foo; puts 'foo'; end -def foo; puts 'bar'; end -foo -#=> bar - -# You can also take a reference to an existing method before -# redefining a new one, using the `alias' keyword -def foo; puts 'foo'; end -alias foo_orig foo -def foo; puts 'bar'; end -foo_orig -foo -#=> foo -#=> bar - -# AFAIK, there is no direct way to create a new method whose name -# comes from a variable, so use "eval" -colors = %w(red blue green yellow orange purple violet) -colors.each { |c| - eval <<-EOS - def #{c}(*a) - "<FONT COLOR='#{c}'>" + a.to_s + "</FONT>" - end - EOS -} - - -# @@PLEAC@@_10.15 -def method_missing(name, *args) - "<FONT COLOR='#{name}'>" + args.join(' ') + "</FONT>" -end -puts chartreuse("stuff") - - -# @@PLEAC@@_10.16 -def outer(arg) - x = arg + 35 - inner = proc { x * 19 } - x + inner.call() -end - - -# @@PLEAC@@_10.17 -#!/usr/bin/ruby -w -# mailsort - sort mbox by different criteria -require 'English' -require 'Date' - -# Objects of class Mail represent a single mail. -class Mail - attr_accessor :no - attr_accessor :subject - attr_accessor :fulltext - attr_accessor :date - - def initialize - @fulltext = "" - @subject = "" - end - - def append(para) - @fulltext << para - end - - # this is called if you call puts(mail) - def to_s - @fulltext - end -end - -# represents a list of mails. -class Mailbox < Array - - Subjectpattern = Regexp.new('Subject:\s*(?:Re:\s*)*(.*)\n') - Datepattern = Regexp.new('Date:\s*(.*)\n') - - # reads mails from open file and stores them - def read(file) - $INPUT_RECORD_SEPARATOR = '' # paragraph reads - msgno = -1 - file.each { |para| - if para =~ /^From/ - mail = Mail.new - mail.no = (msgno += 1) - md = Subjectpattern.match(para) - if md - mail.subject = md[1] - end - md = Datepattern.match(para) - if md - mail.date = DateTime.parse(md[1]) - else - mail.date = DateTime.now - end - self.push(mail) - end - mail.append(para) if mail - } - end - - def sort_by_subject_and_no - self.sort_by { |m| - [m.subject, m.no] - } - end - - # sorts by a list of attributs of mail, given as symbols - def sort_by_attributs(*attrs) - # you can sort an Enumerable by an array of - # values, they would be compared - # from ary[0] to ary[n]t, say: - # ['b',1] > ['a',10] > ['a',9] - self.sort_by { |elem| - attrs.map { |attr| - elem.send(attr) - } - } - end - -end - -mailbox = Mailbox.new -mailbox.read(ARGF) - -# print only subjects sorted by subject and number -for m in mailbox.sort_by_subject_and_no - puts(m.subject) -end - -# print complete mails sorted by date, then subject, then number -for m in mailbox.sort_by_attributs(:date, :subject) - puts(m) -end - - -# @@PLEAC@@_11.7 -def mkcounter(count) - start = count - bundle = { - "NEXT" => proc { count += 1 }, - "PREV" => proc { count -= 1 }, - "RESET" => proc { count = start } - } - bundle["LAST"] = bundle["PREV"] - return bundle -end - -c1 = mkcounter(20) -c2 = mkcounter(77) - -puts "next c1: #{c1["NEXT"].call}" # 21 -puts "next c2: #{c2["NEXT"].call}" # 78 -puts "next c1: #{c1["NEXT"].call}" # 22 -puts "last c1: #{c1["PREV"].call}" # 21 -puts "last c1: #{c1["LAST"].call}" # 20 -puts "old c2: #{c2["RESET"].call}" # 77 - - -# @@PLEAC@@_11.15 -class Binary_tree - def initialize(val) - @value = val - @left = nil - @right = nil - end - - # insert given value into proper point of - # provided tree. If no tree provided, - # use implicit pass by reference aspect of @_ - # to fill one in for our caller. - def insert(val) - if val < @value then - if @left then - @left.insert(val) - else - @left = Binary_tree.new(val) - end - elsif val > @value then - if @right then - @right.insert(val) - else - @right = Binary_tree.new(val) - end - else - puts "double" - # do nothing, no double values - end - end - - # recurse on left child, - # then show current value, - # then recurse on right child. - def in_order - @left.in_order if @left - print @value, " " - @right.in_order if @right - end - - # show current value, - # then recurse on left child, - # then recurse on right child. - def pre_order - print @value, " " - @left.pre_order if @left - @right.pre_order if @right - end - - # recurse on left child, - # then recurse on right child, - # then show current value. - def post_order - @left.post_order if @left - @right.post_order if @right - print @value, " " - end - - # find out whether provided value is in the tree. - # if so, return the node at which the value was found. - # cut down search time by only looking in the correct - # branch, based on current value. - def search(val) - if val == @value then - return self - elsif val < @value then - return @left.search(val) if @left - return nil - else - return @right.search(val) if @right - return nil - end - end -end - -# first generate 20 random inserts -test = Binary_tree.new(0) -for a in 0..20 - test.insert(rand(1000)) -end - -# now dump out the tree all three ways -print "Pre order: "; test.pre_order; puts "" -print "In order: "; test.in_order; puts "" -print "Post order: "; test.post_order; puts "" - -print "search?" -while gets - print test.search($_.to_i) - print "\nsearch?" -end - - -# @@PLEAC@@_12.0 -# class and module names need to have the first letter capitalized -module Alpha - NAME = 'first' -end -module Omega - NAME = 'last' -end -puts "Alpha is #{Alpha::NAME}, Omega is #{Omega::NAME}" - -# ruby doesn't differentiate beteen compile-time and run-time -require 'getoptlong.rb' -require 'getoptlong' # assumes the .rb -require 'cards/poker.rb' -require 'cards/poker' # assumes the .rb -load 'cards/poker' # require only loads the file once - -module Cards - module Poker - @card_deck = Array.new # or @card_deck = [] - def shuffle - end - end -end - - -# @@PLEAC@@_12.1 -# a module exports all of its functions -module Your_Module - def self.function - # this would be called as Your_Module.function - end - - def Your_Module.another - # this is the same as above, but more specific - end -end - -# @@PLEAC@@_12.2 -begin - require 'nonexistent' -rescue LoadError - puts "Couldn't load #{$!}" # $! contains the last error string -end - -# @@PLEAC@@_12.4 -# module variables are private unless access functions are defined -module Alpha - @aa = 10 - @bb = 11 - - def self.put_aa - puts @aa - end - - def self.bb=(val) - @bb = val - end -end - -Alpha.bb = 12 -# Alpha.aa = 10 # error, no aa=method - - -# @@PLEAC@@_12.5 -# caller provides a backtrace of the call stack -module MyModule - def find_caller - caller - end - - def find_caller2(i) - caller(i) # an argument limits the size of the stack returned - end -end - - -# @@PLEAC@@_12.6 -BEGIN { - $logfile = '/tmp/mylog' unless defined? $logfile - $LF = File.open($logfile, 'a') -} - -module Logger - def self.logmsg(msg) - $LF.puts msg - end - - logmsg('startup') -end - -END { - Logger::logmsg('shutdown') - $LF.close -} - - -# @@PLEAC@@_12.7 -#----------------------------- -# results may be different on your system -# % ruby -e "$LOAD_PATH.each_index { |i| printf("%d %s\n", i, $LOAD_PATH[i] } -#0 /usr/local/lib/site_ruby/1.6 -#1 /usr/local/lib/site_ruby/1.6/i386-linux -#2 /usr/local/lib/site_ruby/ -#3 /usr/lib/ruby/1.6 -#4 /usr/lib/ruby/1.6/i136-linux -#5 . -#----------------------------- -# syntax for sh, bash, ksh, or zsh -#$ export RUBYLIB=$HOME/rubylib - -# syntax for csh or tcsh -# % setenv RUBYLIB ~/rubylib -#----------------------------- -$LOAD_PATH.unshift "/projects/spectre/lib"; - - -# @@PLEAC@@_12.8 -# equivalents in ruby are mkmf, SWIG, or Ruby/DL depending on usage - - -# @@PLEAC@@_12.9 -# no equivalent in ruby - - -# @@PLEAC@@_12.10 -# no equivalent in ruby - - -# @@PLEAC@@_12.11 -module FineTime - def self.time - # to be defined later - end -end - - -module FineTime - def self.time - "its a fine time" - end -end - -puts FineTime.time #=> "its a fine time" - - -# @@PLEAC@@_12.12 -def even_only(n) - raise "#{n} is not even" if (n & 1) != 0 # one way to test - # ... -end -def even_only(n) - $stderr.puts "#{n} is not even" if (n & 1) != 0 - # ... -end - - -# @@PLEAC@@_12.17 -# The library archive for ruby is called Ruby Application archive, -# or shorter RAA, and can be found at http://raa.ruby-lang.org. -# A typical library is installed like this: -# % gunzip some-module-4.54.tar.gz -# % tar xf some-module-4.54.tar -# % cd some-module-4.54.tar -# % ruby install.rb config -# % ruby install.rb setup -# get superuser previleges here if needed for next step -# % ruby install.rb install - -# Some modules use a different process, -# you should find details in the documentation -# Here is an example of such a different process -# % ruby extconf.rb -# % make -# % make install - -# If you want the module installed in your own directory: -# For ruby version specific libraries -# % ruby install.rb config --site-ruby=~/lib -# For version independent libraries -# % ruby install.rb config --site-ruby-common=~/lib - -# Information about possible options for config -# % ruby install.rb --help - -# If you have your own complete distribution -# % ruby install.rb --prefix=path=~/ruby-private - - -# @@PLEAC@@_13.0 -# Classes and objects in Ruby are rather straigthforward -class Person - # Class variables (also called static attributes) are prefixed by @@ - @@person_counter=0 - - # object constructor - def initialize(age, name, alive = true) # Default arg like in C++ - @age, @name, @alive = age, name, alive # Object attributes are prefixed by '@' - @@person_counter += 1 - # There is no '++' operator in Ruby. The '++'/'--' operators are in fact - # hidden assignments which affect variables, not objects. You cannot accomplish - # assignment via method. Since everything in Ruby is object, '++' and '--' - # contradict Ruby OO ideology. Instead '-=' and '+=' are used. - end - - attr_accessor :name, :age # This creates setter and getter methods for @name - # and @age. See 13.3 for detailes. - - # methods modifying the receiver object usually have the '!' suffix - def die! - @alive = false - puts "#{@name} has died at the age of #{@age}." - @alive - end - - def kill(anotherPerson) - print @name, ' is killing ', anotherPerson.name, ".\n" - anotherPerson.die! - end - - # methods used as queries - # usually have the '?' suffix - def alive? - @alive && true - end - - def year_of_birth - Time.now.year - @age - end - - # Class method (also called static method) - def Person.number_of_people - @@person_counter - end -end - -# Using the class: -# Create objects of class Person -lecter = Person.new(47, 'Hannibal') -starling = Person.new(29, 'Clarice', true) -pazzi = Person.new(40, 'Rinaldo', true) - -# Calling a class method -print "There are ", Person.number_of_people, " Person objects\n" - -print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n" -lecter.kill(pazzi) -print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n" - -print starling.name , ' was born in ', starling.year_of_birth, "\n" - - -# @@PLEAC@@_13.1 -# If you don't need any initialisation in the constructor, -# you don't need to write a constructor. -class MyClass -end - -class MyClass - def initialize - @start = Time.new - @age = 0 - end -end - -class MyClass - def initialize(inithash) - @start = Time.new - @age = 0 - for key, value in inithash - instance_variable_set("@#{key}", value) - end - end -end - -# @@PLEAC@@_13.2 -# Objects are destroyed by the garbage collector. -# The time of destroying is not predictable. -# The ruby garbage collector can handle circular references, -# so there is no need to write destructor for that. - -# There is no direct support for destructor. -# You can call a custom function, or more specific a proc object, when the -# garbage collector is about to destruct the object, but it is unpredictable -# when this occurs. -# Also if such a finalizer object has a reference to the orignal object, -# this may prevent the original object to get garbage collected. -# Because of this problem the finalize method below is -# a class method and not a instance method. -# So if you need to free resources for an object, like -# closing a socket or kill a spawned subprocess, -# you should do it explicitly. - -class MyClass - def initialize - ObjectSpace.define_finalizer(self, - self.class.method(:finalize).to_proc) - end - def MyClass.finalize(id) - puts "Object #{id} dying at #{Time.new}" - end -end - -# test code -3.times { - MyClass.new -} -ObjectSpace.garbage_collect - - -# @@PLEAC@@_13.3 -# You can write getter and setter methods in a natural way: -class Person - def name - @name - end - def name=(name) - @name = name - end -end - -# But there is a better and shorter way -class Person - attr_reader :age - attr_writer :name - # attr_reader and attr_writer are actually methods in class Class - # which set getter and setter methods for you. -end - -# There is also attr_accessor to create both setters and getters -class Person - attr_accessor :age, :name -end - - -# @@PLEAC@@_13.4 -class Person - # Class variables (also called static attributes) are prefixed by @@ - @@person_counter = 0 - - def Person.population - @@person_counter - end - def initialize - @@person_counter += 1 - ObjectSpace.define_finalizer(self, - self.class.method(:finalize).to_proc) - end - def Person.finalize(id) - @@person_counter -= 1 - end -end -people = [] -10.times { - people.push(Person.new) -} -printf("There are %d people alive", Person.population) - - -FixedArray.class_max_bounds = 100 -alpha = FixedArray.new -puts "Bound on alpha is #{alpha.max_bounds}" - -beta = FixedArray.new -beta.max_bounds = 50 # calls the instance method -beta.class.class_max_bounds = 50 # alternative, calls the class method -puts "Bound on alpha is #{alpha.max_bounds}" - -class FixedArray - @@bounds = 7 - - def max_bounds - @@max_bounds - end - # instance method, which sets the class variable - def max_bounds=(value) - @@max_bounds = value - end - # class method. This can only be called on a class, - # but not on the instances - def FixedArray.class_max_bounds=(value) - @@max_bounds = value - end -end - - -# @@PLEAC@@_13.5 -PersonStruct = Struct.new("Person", :name, :age, :peers) -# creates a class "Person::Struct", which is accessiable with the -# constant "PersonStruct" -p = PersonStruct.new -p = Struct::Person.new # alternative using the classname -p.name = "Jason Smythe" -p.age = 13 -p.peers = ["Wilbur", "Ralph", "Fred"] -p[:peers] = ["Wilbur", "Ralph", "Fred"] # alternative access using symbol -p["peers"] = ["Wilbur", "Ralph", "Fred"] # alternative access using name of field -p[2] = ["Wilbur", "Ralph", "Fred"] # alternative access using index of field -puts "At age #{p.age}, #{p.name}'s first friend is #{p.peers[0]}" - -# The fields of a struct have no special type, like other ruby variables -# you can put any objects in. Therefore the discussions how to specify -# the types of the fields do not apply to ruby. - -FamilyStruct = Struct.new("Family", :head, :address, :members) -folks = FamilyStruct.new -folks.head = PersonStruct.new -dad = folks.head -dad.name = "John" -dad.age = 34 - -# supply of own accessor method for the struct for error checking -class PersonStruct - def age=(value) - if !value.kind_of?(Integer) - raise(ArgumentError, "Age #{value} isn't an Integer") - elsif value > 150 - raise(ArgumentError, "Age #{value} is unreasonable") - end - @age = value - end -end - - -# @@PLEAC@@_13.6 -# The ruby Object class defines a dup and a clone method. -# The dup method is recommended for prototype object creation. -# The default implementation makes a shallow copy, -# but each class can override it, for example to make a deep copy. - -# If you want to call 'new' directly on the instances, -# you can create a instance method "new", which returns a new duplicate. -# This method is distinct from the class method new. -# -class A - def new - dup - end -end - -ob1 = A.new -# later on -ob2 = ob1.new - - -# @@PLEAC@@_13.7 -methname = 'flicker' -obj.send(methname, 10) # calls obj.flicker(10) - -# call three methods on the object, by name -['start', 'run', 'stop'].each do |method_string| - obj.send(method_string) -end - -# Another way is to create a Method object -method_obj = obj.method('flicker') -# And then call it -method_obj.call(10) - - -# @@PLEAC@@_13.8 -# All classes in Ruby inherit from class Object -# and thus all objects share methods defined in this class - -# the class of the object -puts any_object.type - -# Ruby classes are actually objects of class Class and they -# respond to methods defined in Object class as well - -# the superclass of this class -puts any_object.class.superclass - -# ask an object whether it is an instance of particular class -n = 4.7 -puts n.instance_of?(Float) # true -puts n.instance_of?(Numeric) # false - -# ask an object whether it is an instance of class, one of the -# superclasses of the object, or modules included in it -puts n.kind_of?(Float) # true (the class) -puts n.kind_of?(Numeric) # true (an ancestor class) -puts n.kind_of?(Comparable) # true (a mixin module) -puts n.kind_of?(String) # false - -# ask an object whether it can respond to a particular method -puts n.respond_to?('+') # true -puts n.respond_to?('length') # false - -# all methods an object can respond to -'just a string'.methods.each { |m| puts m } - - -# @@PLEAC@@_13.9 -# Actually any class in Ruby is inheritable -class Person - attr_accessor :age, :name - def initialize - @name - @age - end -end -#----------------------------- -dude = Person.new -dude.name = 'Jason' -dude.age = 23 -printf "%s is age %d.\n", dude.name, dude.age -#----------------------------- -# Inheriting from Person -class Employee < Person - attr_accessor :salary -end -#----------------------------- -empl = Employee.new -empl.name = 'Jason' -empl.age = 23 -empl.salary = 200 -printf "%s is age %d, the salary is %d.\n", empl.name, empl.age, empl.salary -#----------------------------- -# Any built-in class can be inherited the same way -class WeirdString < String - def initialize(obj) - super obj - end - def +(anotherObj) # + method in this class is overridden - # to return the sum of string lengths - self.length + anotherObj.length # 'self' can be omitted - end -end -#----------------------------- -a = WeirdString.new('hello') -b = WeirdString.new('bye') - -puts a + b # the overridden + -#=> 8 -puts a.length # method from the superclass, String -#=> 5 - - -# @@PLEAC@@_13.11 -# In ruby you can override the method_missing method -# to have a solution similar to perls AUTOLOAD. -class Person - - def initialize - @ok_fields = %w(name age peers parent) - end - - def valid_attribute?(name) - @ok_fields.include?(name) - end - - def method_missing(namesymbol, *params) - name = namesymbol.to_s - return if name =~ /^A-Z/ - if name.to_s[-1] == ('='[0]) # we have a setter - isSetter = true - name.sub!(/=$/, '') - end - if valid_attribute?(name) - if isSetter - instance_variable_set("@#{name}", *params) - else - instance_variable_get("@#{name}", *params) - end - else - # if no annestor is responsible, - # the Object class will throw a NoMethodError exception - super(namesymbol, *params) - end - end - - def new - kid = Person.new - kid.parent = self - kid - end - -end - -dad = Person.new -dad.name = "Jason" -dad.age = 23 -kid = dad.new -kid.name = "Rachel" -kid.age = 2 -puts "Kid's parent is #{kid.parent.name}" -puts dad -puts kid - -class Employee < Person - def initialize - super - @ok_fields.push("salary", "boss") - end - def ok_fields - @ok_fields - end -end - - -# @@PLEAC@@_13.13 -# The ruby garbage collector pretends to cope with circular structures. -# You can test it with this code: -class RingNode - attr_accessor :next - attr_accessor :prev - attr_reader :name - - def initialize(aName) - @name = aName - ObjectSpace.define_finalizer(self, - self.class.method(:finalize).to_proc) - end - - def RingNode.finalize(id) - puts "Node #{id} dying" - end - - def RingNode.show_all_objects - ObjectSpace.each_object {|id| - puts id.name if id.class == RingNode - } - end -end - -def create_test - a = RingNode.new("Node A") - b = RingNode.new("Node B") - c = RingNode.new("Node C") - a.next = b - b.next = c - c.next = a - a.prev = c - c.prev = b - b.prev = a - - a = nil - b = nil - c = nil -end - -create_test -RingNode.show_all_objects -ObjectSpace.garbage_collect -puts "After garbage collection" -RingNode.show_all_objects - - -# @@PLEAC@@_13.14 -class String - def <=>(other) - self.casecmp other - end -end - -# There is no way to directly overload the '""' (stringify) -# operator in Ruby. However, by convention, classes which -# can reasonably be converted to a String will define a -# 'to_s' method as in the TimeNumber class defined below. -# The 'puts' method will automatcally call an object's -# 'to_s' method as is demonstrated below. -# Furthermore, if a class defines a to_str method, an object of that -# class can be used most any place where the interpreter is looking -# for a String value. - -#--------------------------------------- -# NOTE: Ruby has a builtin Time class which would usually be used -# to manipulate time objects, the following is supplied for -# educational purposes to demonstrate operator overloading. -# -class TimeNumber - attr_accessor :hours,:minutes,:seconds - def initialize( hours, minutes, seconds) - @hours = hours - @minutes = minutes - @seconds = seconds - end - - def to_s - return sprintf( "%d:%02d:%02d", @hours, @minutes, @seconds) - end - - def to_str - to_s - end - - def +( other) - seconds = @seconds + other.seconds - minutes = @minutes + other.minutes - hours = @hours + other.hours - if seconds >= 60 - seconds %= 60 - minutes += 1 - end - if minutes >= 60 - minutes %= 60 - hours += 1 - end - return TimeNumber.new(hours, minutes, seconds) - end - - def -(other) - raise NotImplementedError - end - - def *(other) - raise NotImplementedError - end - - def /( other) - raise NotImplementedError - end -end - -t1 = TimeNumber.new(0, 58, 59) -sec = TimeNumber.new(0, 0, 1) -min = TimeNumber.new(0, 1, 0) -puts t1 + sec + min + min - -#----------------------------- -# StrNum class example: Ruby's builtin String class already has the -# capabilities outlined in StrNum Perl example, however the '*' operator -# on Ruby's String class acts differently: It creates a string which -# is the original string repeated N times. -# -# Using Ruby's String class as is in this example: -x = "Red"; y = "Black" -z = x+y -r = z*3 # r is "RedBlackRedBlackRedBlack" -puts "values are #{x}, #{y}, #{z}, and #{r}" -print "#{x} is ", x < y ? "LT" : "GE", " #{y}\n" -# prints: -# values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack -# Red is GE Black - -#----------------------------- -class FixNum - REGEX = /(\.\d*)/ - DEFAULT_PLACES = 0 - attr_accessor :value, :places - def initialize(value, places = nil) - @value = value - if places - @places = places - else - m = REGEX.match(value.to_s) - if m - @places = m[0].length - 1 - else - @places = DEFAULT_PLACES - end - end - end - - def +(other) - FixNum.new(@value + other.value, max(@places, other.places)) - end - - def *(other) - FixNum.new(@value * other.value, max(@places, other.places)) - end - - def /(other) - puts "Divide: #{@value.to_f/other.value.to_f}" - result = FixNum.new(@value.to_f/other.value.to_f) - result.places = max(result.places,other.places) - result - end - - def to_s - sprintf("STR%s: %.*f", self.class.to_s , @places, @value) #. - end - - def to_str - to_s - end - - def to_i #convert to int - @value.to_i - end - - def to_f #convert to float` - @value.to_f - end - - private - def max(a,b) - a > b ? a : b - end -end - -def demo() - x = FixNum.new(40) - y = FixNum.new(12, 0) - - puts "sum of #{x} and #{y} is #{x+y}" - puts "product of #{x} and #{y} is #{x*y}" - - z = x/y - puts "#{z} has #{z.places} places" - unless z.places - z.places = 2 - end - - puts "div of #{x} by #{y} is #{z}" - puts "square of that is #{z*z}" -end - -if __FILE__ == $0 - demo() -end - - -# @@PLEAC@@_14.1 -# There are dbm, sdbm, gdbm modules -# and the bdb module for accessing the berkeley db -# sdbm seem to be available on the most systems, -# so we use it here -# -require "sdbm" -SDBM.open("filename", 0666) { |dbobj| - # raises exception if open error - - # the returned sdbm-dbobj has most of the methods of a hash - v = dbobj["key"] - dbobj["key"] = "newvalue" - if dbobj.has_key?("key") - # ... - end - dbobj.delete("key2") -} -# database is open only inside the block. - -# It is also possible to use a open .. close pair: -dbobj = SDBM.open("filename", 0666) -#.. do something with dbobj -dbobj.close - -#!/usr/bin/ruby -w -# userstats - generate statistics on who is logged in -# call with usernames as argument to display the totals -# for the given usernames, call with "ALL" to display all users - -require "sdbm" -filename = '/tmp/userstats.db' -SDBM.open(filename, 0666) { |dbobj| - if ARGV.length > 0 - if ARGV[0] == "ALL" - # ARGV is constant, so we need the variable userlist - userlist = dbobj.keys().sort() - else - userlist = ARGV - end - userlist.each { |user| - print "#{user}\t#{dbobj[user]}\n" - } - else - who = `who` - who.split("\n").each { |line| - md = /^(\S+)/.match(line) - raise "Bad line from who: #{line}" unless md - # sdbm stores only strings, so "+=" doesn't work, - # we need to convert them expicitly back to integer. - if dbobj.has_key?(md[0]) - dbobj[md[0]] = dbobj[md[0]].to_i + 1 - else - dbobj[md[0]] = "1" - end - } - end -} - - -# @@PLEAC@@_14.2 -# using open and clear -dbobj = SDBM.open("filename", 0666) -dbobj.clear() -dbobj.close() -# deleting file and recreating it -# the filenames depend on the flavor of dbm you use, -# for example sdbm has two files named filename.pag and filename.dir, -# so you need to delete both files -begin - File.delete("filename") - # raises Exception if not exist - dbobj = SDBM.open("filename", 0666) -rescue - # add error handling here -end - - -# @@PLEAC@@_14.3 -# sdbm2gdbm: converts sdbm database to a gdbm database -require "sdbm" -require "gdbm" - -unless ARGV.length == 2 - fail "usage: sdbm2gdbm infile outfile" -end -infile = ARGV[0] -outfile = ARGV[1] - -sdb = SDBM.open(infile) -gdb = GDBM.open(outfile, 0666) -sdb.each { |key, val| - gdb[key] = val -} -gdb.close -sdb.close - - -# @@PLEAC@@_14.4 -#!/usr/bin/ruby -w -# dbmmerge: merges two dbm databases -require "sdbm" - -unless ARGV.length == 3 - fail "usage: dbmmerge indb1 indb2 outdb" -end -infile1 = ARGV[0] -infile2 = ARGV[0] -outfile = ARGV[2] - -in1 = SDBM.open(infile1, nil) -in2 = SDBM.open(infile2, nil) -outdb = SDBM.open(outfile, 0666) - -[in1, in2].each { |indb| - indb.each { |key, val| - if outdb.has_key?(key) - # decide which value to set. - # set outdb[key] if necessary - else - outdb[key] = val - end - } -} -in1.close -in2.close -outdb.close - - -# @@PLEAC@@_14.7 -# we write a tie method that extends the Array class. -# It reads the file into the memory, executes the code block -# in which you can manipulate the array as needed, and writes -# the array back to the file after the end of the block execution -class Array - def tie(filename, flags) - File.open(filename, flags) { |f| - f.each_line { |line| - self.push(line.chomp) - } - yield - f.rewind - each { |line| - if line - f.puts(line) - else - f.puts "" - end - } - } - end -end - -array = Array.new -array.tie("/tmp/textfile.txt", File::RDWR|File::CREAT) { - array[4] = "a new line 4" -} - -# The tied array can be manipulated like a normal array, -# so there is no need for a special API, and the recno_demo program -# to demonstrate is API is useless - - -# tied array demo: show how to use array with a tied file -filename = "db_file.txt" -lines = Array.new -File.unlink(filename) if File.exists?(filename) -lines.tie(filename, File::RDWR | File::CREAT) { - # first create a textfile to play with - lines[0] = "zero" - lines[1] = "one" - lines[2] = "two" - lines[3] = "three" - lines[4] = "four" - - # print the records in order. - # Opposed to perl, the tied array behaves exactly as a normal array - puts "\nOriginal" - for i in 0..(lines.length-1) - puts "#{i}: #{lines[i]}" - end - - #use push and pop - a = lines.pop - lines.push("last") - puts("The last line was [#{a}]") - - #use shift and unshift - a = lines.shift - lines.unshift("first") - puts("The first line was [#{a}]") - - # add record after record 2 - i = 2 - lines.insert(i + 1, "Newbie") - - # add record before record one - i = 1 - lines.insert(i, "New One") - - # delete record 3 - lines.delete_at(3) - - #now print the records in reverse order - puts "\nReverse" - (lines.length - 1).downto(0){ |i| - puts "#{i}: #{lines[i]}" - } - -} - - -# @@PLEAC@@_14.8 -# example to store complex data in a database -# uses marshall from the standard library -require "sdbm" -db = SDBM.open("pleac14-8-database", 0666) - -# convert the Objects into strings and back by using the Marshal module. -# Most normal objects can be converted out of the box, -# but not special things like procedure objects, -# IO instance variables, singleton objects - -db["Tom Christiansen"] = Marshal.dump(["book author", "tchrist@perl.com"]) -db["Tom Boutell"] = Marshal.dump(["shareware author", -"boutell@boutell.com"]) - -name1 = "Tom Christiansen" -name2 = "Tom Boutell" - -tom1 = Marshal.load(db[name1]) -tom2 = Marshal.load(db[name2]) - -puts "Two Toming: #{tom1} #{tom2}" - -if tom1[0] == tom2[0] && tom1[1] == tom2[1] - puts "You're having runtime fun with one Tom made two." -else - puts "No two Toms are ever alike" -end - -# To change parts of an entry, get the whole entry, change the parts, -# and save the whole entry back -entry = Marshal.load(db["Tom Boutell"]) -entry[0] = "Poet Programmer" -db["Tom Boutell"] = Marshal.dump(entry) -db.close - - -# @@PLEAC@@_14.9 -# example to make data persistent -# uses Marshal from the standard lib -# Stores the data in a simple file, -# see 14.8 on how to store it in a dbm file - -# The BEGIN block is executed before the rest of the script -# we use global variables here because local variables -# will go out of scope and are not accessible from the main script - -BEGIN { - $persistent_store = "persitence.dat" - begin - File.open($persistent_store) do |f| - $stringvariable1 = Marshal.load(f) - $arrayvariable2 = Marshal.load(f) - end - rescue - puts "Can not open #{$persistent_store}" - # Initialisation if this script runs the first time - $stringvariable1 = "" - $arrayvariable2 = [] - end -} - -END { - File.open($persistent_store, "w+") do |f| - Marshal.dump($stringvariable1, f) - Marshal.dump($arrayvariable2, f) - end -} - -# simple test program -puts $stringvariable1 -puts $arrayvariable2 -$stringvariable1 = "Hello World" -$arrayvariable2.push(5) -puts $stringvariable1 -puts $arrayvariable2 - - -# @@PLEAC@@_14.10 -#!/usr/bin/ruby -w -# Ruby has a dbi module with an architecture similar -# to the Perl dbi module: the dbi module provides an unified -# interface and uses specialized drivers for each dbms vendor -# -begin - DBI.connect("DBI:driver:driverspecific", "username", "auth") { - |dbh| - - dbh.do(SQL1) - - dbh.prepare(SQL2){ |sth| - sth.execute - sth.fetch {|row| - # ... - } - } # end of block finishes the statement handle - } # end of block closes the database connection -rescue DBI::DatabaseError => e - puts "dbi error occurred" - puts "Error code: #{e.err}" - puts "Error message: #{e.errstr}" -end - -#!/usr/bin/ruby -w -# dbusers - example for mysql which creates a table, -# fills it with values, retrieves the values back, -# and finally destroys the table. - -require "dbi" - -# replacement for the User::pwnt module -def getpwent - result = [] - File.open("/etc/passwd") {|file| - file.each_line {|line| - next if line.match(/^#/) - cols = line.split(":") - result.push([cols[2], cols[0]]) - } - } - result -end - -begin - DBI.connect("DBI:Mysql:pleacdatabase", "pleac", "pleacpassword") { - |conn| - - conn.do("CREATE TABLE users (uid INT, login CHAR(8))") - - users = getpwent - - conn.prepare("INSERT INTO users VALUES (?,?)") {|sth| - users.each {|entry| - sth.execute(entry[0], entry[1]) - } - } - - conn.execute("SELECT uid, login FROM users WHERE uid < 50") {|sth| - sth.fetch {|row| - puts row.collect {|col| - if col.nil? - "(null)" - else - col - end - }.join(", ") - } - } - - conn.do("DROP TABLE users") - } -rescue DBI::DatabaseError => e - puts "dbi error occurred" - puts "Error code: #{e.err}" - puts "Error message: #{e.errstr}" -end - - -# @@PLEAC@@_15.1 -# This test program demonstrates parsing program arguments. -# It uses the optparse library, which is included with ruby 1.8 -# It handles classic unix style and gnu style options -require 'optparse' - -@debugmode = false -@verbose = false - -ARGV.options do |opts| - opts.banner = "Usage: ruby #{$0} [OPTIONS] INPUTFILES" - - opts.on("-h", "--help", "show this message") { - puts opts - exit - } - # The OptionParser#on method is called with a specification of short - # options, of long options, a data type spezification and user help - # messages for this option. - # The method analyses the given parameter and decides what it is, - # so you can leave out the long option if you don't need it - opts.on("-v", "--[no-]verbose=[FLAG]", TrueClass, "run verbosly") { - |@verbose| # sets @verbose to true or false - } - opts.on("-D", "--DEBUG", TrueClass, "turns on debug mode" ){ - |@debugmode| # sets @debugmode to true - } - opts.on("-c", "--count=NUMBER", Integer, "how many times we do it" ){ - |@count| # sets @count to given integer - } - opts.on("-o", "--output=FILE", String, "file to write output to"){ - |@outputfile| # sets @outputfile to given string - } - opts.parse! -end - -# example to use the options in the main program -puts "Verbose is on" if @verbose -puts "Debugmode is on" if @debugmode -puts "Outfile is #{@outputfile}" if defined? @outputfile -puts "Count is #{@count}" if defined? @count -ARGV.each { |param| - puts "Got parameter #{param}" -} - - -# @@PLEAC@@_15.4 -buf = "\0" * 8 -$stdout.ioctl(0x5413, buf) -ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("S4") - -raise "You must have at least 20 characters" unless ws_col >= 20 -max = 0 -values = (1..5).collect { rand(20) } # generate an array[5] of rand values -for i in values - max = i if max < i -end -ratio = Float(ws_col-12)/max # chars per unit -for i in values - printf "%8.1f %s\n", i, "*" * (ratio*i) -end - -# gives, for example: -# 15.0 ******************************* -# 10.0 ********************* -# 5.0 ********** -# 14.0 ***************************** -# 18.0 ************************************** - - -# @@PLEAC@@_16.1 -output = `program args` # collect output into one multiline string -output = `program args`.split # collect output into array, one line per -element - -readme = IO.popen("ls") -output = "" -while readme.gets do - output += $_ -end -readme.close - -`fsck -y /dev/rsd1a` # BAD AND SCARY in Perl because it's managed by the shell - # I donna in Ruby ... - -# so the "clean and secure" version -readme, writeme = IO.pipe -pid = fork { - # child - $stdout = writeme - readme.close - exec('find', '..') -} -# parent -Process.waitpid(pid, 0) -writeme.close -while readme.gets do - # do something with $_ -end - - -# @@PLEAC@@_16.2 -status = system("xemacs #{myfile}") - -status = system("xemacs", myfile) - -system("cmd1 args | cmd2 | cmd3 >outfile") -system("cmd args <infile >outfile 2>errfile") - -# stop if the command fails -raise "$program exited funny: #{$?}" unless system("cmd", "args1", "args2") - -# get the value of the signal sent to the child -# even if it is a SIGINT or SIGQUIT -system(arglist) -raise "program killed by signal #{$?}" if ($? & 127) != 0 - -pid = fork { - trap("SIGINT", "IGNORE") - exec("sleep", "10") -} -trap ("SIGINT") { - puts "Tsk tsk, no process interruptus" -} -Process.waitpid(pid, 0) - -# Ruby doesn't permit to lie to the program called by a 'system'. -# (ie specify what return argv[0] in C, $0 in Perl/Ruby ...) -# A (dirty) way is to create a link (under Unix), run this link and -# erase it. Somebody has a best idea ? - - -# @@PLEAC@@_16.3 -exec("archive *.data") - -exec("archive", "accounting.data") - -exec("archive accounting.data") - - -# @@PLEAC@@_16.4 -# read the output of a program -IO.popen("ls") {|readme| - while readme.gets do - # ... - end -} -# or -readme = IO.popen("ls") -while readme.gets do - # ... -end -readme.close - -# "write" in a program -IO.popen("cmd args","w") {|pipe| - pipe.puts("data") - pipe.puts("foo") -} - -# close wait for the end of the process -read = IO.popen("sleep 10000") # child goes to sleep -read.close # and the parent goes to lala land - -writeme = IO.popen("cmd args", "w") -writeme.puts "hello" # program will get hello\n on STDIN -writeme.close # program will get EOF on STDIN - -# send in a pager (eg less) all output -$stdout = IO.popen("/usr/bin/less","w") -print "huge string\n" * 10000 - - -# @@PLEAC@@_16.5 -#----------------------------- -def head(lines = 20) - pid = open("|-","w") - if pid == nil - return - else - while gets() do - pid.print - lines -= 1 - break if lines == 0 - end - end - exit -end - -head(100) -while gets() do - print -end -#----------------------------- -1: > Welcome to Linux, version 2.0.33 on a i686 - -2: > - -3: > "The software required `Windows 95 or better', - -4: > so I installed Linux." -#----------------------------- -> 1: Welcome to Linux, Kernel version 2.0.33 on a i686 - -> 2: - -> 3: "The software required `Windows 95 or better', - -> 4: so I installed Linux." -#----------------------------- -#!/usr/bin/ruby -# qnumcat - demo additive output filters - -def number() - pid = open("|-","w") - if pid == nil - return - else - while gets() do pid.printf("%d: %s", $., $_); end - end - exit -end - -def quote() - pid = open("|-","w") - if pid == nil - return - else - while gets() do pid.print "> #{$_}" end - end - exit -end - -number() -quote() - -while gets() do - print -end -$stdout.close -exit - - -# @@PLEAC@@_16.6 -ARGV.map! { |arg| - arg =~ /\.(gz|Z)$/ ? "|gzip -dc #{arg}" : arg -} -for file in ARGV - fh = open(file) - while fh.gets() do - # ....... - end -end -#----------------------------- -ARGV.map! { |arg| - arg =~ %r#^\w+://# ? "|GET #{arg}" : arg # -} -for file in ARGV - fh = open(file) - while fh.gets() do - # ....... - end -end -#----------------------------- -pwdinfo = (`domainname` =~ /^(\(none\))?$/) ? '/etc/passwd' : '|ypcat passwd'; -pwd = open(pwdinfo); -#----------------------------- -puts "File, please? "; -file = gets().chomp(); -fh = open(file); - - -# @@PLEAC@@_16.7 -output = `cmd 2>&1` # with backticks -# or -ph = open("|cmd 2>&1") # with an open pipe -while ph.gets() { } # plus a read -#----------------------------- -output = `cmd 2>/dev/null` # with backticks -# or -ph = open("|cmd 2>/dev/null") # with an open pipe -while ph.gets() { } # plus a read -#----------------------------- -output = `cmd 2>&1 1>/dev/null` # with backticks -# or -ph = open("|cmd 2>&1 1>/dev/null") # with an open pipe -while ph.gets() { } # plus a read -#----------------------------- -output = `cmd 3>&1 1>&2 2>&3 3>&-` # with backticks -# or -ph = open("|cmd 3>&1 1>&2 2>&3 3>&-") # with an open pipe -while ph.gets() { } # plus a read -#----------------------------- -system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr") -#----------------------------- -output = `cmd 3>&1 1>&2 2>&3 3>&-` -#----------------------------- -fd3 = fd1 -fd1 = fd2 -fd2 = fd3 -fd3 = undef -#----------------------------- -system("prog args 1>tmpfile 2>&1") -system("prog args 2>&1 1>tmpfile") -#----------------------------- -# system ("prog args 1>tmpfile 2>&1") -fd1 = "tmpfile" # change stdout destination first -fd2 = fd1 # now point stderr there, too -#----------------------------- -# system("prog args 2>&1 1>tmpfile") -fd2 = fd1 # stderr same destination as stdout -fd1 = "tmpfile" # but change stdout destination -#----------------------------- -# It is often better not to rely on the shell, -# because of portability, possible security problems -# and bigger resource usage. So, it is often better to use the open3 library. -# See below for an example. -# opening stdin, stdout, stderr -require "open3" -stdin, stdout, stderr = Open3.popen('cmd') - - -# @@PLEAC@@_16.8 -#----------------------------- -# Contrary to perl, we don't need to use a module in Ruby -fh = Kernel.open("|" + program, "w+") -fh.puts "here's your input\n" -output = fh.gets() -fh.close() -#----------------------------- -Kernel.open("|program"),"w+") # RIGHT ! -#----------------------------- -# Ruby has already object methods for I/O handles -#----------------------------- -begin - fh = Kernel.open("|" + program_and_options, "w+") -rescue - if ($@ ~= /^open/) - $stderr.puts "open failed : #{$!} \n #{$@} \n" - break - end - raise # reraise unforseen exception -end - - -# @@PLEAC@@_16.13 -#% kill -l -#HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE -#ALRM TERM CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM -#PROF WINCH POLL PWR -#----------------------------- -#% ruby -e 'puts Signal.list.keys.join(" ")' -#PWR USR1 BUS USR2 TERM SEGV KILL POLL STOP SYS TRAP IOT HUP INT # -#WINCH XCPU TTIN CLD TSTP FPE IO TTOU PROF CHLD CONT PIPE ABRT -#VTALRM QUIT ILL XFSZ URG ALRM -#----------------------------- -# After that, the perl script create an hash equivalent to Signal.list, -# and an array. The array can be obtained by : -signame = [] -Signal.list.each { |name, i| signame[i] = name } - - -# @@PLEAC@@_16.14 -Process.kill(9, pid) # send $pid a signal 9 -Process.kill(-1, Process.getpgrp()) # send whole job a signal 1 -Process.kill("USR1", $$) # send myself a SIGUSR1 -Process.kill("HUP", pid1, pid2, pid3) # send a SIGHUP to processes in @pids -#----------------------------- -begin - Process.kill(0, minion) - puts "#{minion} is alive!" -rescue Errno::EPERM # changed uid - puts "#{minion} has escaped my control!"; -rescue Errno::ESRCH - puts "#{minion} is deceased."; # or zombied -rescue - puts "Odd; I couldn't check the status of #{minion} : #{$!}" -end - - -# @@PLEAC@@_16.15 -Kernel.trap("QUIT", got_sig_quit) # got_sig_quit = Proc.new { puts "Quit\n" } -trap("PIPE", "got_sig_quit") # def got_sig_pipe ... -trap("INT") { ouch++ } # increment ouch for every SIGINT -#----------------------------- -trap("INT", "IGNORE") # ignore the signal INT -#----------------------------- -trap("STOP", "DEFAULT") # restore default STOP signal handling - - -# @@PLEAC@@_16.16 -# the signal handler -def ding - trap("INT", "ding") - puts "\aEnter your name!" -end - -# prompt for name, overriding SIGINT -def get_name - save = trap("INT", "ding") - - puts "Kindly Stranger, please enter your name: " - name = gets().chomp() - trap("INT", save) - name -end - - -# @@PLEAC@@_16.21 -# implemented thanks to http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/1760 -require 'timeout' - -# we'll do something vastly more useful than cookbook to demonstrate timeouts -begin - timeout(5) { - waitsec = rand(10) - puts "Let's see if a sleep of #{waitsec} seconds is longer than 5 seconds..." - system("sleep #{waitsec}") - } - puts "Timeout didn't occur" -rescue Timeout::Error - puts "Timed out!" -end - - -# @@PLEAC@@_17.1 -# A basic TCP client connection -require 'socket' -begin - t = TCPSocket.new('www.ruby-lang.org', 'www') -rescue - puts "error: #{$!}" -else - # ... do something with the socket - t.print "GET / HTTP/1.0\n\n" - answer = t.gets(nil) - # and terminate the connection when we're done - t.close -end - -# Using the evil low level socket API -require 'socket' -# create a socket -s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) -# build the address of the remote machine -sockaddr_server = [Socket::AF_INET, 80, - Socket.gethostbyname('www.ruby-lang.org')[3], - 0, 0].pack("snA4NN") -# connect -begin - s.connect(sockaddr_server) -rescue - puts "error: #{$!}" -else - # ... do something with the socket - s.print "GET / HTTP/1.0\n\n" - # and terminate the connection when we're done - s.close -end - -# TCP connection with management of error (DNS) -require 'socket' -begin - client = TCPSocket.new('does not exists', 'www') -rescue - puts "error: #{$!}" -end - -# TCP connection with a time out -require 'socket' -require 'timeout' -begin - timeout(1) do #the server has one second to answer - client = TCPSocket.new('www.host.com', 'www') - end -rescue - puts "error: #{$!}" -end - - -# @@PLEAC@@_17.12 -require 'socket' - -class Preforker - attr_reader (:child_count) - - def initialize(prefork, max_clients_per_child, port, client_handler) - @prefork = prefork - @max_clients_per_child = max_clients_per_child - @port = port - @child_count = 0 - - @reaper = proc { - trap('CHLD', @reaper) - pid = Process.wait - @child_count -= 1 - } - - @huntsman = proc { - trap('CHLD', 'IGNORE') - trap('INT', 'IGNORE') - Process.kill('INT', 0) - exit - } - - @client_handler=client_handler - end - - def child_handler - trap('INT', 'EXIT') - @client_handler.setUp - # wish: sigprocmask UNblock SIGINT - @max_clients_per_child.times { - client = @server.accept or break - @client_handler.handle_request(client) - client.close - } - @client_handler.tearDown - end - - def make_new_child - # wish: sigprocmask block SIGINT - @child_count += 1 - pid = fork do - child_handler - end - # wish: sigprocmask UNblock SIGINT - end - - def run - @server = TCPserver.open(@port) - trap('CHLD', @reaper) - trap('INT', @huntsman) - loop { - (@prefork - @child_count).times { |i| - make_new_child - } - sleep .1 - } - end -end - -#----------------------------- -#!/usr/bin/ruby - -require 'Preforker' - -class ClientHandler - def setUp - end - - def tearDown - end - - def handle_request(client) - # do stuff - end -end - -server = Preforker.new(1, 100, 3102, ClientHandler.new) -server.run - - -# @@PLEAC@@_18.2 -require 'net/ftp' - -begin - ftp = Net::FTP::new("ftp.host.com") - ftp.login(username,password) - ftp.chdir(directory) - ftp.get(filename) - ftp.put(filename) -rescue Net::FTPError - $stderr.print "FTP failed: " + $! -ensure - ftp.close() if ftp -end - -# A better solution for a local use could be : -Net::FTP::new("ftp.host.com") do |ftp| - ftp.login(username,password) - ftp.chdir(directory) - ftp.get(filename) - ftp.put(filename) -end - -# If you have only one file to get, there is a simple solution : -require 'open-uri' -open("ftp://www.ruby-lang.org/path/filename") do |fh| - # read from filehandle fh -end -#-------------------------------------------- -# to wait a defined time for the connection, -# use the timeout module -require 'timeout' -begin - timeout(30){ - ftp = Net::FTP::new("ftp.host.com") - ftp.debug_mode = true - } -rescue Net::FTPError - $stderr.puts "Couldn't connect." -rescue Timeout::Error - $stderr.puts "Timeout while connecting to server." -end - -begin - ftp.login() -rescue Net::FTPError - $stderr.print "Couldn't authentificate.\n" -end - -begin - ftp.login(username) -rescue Net::FTPError - $stderr.print "Still couldn't authenticate.\n" -end - -begin - ftp.login(username, password) -rescue Net::FTPError - $stderr.print "Couldn't authenticate, even with explicit - username and password.\n" -end - -begin - ftp.login(username, password, account) -rescue Net::FTPError - $stderr.print "No dice. It hates me.\n" -end -#----------------------------- -ftp.put(localfile, remotefile) -#----------------------------- -# Sending data from STDIN is not directly supported -# by the ftp library module. A possible way to do it is to use the -# storlines method directly to send raw commands to the ftp server. -#----------------------------- -ftp.get(remotefile, localfile) -#----------------------------- -ftp.get(remotefile) { |data| puts data } -#----------------------------- -ftp.chdir("/pub/ruby") -print "I'm in the directory ", ftp.pwd(), "\n" -#----------------------------- -ftp.mkdir("/pub/ruby/new_dir") -#----------------------------- -lines = ftp.ls("/pub/ruby/") -# => ["drwxr-xr-x 2 matz users 4096 July 17 1998 1.0", ... ] - -latest = ftp.dir("/pub/ruby/*.tgz").sort.last - -ftp.nlst("/pub/ruby") -# => ["/pub/ruby/1.0", ... ] -#----------------------------- -ftp.quit() - - -# @@PLEAC@@_18.6 -require 'net/telnet' -t = Net::Telnet::new( "Timeout" => 10, - "Prompt" => /%/, - "Host" => host ) -t.login(username, password) -files = t.cmd("ls") -t.print("top") -process_string = t.waitfor(/\d+ processes/) -t.close -#----------------------------- -/[$%#>] \z/n -#----------------------------- -# In case of an error, the telnet module throws an exception. -# For control of the behavior in case of an error, -# you just need to catch the exceptions and do your custom -# error handling. -#----------------------------- -begin - telnet.login(username, password) -rescue TimeoutError - fail "Login failed !\n" -end -#----------------------------- -telnet.waitfor('/--more--/') -#----------------------------- -telnet.waitfor(String => 'greasy smoke', Timeout => 30) - - -# @@PLEAC@@_18.7 -require 'ping' - -puts "#{host} is alive.\n" if Ping.pingecho(host); -#----------------------------- -# the ping module only use TCP ping, not ICMP even if we are root -if Ping.pingecho("kingkong.com") - puts "The giant ape lives!\n"; -else - puts "All hail mighty Gamera, friend of children!\n"; -end - - -# @@PLEAC@@_19.1 -#!/usr/local/bin/ruby -w -# hiweb - load CGI class to decode information given by web server - -require 'cgi' - -cgi = CGI.new('html3') - -# get a parameter from a form -value = cgi.params['PARAM_NAME'][0] - -# output a document -cgi.out { - cgi.html { - cgi.head { cgi.title { "Howdy there!" } } + - cgi.body { cgi.p { "You typed: " + cgi.tt { - CGI.escapeHTML(value) } } } - } -} - -require 'cgi' -cgi = CGI.new -who = cgi.param["Name"][0] # first param in list -phone = cgi.param["Number"][0] -picks = cgi.param["Choices"] # complete list - -print cgi.header( 'type' => 'text/plain', - 'expires' => Time.now + (3 * 24 * 60 * 60) ) - - -# @@PLEAC@@_19.3 -#!/usr/local/bin/ruby -w -# webwhoami - show web user's id -require 'etc' -print "Content-Type: text/plain\n\n" -print "Running as " + Etc.getpwuid.name + "\n" - -# % ruby -wc cgi-script # just check syntax - -# % ruby -w cgi-script # params from stdin -# (offline mode: enter name=value pairs on standard input) -# name=joe -# number=10 -# ^D - -# % ruby -w cgi-script name=joe number=10 # run with mock form input -# % ruby -d cgi-script name=joe number=10 # ditto, under the debugger - -# POST method script in csh -# % (setenv HTTP_METHOD POST; ruby -w cgi-script name=joe number=10) -# POST method script in sh -# % HTTP_METHOD=POST perl -w cgi-script name=joe number=10 - - -# @@PLEAC@@_19.4 -# ruby has several security levels, the level "1" is similar to perls taint mode. -# It can be switched on by providing the -T command line parameter -# or by setting $SAFE to 1. Setting $SAFE to 2,3 or 4 restricts possible -# harmful operations further. - -#!/usr/bin/ruby -T -$SAFE = 1 -File.open(ARGV[0], "w") -# ruby warns with: -# taint1.rb:2:in `initialize': Insecure operation - initialize (SecurityError) - -$SAFE = 1 -file = ARGV[0] -unless /^([\w.-]+)$/.match(file) - raise "filename #{file} has invalid characters" -end -file = $1 -# In ruby, even the back reference from a regular expression stays tainted. -# you need to explicitly untaint the variable: -file.untaint -File.open(file, "w") - -# Race condition exists like in perl: -unless File.exists(filename) # Wrong because of race condition - File.open(filename, "w") -end - - - -# @@PLEAC@@_19.10 -preference_value = cgi.cookies["preference name"][0] - -packed_cookie = CGI::Cookie.new("name" => "preference name", - "value" => "whatever you'd like", - "expires" => Time.local(Time.now.year + 2, - Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) ) - -cgi.header("cookie" => [packed_cookie]) - -#!/usr/local/bin/ruby -w -# ic_cookies - sample CGI script that uses a cookie -require 'cgi' - -cgi = CGI.new('html3') - -cookname = "favorite ice cream" -favorite = cgi.params["flavor"][0] -tasty = cgi.cookies[cookname][0] || 'mint' - -unless favorite - cgi.out { - cgi.html { - cgi.head { cgi.title { "Ice Cookies" } } + - cgi.body { - cgi.h1 { "Hello Ice Cream" } + - cgi.hr + - cgi.form { - cgi.p { "Please select a flavor: " + - cgi.text_field("flavor", tasty ) } - } + - cgi.hr - } - } - } -else - cookie = CGI::Cookie.new( "name" => cookname, - "value" => favorite, - "expires" => Time.local(Time.now.year + 2, -Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) ) - cgi.out("cookie" => [cookie]) { - cgi.html { - cgi.head { cgi.title { "Ice Cookies" } } + - cgi.body { - cgi.h1 { "Hello Ice Cream" } + - cgi.p { "You chose as your favorite flavor `#{favorite}'." } - } - } - } -end - - -# @@PLEAC@@_20.9 -def templatefile(filename, fillings) - aFile = File.new(filename, "r") - text = aFile.read() - aFile.close() - pattern = Regexp.new('%%(.*?)%%') - text.gsub!(pattern) { - fillings[$1] || "" - } - text -end - -fields = { - 'username' => whats_his_name, - 'count' => login_count, - 'total' => minutes_used -} -puts templatefile('simple.template', fields) - -# @@INCOMPLETE@@ -# An example using databases is missing - |