<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6088262555941383716</id><updated>2012-02-14T14:16:17.600-05:00</updated><category term='ruby'/><category term='linux'/><category term='doxygen'/><category term='javascript'/><category term='c/c++'/><category term='disasm'/><category term='programming'/><category term='r'/><category term='sip'/><category term='open source'/><category term='book'/><category term='osx'/><category term='rattle'/><category term='kde'/><category term='x1'/><category term='e17'/><category term='mvc'/><category term='regex'/><category term='firefox'/><category term='praat'/><category term='tcp/ip'/><category term='passenger'/><category term='e70'/><category term='git'/><category term='python'/><category term='shell'/><category term='words'/><category term='Google Go'/><category term='r500'/><category term='rails'/><category term='software engineering'/><category term='kernel'/><category term='binary analysis'/><category term='sinatra'/><category term='gcc'/><category term='grub2'/><category term='vim'/><category term='jruby'/><category term='qt'/><category term='macports'/><category term='ubuntu'/><category term='upgrade rage'/><category term='qmake'/><category term='database'/><title type='text'>The Sorry Scheme of Things Entire</title><subtitle type='html'>Dissatisfaction with the state of the art.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>75</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-7989848419525100572</id><published>2012-02-14T14:16:00.000-05:00</published><updated>2012-02-14T14:16:17.615-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Ruby: Make any program a Quine</title><content type='html'>One of the amusing diversions available to writers of compiled-language programs was the challenged of writing a &lt;i&gt;quine&lt;/i&gt; (after W.V.O., of course): a program which, when executed, prints its own source code.&lt;br /&gt;&lt;br /&gt;Attempts in C range with the ugly but obvious (repeating the source code as a string, and essentially printing the string twice) to the downright &lt;a href="http://www.nyx.net/~gthompso/self_c.txt"&gt;obfuscated&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Things are different in dynamic languages, of course, and there's a reason why this problem was never a RubyQuiz (UPDATE: it &lt;a href="http://rubyquiz.strd6.com/quizzes/207-quine"&gt;has&lt;/a&gt;! with a necessary extra 'piping' condition): &lt;b&gt;any&lt;/b&gt; ruby program can be turned into a quine with the following code:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;if __FILE__ == $0&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; puts File.open(__FILE__, 'r') { |f| f.read }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But perhaps something more can be done? Perhaps the source code &lt;b&gt;and&lt;/b&gt; the bytecode can be displayed.&lt;/div&gt;&lt;div&gt;It turns out that the RubyVM object in YARV-based Ruby interpreters makes this possible:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;def no_op&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; $some_variable ||= 1024&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;if __FILE__ == $0&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; buf = File.open(__FILE__, 'r') { |f| f.read }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; code = RubyVM::InstructionSequence.compile(buf)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; puts buf&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; puts code.disasm&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Running this under ruby-1.9.2-head produces the following output:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;rvm ruby-1.9.2-head do ruby quine.rb&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;def no_op&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; $some_variable ||= 1024&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;if __FILE__ == $0&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; buf = File.open(__FILE__, 'r') { |f| f.read }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; code = RubyVM::InstructionSequence.compile(buf)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; puts buf&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; puts code.disasm&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;== disasm: &amp;lt;RubyVM::InstructionSequence:&amp;lt;compiled&amp;gt;@&amp;lt;compiled&amp;gt;&amp;gt;==========&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;== catch table&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;| catch type: break &amp;nbsp;st: 0029 ed: 0046 sp: 0000 cont: 0046&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;|------------------------------------------------------------------------&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;local table (size: 3, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;[ 3] buf &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[ 2] code &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0000 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp; 3)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0002 putspecialobject 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0004 putspecialobject 2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0006 putobject &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;:no_op&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0008 putiseq &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;no_op&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0010 send &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; :"core#define_method", 3, nil, 0, &amp;lt;ic:0&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0016 pop &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0017 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp; 7)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0019 putstring &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"&amp;lt;compiled&amp;gt;"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0021 getglobal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$0&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0023 opt_eq &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;ic:9&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0025 branchunless &amp;nbsp; &amp;nbsp; 100&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0027 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp; 8)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0029 getinlinecache &amp;nbsp; 36, &amp;lt;ic:2&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0032 getconstant &amp;nbsp; &amp;nbsp; &amp;nbsp;:File&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0034 setinlinecache &amp;nbsp; &amp;lt;ic:2&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0036 putstring &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"&amp;lt;compiled&amp;gt;"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0038 putstring &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;"r"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0040 send &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; :open, 2, block in &amp;lt;compiled&amp;gt;, 0, &amp;lt;ic:3&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0046 setlocal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; buf&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0048 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp; 9)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0050 getinlinecache &amp;nbsp; 59, &amp;lt;ic:4&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0053 getconstant &amp;nbsp; &amp;nbsp; &amp;nbsp;:RubyVM&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0055 getconstant &amp;nbsp; &amp;nbsp; &amp;nbsp;:InstructionSequence&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0057 setinlinecache &amp;nbsp; &amp;lt;ic:4&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0059 getlocal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; buf&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0061 send &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; :compile, 1, nil, 0, &amp;lt;ic:5&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0067 setlocal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; code&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0069 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp;10)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0071 putnil &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0072 getlocal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; buf&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0074 send &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; :puts, 1, nil, 8, &amp;lt;ic:6&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0080 pop &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0081 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp;11)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0083 putnil &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0084 getlocal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; code&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0086 send &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; :disasm, 0, nil, 0, &amp;lt;ic:7&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0092 send &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; :puts, 1, nil, 8, &amp;lt;ic:8&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0098 leave &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;( &amp;nbsp; 7)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0099 pop &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0100 putnil &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp;11)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0101 leave &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;== disasm: &amp;lt;RubyVM::InstructionSequence:no_op@&amp;lt;compiled&amp;gt;&amp;gt;===============&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0000 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;8 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp; 3)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0002 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp; 4)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0004 putnil &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0005 defined &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;7, :$some_variable, false&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0009 branchunless &amp;nbsp; &amp;nbsp; 17&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0011 getglobal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$some_variable&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0013 dup &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0014 branchif &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 22&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0016 pop &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0017 putobject &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1024&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0019 dup &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0020 setglobal &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;$some_variable&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0022 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;16 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;( &amp;nbsp; 5)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0024 leave &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;( &amp;nbsp; 4)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;== disasm: &amp;lt;RubyVM::InstructionSequence:block in &amp;lt;compiled&amp;gt;@&amp;lt;compiled&amp;gt;&amp;gt;=&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;== catch table&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;| catch type: redo &amp;nbsp; st: 0000 ed: 0011 sp: 0000 cont: 0000&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;| catch type: next &amp;nbsp; st: 0000 ed: 0011 sp: 0000 cont: 0011&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;|------------------------------------------------------------------------&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;[ 2] f&amp;lt;Arg&amp;gt; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0000 trace &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ( &amp;nbsp; 8)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0002 getdynamic &amp;nbsp; &amp;nbsp; &amp;nbsp; f, 0&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0005 send &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; :read, 0, nil, 0, &amp;lt;ic:0&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;0011 leave &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-7989848419525100572?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/7989848419525100572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2012/02/ruby-make-any-program-quine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7989848419525100572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7989848419525100572'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2012/02/ruby-make-any-program-quine.html' title='Ruby: Make any program a Quine'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-7224382942483545</id><published>2012-02-04T22:25:00.001-05:00</published><updated>2012-02-04T22:28:06.664-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Web page mirroring (wget) in Ruby</title><content type='html'>Doing a basic search for 'wget in ruby' or 'web page mirroring in ruby' generally turns up a number of unimaginative (not to mention lazy) examples of invoking wget from a sub-shell. While these solutions often meet the immediate needs of their authors, they provide no guidance for those who need to implement the mirroring of remote documents as part of a larger data processing or data management application.&lt;br /&gt;&lt;br /&gt;The following are the results of implementing web page mirroring (as performed by non-recursive wget, or by Firefox's Save As...[Web Page, Complete] feature) in such an application. The provided code will download an HTML document, parse it for JS, CSS, and image files (referred to as &lt;i&gt;resources&lt;/i&gt;), download those files to the destination directory, rewrite the references to those resources so they refer to the local (downloaded) files, then write the updated HTML document to the destination directory.&lt;br /&gt;&lt;br /&gt;The bulk of the work is done by &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Nokogiri&lt;/span&gt;, which parses the HTML document and supports XPath queries on its elements. Fetching remote documents is handled by Ruby's built-in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;net/http&lt;/span&gt; module.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# faux-wget.rb : example of performing wget-style mirroring&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;require 'nokogiri'&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;require 'net/http'&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;require 'fileutils'&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;require 'uri'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The implementation uses a single RemoteDocument class which takes a URI in its constructor and provides the high-level method &lt;b&gt;mirror&lt;/b&gt; which downloads and localizes the document at the URI.&lt;br /&gt;&lt;br /&gt;The mirror method invokes four helper methods:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;html_get&lt;/b&gt; will download the remote document&lt;/li&gt;&lt;li&gt;&lt;b&gt;Nokogiri::HTML&lt;/b&gt; parses the downloaded document&lt;/li&gt;&lt;li&gt;&lt;b&gt;process_contents&lt;/b&gt; extracts resource tags from the document&lt;/li&gt;&lt;li&gt;&lt;b&gt;save_locally&lt;/b&gt; localizes all resources&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The document parsing is straighforward: Nokogiri does all the work, and &lt;b&gt;process_contents&lt;/b&gt; simply performs XPath queries to build lists of CSS, JS and IMG tags. The document fetching is also pretty straightforward; support for response codes in the 3xx (redirect) and 4xx-5xx series (not found,&lt;br /&gt;unauthorized, internal error, etc) adds only a couple of lines of code&lt;br /&gt;&lt;br /&gt;The &lt;b&gt;save_locally&lt;/b&gt; method begins by creating the destination directory if it does not exist. If the document contains a BASE tag, it is removed - which makes the mirrored page behave better in most web browsers. Next, the localize method is invoked on all of the resource tags. Finally, the document is written to the destination directory.&lt;br /&gt;&lt;br /&gt;The&amp;nbsp;&lt;b&gt;localize&lt;/b&gt;&amp;nbsp;method performs all of the interesting work.&lt;br /&gt;&lt;br /&gt;It begins with a random delay of tens of milliseconds, which prevents most web servers from blocking the barrage of requests when downloading the resource files. The resource URL is obtained from the HREF attribute of CSS tags, or from the SRC attribute of JS and IMG tags.&lt;br /&gt;&lt;br /&gt;This URL can be absolute or relative (e.g. the file "http://foo.com/images/bar.jpg" may be referenced as "images/bar.jpg"), so an absolute URL is generated from the document URI if needed. The URL is then converted to a local path, meaning that the downloaded resource can end up in the directory "images" inside the destination directory.&lt;br /&gt;&lt;br /&gt;The remote resource file is then downloaded to this path, and the URL in the tag attribute (which is part of the original document's source) is replaced with the local path to the downloaded resource.&lt;br /&gt;&lt;br /&gt;The local path generation logic in&lt;b&gt; localize_url &lt;/b&gt;is a bit simplistic (resulting in ugly directory trees), and the mirroring process itself is non-recursive (i.e. HREF attributes in A tags are detected but ignored). The caller can easily use the RemoteDocument object to provide additional services: the parsed contents of the HTML document are available in the &lt;b&gt;contents&lt;/b&gt; property, the metadata tags (e.g. keywords) are available in the &lt;b&gt;meta&lt;/b&gt; property, and all links found in the document are available in the &lt;b&gt;links&lt;/b&gt; property.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Wrap a URI and provide methods for download, parsing, and mirroring of remote HTML document.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;class RemoteDocument&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; attr_reader :uri&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; attr_reader :contents&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; attr_reader :css_tags, :js_tags, :img_tags, :meta, :links&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def initialize(uri)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @uri = uri&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Download, parse, and save the RemoteDocument and all resources (JS, CSS,&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;images) in the specified directory.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def mirror(dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; source = html_get(uri)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @contents = Nokogiri::HTML( source )&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; process_contents&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; save_locally(dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Extract resources (CSS, JS, Image files) from the parsed html document.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def process_contents&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @css_tags = @contents.xpath( '//link[@rel="stylesheet"]' )&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @js_tags = @contents.xpath('//script[@src]')&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @img_tags = @contents.xpath( '//img[@src]' )&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; # Note: meta tags and links are unused in this example&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; find_meta_tags&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; find_links&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Extract contents of META tags to @meta Hash.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def find_meta_tags&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @meta = {}&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @contents.xpath('//meta').each do |tag|&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; last_name = name = value = nil&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; tag.attributes.each do |key, attr|&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if attr.name == 'content'&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; value = attr.value&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; elsif attr.name == 'name'&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; name = attr.value&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; last_name = attr.value&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; name = last_name if not name&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; @meta[name] = value if name &amp;amp;&amp;amp; value&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Generate a Hash URL -&amp;gt; Title of all (unique) links in document.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def find_links&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @links = {}&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @contents.xpath('//a[@href]').each do |tag|&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; @links[tag[:href]] = (tag[:title] || '') if (! @links.include? tag[:href])&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Generate a local, legal filename for url in dir.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def localize_url(url, dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; path = url.gsub(/^[|[:alpha]]+:\/\//, '')&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; path.gsub!(/^[.\/]+/, '')&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; path.gsub!(/[^-_.\/[:alnum:]]/, '_')&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; File.join(dir, path)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Construct a valid URL for an HREF or SRC parameter. This uses the document URI&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;to convert a relative URL ('/doc') to an absolute one ('http://foo.com/doc').&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def url_for(str)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; return str if str =~ /^[|[:alpha:]]+:\/\//&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; File.join((uri.path.empty?) ? uri.to_s : File.dirname(uri.to_s), str)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Send GET to url, following redirects if required.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def html_get(url)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; resp = Net::HTTP.get_response(url)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; if ['301', '302', '307'].include? resp.code&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; url = URI.parse resp['location']&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; elsif resp.code.to_i &amp;gt;= 400&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; $stderr.puts "[#{resp.code}] #{url}"&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; return&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; Net::HTTP.get url&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Download a remote file and save it to the specified path&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def download_resource(url, path)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; FileUtils.mkdir_p File.dirname(path)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; the_uri = URI.parse(url)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; if the_uri&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; data = html_get the_uri&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; File.open(path, 'wb') { |f| f.write(data) } if data&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Download resource for attribute 'sym' in 'tag' (e.g. :src in IMG), saving it to&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;'dir' and modifying the tag attribute to reflect the new, local location.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def localize(tag, sym, dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; delay&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; url = tag[sym]&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; resource_url = url_for(url)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; dest = localize_url(url, dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; download_resource(resource_url, dest)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; tag[sym.to_s] = dest.partition(File.dirname(dir) + File::SEPARATOR).last&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Attempt to "play nice" with web servers by sleeping for a few ms.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def delay&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; sleep(rand / 100)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Download all resources to destination directory, rewriting in-document tags&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;to reflect the new resource location, then save the localized document.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Creates destination directory if it does not exist.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; def save_locally(dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; Dir.mkdir(dir) if (! File.exist? dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; # remove HTML BASE tag if it exists&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @contents.xpath('//base').each { |t| t.remove }&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; # save resources&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @img_tags.each { |tag| localize(tag, :src, File.join(dir, 'images')) }&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @js_tags.each { |tag| localize(tag, :src, File.join(dir, 'js')) }&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; @css_tags.each { |tag| localize(tag, :href, File.join(dir, 'css')) }&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; save_path = File.join(dir, File.basename(uri.to_s))&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; save_path += '.html' if save_path !~ /\.((html?)|(txt))$/&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; File.open(save_path, 'w') { |f| f.write(@contents.to_html) }&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Finally. the source code file needs a driver method to make it useful from the command line. This one takes a URL and a destination directory as its parameters:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# ----------------------------------------------------------------------&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;if __FILE__ == $0&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; if ARGV.count &amp;lt; 2&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; $stderr.puts "Usage: #{$0} URL DIR"&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; exit 1&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; url = ARGV.shift&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; dir = ARGV.shift&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; doc = RemoteDocument.new(URI.parse(url))&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; doc.mirror(dir)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-7224382942483545?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/7224382942483545/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2012/02/web-page-mirroring-wget-in-ruby.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7224382942483545'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7224382942483545'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2012/02/web-page-mirroring-wget-in-ruby.html' title='Web page mirroring (wget) in Ruby'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4536666731646467893</id><published>2011-11-09T16:36:00.001-05:00</published><updated>2011-11-09T16:37:57.407-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><title type='text'>JRuby and RubyGems</title><content type='html'>For reasons too tangential to cover here, it was decided to port a Ruby 1.8.7 project to JRuby (&amp;gt;= 1.6.0). This worked well enough, except for the fatal flaw in JRuby: using gems.&lt;br /&gt;&lt;br /&gt;To begin with, getting *any* gem with native extensions to build in JRuby requires some manual intervention.&lt;br /&gt;&lt;br /&gt;The reason? The RVM JRuby has its Ruby include files under &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;cext/src/include/&lt;/span&gt;, instead of &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;lib/native/include&lt;/span&gt; like the other RVM Rubies. This appears to be &lt;a href="https://github.com/wayneeseguin/rvm/issues/440"&gt;a known issue.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A symlink handily solves the problem:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ cd /usr/local/rvm/rubies/jruby-head&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ ln -s cext/src/include lib/native/include&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Making this change will allow most gems with native extensions (e.q. pg, sqlite&lt;br /&gt;3, etc) to be built.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The qtbindings gem, however, still fails with an error like the following:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;bash$ sudo rvm jruby-head do gem install qtbindings &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;CMake Error at /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:91 (MESSAGE):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; Could NOT find Ruby (missing: RUBY_INCLUDE_DIR)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;Call Stack (most recent call first):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:252 (_FPHSA_FAILURE_MESSAGE)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; cmake/modules/FindRuby.cmake:249 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; CMakeLists.txt:18 (FIND_PACKAGE)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;-- Configuring incomplete, errors occurred!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;make: [build] Error 1 (ignored)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;cd ext/build; make&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;make[1]: Entering directory `/usr/local/rvm/gems/jruby-head/gems/qtbindings-4.6.3.4/ext/build'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;make[1]: *** No targets specified and no makefile found. &amp;nbsp;Stop.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;make[1]: Leaving directory `/usr/local/rvm/gems/jruby-head/gems/qtbindings-4.6.3.4/ext/build'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;make: *** [build] Error 2&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Unpacking the gem, fixing the extconf.rb to properly find JRuby, and repacking the gem gets past these problems, but ultimately fails when compiling the actual C extension code for Qt4.&lt;br /&gt;&lt;br /&gt;The reason? Qt4 uses heavy inspection of the stack frame in order to implement its signal/slot mechanisms, and JRuby does not provide access to this information. Until equivalent functions for&amp;nbsp;rb_frame_callee() and the like are provided by JRuby, Qt4 will remain incompatible with it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Postscript: For the curious, the following lines (or variants thereof) can be added to extconf.rb to fix the Cmake build issues:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; &amp;nbsp; file.puts "-DRUBY_EXECUTABLE=/usr/local/rvm/rubies/jruby-head/bin/jruby \\"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; &amp;nbsp; file.puts "-DRUBY_LIBRARY=/usr/local/rvm/rubies/jruby-head/lib/native/x86_64-Linux/libjruby-cext.so \\"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; &amp;nbsp; file.puts "-DRUBY_LIB_PATH=/usr/local/rvm/rubies/jruby-head/lib/native/x86_64-Linux \\"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; &amp;nbsp; file.puts "-DRUBY_INCLUDE_DIR=/usr/local/rvm/rubies/jruby-head/cext/src/include/ruby \\"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; &amp;nbsp; file.puts "-DRUBY_CONFIG_INCLUDE_DIR=/usr/local/rvm/rubies/jruby-head/cext/src/include/ruby \\"&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;file.puts "-DCMAKE_INSTALL_PREFIX=/usr/local/rvm/rubies/jruby-head \\"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The lines go in the 'else' block of the 'if windows' condition, where the other cmake options are defined (around line 216, or search for '-DENABLE_SMOKE').&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4536666731646467893?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4536666731646467893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/11/jruby-and-rubygems.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4536666731646467893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4536666731646467893'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/11/jruby-and-rubygems.html' title='JRuby and RubyGems'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-5527687614217656656</id><published>2011-09-26T01:01:00.003-04:00</published><updated>2011-09-26T01:04:10.749-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><title type='text'>Qt4 and Ruby1.9.x</title><content type='html'>After a long period with no Qt4 support for Ruby 1.9, the &lt;a href="http://rubygems.org/gems/qtbindings"&gt;qtbindings&lt;/a&gt; gem has stepped in to fill the void. This project packages the&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt; qtruby4-qtruby&lt;/span&gt; library from the &lt;a href="http://rubyforge.org/projects/korundum/"&gt;korundum&lt;/a&gt; project, so the &lt;a href="http://rubydoc.info/gems/qtbindings/4.6.3.4/frames"&gt;API&lt;/a&gt; is unchanged.&lt;br /&gt;&lt;br /&gt;Installation is straightforward:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;sudo gem install qtbindings&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;or for rvm users:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;sudo rvm gem install qtbindings&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Compatible with 1.8 as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-5527687614217656656?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/5527687614217656656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/09/qt4-and-ruby19x.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5527687614217656656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5527687614217656656'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/09/qt4-and-ruby19x.html' title='Qt4 and Ruby1.9.x'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-7042839616827995697</id><published>2011-09-25T23:03:00.002-04:00</published><updated>2011-09-25T23:04:32.479-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Determining installed version of Ubuntu</title><content type='html'>Neither &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;uname&lt;/span&gt; nor &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;motd&lt;/span&gt; are of much use in determining the version of Ubuntu installed on a third-party system, and searching launchpad.net for version details of key packages in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;dpkg&lt;/span&gt; is annoying.&lt;br /&gt;&lt;br /&gt;Fortunately there is a utility called &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;lsb_release&lt;/span&gt; that provides this information:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ lsb_release -si&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;Ubuntu&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ lsb_release -sr&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;11.04&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ lsb_release -sc&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;natty&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Complete all the way down to the silly nickname for each release.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since this is an LSB utility, it's probably been in place for some time, silently waiting to provide more detail than &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;uname&lt;/span&gt;. Seriously, would a mention in the &lt;b&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;SEE ALSO&lt;/span&gt;&lt;/b&gt; section of the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;uname&lt;/span&gt; manpage be *so* hard to add?&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-7042839616827995697?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/7042839616827995697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/09/determining-installed-version-of-ubuntu.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7042839616827995697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7042839616827995697'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/09/determining-installed-version-of-ubuntu.html' title='Determining installed version of Ubuntu'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2409136108830978539</id><published>2011-09-21T14:30:00.000-04:00</published><updated>2011-09-21T14:30:43.931-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='r500'/><category scheme='http://www.blogger.com/atom/ns#' term='x1'/><title type='text'>First impressions, redux</title><content type='html'>The&amp;nbsp;&lt;a href="http://entrenchant.blogspot.com/2007/10/first-impressions.html"&gt;R500&lt;/a&gt;&amp;nbsp;bit the dust over a year and a half ago. Most PC manufacturers were busy trying to crank out netbooks or a Macbook Air ripoff at the time, so there was no suitable replacement -- had to limp along with a Macbook Pro (ugh!) running Ubuntu until spring of this year.&lt;br /&gt;&lt;br /&gt;What replaced the R500? A &lt;a href="http://shop.lenovo.com/us/products/Laptops/ThinkPad/x-series/x1/index.html"&gt;Thinkpad X1&lt;/a&gt;. It weighs twice as much, has no transreflective screen, and is a bit bulkier, but it is paradise after using that Macbook Pro for a year. Dedicated pageup/down keys! Home and End keys! A Delete key! THREE mouse buttons! A hardware kill switch for wifi and bluetooth! A freaken eSATA connector!&lt;br /&gt;&lt;br /&gt;But why no grumpy install post detailing how to get Linux speaking to all the hardware? Because everything just works! Even the volume control buttons and the &lt;a href="http://www.reactivated.net/weblog/archives/2008/07/upek-touchstrip-sensor-only-147e2016-on-linux/"&gt;fingerprint reader&lt;/a&gt;. No joke. You can thank IBM's interest in Linux for that.&lt;br /&gt;&lt;br /&gt;On top of the compatibility, it's a nice looking machine, powerful (64-bit debian runs great on a dual-core i5 with 8 GB of RAM), and tough -- GorillaGlass over the display, MILSPEC (assuming 810) shock and spill resistance (including a keyboard that drains *through* to the bottom of the laptop), the works.&lt;br /&gt;&lt;br /&gt;The one caveat: it's best to use non-standard video drivers, as the standard debian/ubuntu Intel video driver became a bit crashy. Easily fixed, though, by adding the &lt;a href="https://launchpad.net/~xorg-edgers/+archive/ppa"&gt;xorg-edgers PPA&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2409136108830978539?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2409136108830978539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/09/first-impressions-redux.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2409136108830978539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2409136108830978539'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/09/first-impressions-redux.html' title='First impressions, redux'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4793166020323601146</id><published>2011-09-18T20:59:00.000-04:00</published><updated>2011-09-18T20:59:23.267-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>.vimrc and UTF-8</title><content type='html'>Quick .vimrc config to display UTF-8 characters correctly:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;" support UTF-8 automatically when not on console&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;if &amp;nbsp;has('gui_running') &amp;amp;&amp;amp; has('multi_byte')&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; set encoding=utf-8&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; set fileencoding=utf-8&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; set fileencodings=utf-8&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;endif&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Note that the &lt;i&gt;gui_running&lt;/i&gt; requirement ensures that this will be used by &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;gvim&lt;/span&gt; and by &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;vim&lt;/span&gt; run from a terminal emulator (such as &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;urxvt&lt;/span&gt; or &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;mlterm&lt;/span&gt;), not by &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;vim&lt;/span&gt; running from a virtual terminal or console.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4793166020323601146?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4793166020323601146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/09/vimrc-and-utf-8.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4793166020323601146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4793166020323601146'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/09/vimrc-and-utf-8.html' title='.vimrc and UTF-8'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-7248805316820031855</id><published>2011-09-07T20:42:00.002-04:00</published><updated>2011-09-07T21:09:55.649-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='binary analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Fixing strings(1)</title><content type='html'>The first step in any examination of a binary is to run &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;strings(1)&lt;/span&gt; on it.&lt;br /&gt;&lt;br /&gt;This usually results in output like the following:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ strings /bin/ls&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;FXL9GX&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;|"H9&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;GXL9FX&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;|"H9&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;F@L9G@&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;...which, needless to say, sucks.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The problem here is that a 'string' is any sequence of printable ASCII characters of a minimum length (usually 4).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The output of &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;strings(1)&lt;/span&gt; can be made a bit more usable by discarding any string that does not contain a word in the system dictionary.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;An off-the cuff implementation of such a filter in Ruby would be:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;DICT='/usr/share/dict/american-english-insane'&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ARGF.lines.each do |line|&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; is_str = false&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; line.gsub(/[^[:alnum:]]/, ' ').split.each do |word|&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; next if word.length &amp;lt; 4&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; is_str = true if (`grep -Fxc '#{word}' #{DICT}`.chomp.to_i &amp;gt; 0)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; puts line if is_str&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This would then be passed the output of &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;strings(1)&lt;/span&gt;:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ strings /bin/ls | ./strings.rb&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;__gmon_start__&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;_fini&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;clock_gettime&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;acl_get_entry&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;acl_get_tag_type&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is super-slow for large executables, and will miss sprintf-style formatting strings that contain only control characters (e.g. "%p\n"), but for the general case it produces useful output.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Directions for improvement: load one or more dictionary files and perform lookups on them in Ruby. Search for english-like words by taking 4-letter prefixes and suffixes of each 'word' in the string and searching for dictionary words that start or end with that prefix/suffix. Provide early-exit from the inner field look when a match is found. Allow matches of sprintf formatting strings, URIs ('http://'), etc.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-7248805316820031855?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/7248805316820031855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/09/fixing-strings1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7248805316820031855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7248805316820031855'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/09/fixing-strings1.html' title='Fixing strings(1)'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2511711629261096868</id><published>2011-08-11T02:05:00.004-04:00</published><updated>2011-09-06T14:05:12.006-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Ruby __END__ and DATA</title><content type='html'>The &lt;b&gt;__END__&lt;/b&gt; keyword in Ruby causes the parser to stop executing the source file; it is often used for appending documentation such as a LICENSE file to the end of a source code file.&lt;br /&gt;&lt;br /&gt;More interesting is the fact that the contents of the file following the &lt;b&gt;__END__&lt;/b&gt; keyword are available via the global IO object named &lt;b&gt;DATA&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;This means that it is possible to include test data -- even binary data -- at the end of a Ruby source file:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ cat od.rb&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;if __FILE__ == $0&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; offset = 0&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; while (buf = DATA.read(16)) do&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; bytes = buf.unpack 'C*'&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; puts "%08X: %s\n" % [ offset, bytes.map { |b| " %03o" % b } ]&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; offset += 16&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;__END__ &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ cat /bin/true &amp;gt;&amp;gt; od.rb&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ ./od.rb&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;00000000: &amp;nbsp;177 105 114 106 002 001 001 000 000 000 000 000 000 000 000 000&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;00000010: &amp;nbsp;002 000 076 000 001 000 000 000 220 044 100 000 000 000 000 000&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;00000020: &amp;nbsp;100 000 000 000 000 000 000 000 060 226 001 000 000 000 000 000&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2511711629261096868?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2511711629261096868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/08/ruby-end-and-data.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2511711629261096868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2511711629261096868'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/08/ruby-end-and-data.html' title='Ruby __END__ and DATA'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2663351238107595196</id><published>2011-08-08T14:56:00.000-04:00</published><updated>2011-08-08T14:56:13.976-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='kde'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>knotify4 uses 100% CPU</title><content type='html'>This is true, and has been for awhile. It tends to occur when a laptop is suspended while online, then resumed while offline. A nice side effect is that battery life gets reduced by about 75% while offline, which is generally when one wants the longest battery life.&lt;br /&gt;&lt;br /&gt;There are plenty of bug reports open on this, but it's pretty clear that the KDE/Kubuntu guys either have no clue how to fix this, or cannot be bothered.&lt;br /&gt;&lt;br /&gt;Since &lt;b&gt;knotify4&lt;/b&gt; isn't really all that useful (especially when using E17 for a WM), there is a brutal hack that will effectively silence it:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;sudo mv /usr/bin/knotify4&amp;nbsp;&amp;nbsp;/usr/bin/knotify4.orig&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;sudo cp /bin/true /usr/bin/knotify4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Needless to say, the original file should be restored before doing an upgrade.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2663351238107595196?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2663351238107595196/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/08/knotify4-uses-100-cpu.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2663351238107595196'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2663351238107595196'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/08/knotify4-uses-100-cpu.html' title='knotify4 uses 100% CPU'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-3750588205741391581</id><published>2011-08-08T11:43:00.000-04:00</published><updated>2011-08-08T11:43:29.744-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Disabling startup (init.d) services in Ubuntu</title><content type='html'>Ubuntu never has made obvious what the "Ubuntu way" of removing services from System-V run levels is. The GUI tools in GNOME and KDE are incomplete, and a quick investigation of the run level directories shows that they are filled automatically -- so that symlinks added and removed manually might, in the future, get ignored.&lt;br /&gt;&lt;br /&gt;Fortunately, the README in /etc/init.d ends with the following advice:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Use the update-rc.d command to create symbolic links in the /etc/rc?.d&lt;br /&gt;as appropriate. See that man page for more details.&lt;/blockquote&gt;&lt;br /&gt;The man page lists the following forms for invoking &lt;b&gt;upgrade-rc.d&lt;/b&gt;: &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; update-rc.d [-n] [-f] B name &amp;nbsp;remove&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;update-rc.d [-n] B name &amp;nbsp;defaults [NN | SS KK]&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;update-rc.d &amp;nbsp;[-n] &amp;nbsp;name &amp;nbsp;start|stop &amp;nbsp;R &amp;nbsp;NN runlevel &amp;nbsp;[ runlevel ]...&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;update-rc.d [-n] B name &amp;nbsp;disable|enable [ S|2|3|4|5 ]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The following command will remove the service &lt;b&gt;collectd&lt;/b&gt; from all run levels:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo update-rc.d collectd disable&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;update-rc.d: warning: collectd start runlevel arguments (none) do not match LSB Default-Start values (2 3 4 5)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;update-rc.d: warning: collectd stop runlevel arguments (none) do not match LSB Default-Stop values (0 1 6)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Disabling system startup links for /etc/init.d/collectd ...&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Removing any system startup links for /etc/init.d/collectd ...&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc0.d/K95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc1.d/K95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc2.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc3.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc4.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc5.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc6.d/K95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Adding system startup for /etc/init.d/collectd ...&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc0.d/K95collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc1.d/K95collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc6.d/K95collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc2.d/K05collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc3.d/K05collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc4.d/K05collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc5.d/K05collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;The command to remove a service from a specific runlevel should be the following:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;sudo update-rc.d BASENAME disable `runlevel | cut -d ' ' -f 2`&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;...however a quick experiment shows that the runlevel argument is ignored:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;sudo update-rc.d collectd disable 2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;sudo update-rc.d collectd disable 2&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;update-rc.d: warning: collectd start runlevel arguments (none) do not match LSB Default-Start values (2 3 4 5)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;update-rc.d: warning: collectd stop runlevel arguments (none) do not match LSB Default-Stop values (0 1 6)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Disabling system startup links for /etc/init.d/collectd ...&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Removing any system startup links for /etc/init.d/collectd ...&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc0.d/K95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc1.d/K95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc2.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc3.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc4.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc5.d/S95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc6.d/K95collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Adding system startup for /etc/init.d/collectd ...&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc0.d/K95collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc1.d/K95collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc6.d/K95collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc2.d/K05collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc3.d/K05collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;/etc/rc4.d/K05collectd -&amp;gt; ../init.d/collectd&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-3750588205741391581?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/3750588205741391581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/08/disabling-startup-initd-services-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3750588205741391581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3750588205741391581'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/08/disabling-startup-initd-services-in.html' title='Disabling startup (init.d) services in Ubuntu'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-1870560740300961891</id><published>2011-08-07T22:12:00.001-04:00</published><updated>2011-08-07T22:14:25.782-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='disasm'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='kernel'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>perf-backed disassembly</title><content type='html'>Since 2.6.31 or thereabouts, the Linux kernel has come with a built-in performance counter known as&amp;nbsp;&lt;a href="https://perf.wiki.kernel.org/index.php/Main_Page"&gt;perf&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The common form of perf is well-known to be useful in gathering performance statistics on a running program:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;perf stat -cv ./a.out&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;cache-misses: 11313 2020574449 2020574449&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;cache-references: 62031796 2020574449 2020574449&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;branch-misses: 17909 2020574449 2020574449&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;branches: 606684832 2020574449 2020574449&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;instructions: 6324531571 2020574449 2020574449&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;cycles: 6408533747 2020574449 2020574449&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;page-faults: 304 2019963367 2019963367&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;CPU-migrations: 7 2019963367 2019963367&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;context-switches: 205 2019963367 2019963367&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;task-clock-msecs: 2019963367 2019963367 2019963367&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Performance counter stats for './a.out':&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;11313 cache-misses &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp; &amp;nbsp;0.006 M/sec&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 62031796 cache-references &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp; 30.709 M/sec&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;17909 branch-misses &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# &amp;nbsp; &amp;nbsp; &amp;nbsp;0.003 % &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;606684832 branches &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp;300.344 M/sec&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 6324531571 instructions &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp; &amp;nbsp;0.987 IPC &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 6408533747 cycles &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; 3172.599 M/sec&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;304 page-faults &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# &amp;nbsp; &amp;nbsp; &amp;nbsp;0.000 M/sec&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;7 CPU-migrations &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp; &amp;nbsp;0.000 M/sec&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;205 context-switches &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp; &amp;nbsp;0.000 M/sec&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;2019.963367 task-clock-msecs &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp; &amp;nbsp;0.996 CPUs&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 2.027948307 &amp;nbsp;seconds time elapsed&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;The &amp;nbsp;events to be recorded can be specified with the&lt;b&gt; -e &lt;/b&gt;option in order to refine the output:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;perf stat -e cpu-clock -e instructi&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ons &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Performance counter stats for './a.out':&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;2026.748812 cpu-clock-msecs &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 6324293589 instructions &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # &amp;nbsp; &amp;nbsp; &amp;nbsp;0.000 IPC &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 2.032519896 &amp;nbsp;seconds time elapsed&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A list of available events can be obtained via perf list:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;perf list | head&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;List of pre-defined events (to be used in -e):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; cpu-cycles OR cycles &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [Hardware event]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; instructions &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [Hardware event]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; cache-references &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [Hardware event]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; cache-misses &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [Hardware event]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; branch-instructions OR branches &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[Hardware event]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; branch-misses &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[Hardware event]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; bus-cycles &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [Hardware event]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The perf toolchain also includes the utility &lt;b&gt;perf top&lt;/b&gt;, which can be used to monitor a single process, or which can be used to monitor the kernel:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;sudo perf top 2&amp;gt;/dev/null&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;-------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;PerfTop: &amp;nbsp; &amp;nbsp; &amp;nbsp; 0 irqs/sec &amp;nbsp;kernel:-nan% &amp;nbsp;exact: -nan% [1000Hz cycles], &amp;nbsp;(all, 4 CPUs)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;-------------------------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;samples &amp;nbsp;pcnt function &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; DSO&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;_______ _____ ______________________ __________________&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;77.00 39.3% intel_idle &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [kernel.kallsyms]&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;13.00 &amp;nbsp;6.6% __pthread_mutex_unlock libpthread-2.13.so&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;13.00 &amp;nbsp;6.6% pthread_mutex_lock &amp;nbsp; &amp;nbsp; libpthread-2.13.so&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;12.00 &amp;nbsp;6.1% __ticket_spin_lock &amp;nbsp; &amp;nbsp; [kernel.kallsyms]&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 7.00 &amp;nbsp;3.6% schedule &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [kernel.kallsyms]&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 6.00 &amp;nbsp;3.1% menu_select &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[kernel.kallsyms]&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 6.00 &amp;nbsp;3.1% fget_light &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [kernel.kallsyms]&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 6.00 &amp;nbsp;3.1% clear_page_c &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [kernel.kallsyms]&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Where things start to get interesting, however, is with &lt;b&gt;perf record&lt;/b&gt;. This utility is generally used along with&lt;b&gt; perf report &lt;/b&gt;to record the performance counters of a process, and review them later.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This can be used, for example, to generate a call graph:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ &amp;nbsp;perf record -g -o /tmp/a.out.perf ./a.out&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;[ perf record: Woken up 1 times to write data ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;[ perf record: Captured and wrote 0.148 MB /tmp/a.out.perf (~6461 samples) ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;perf report -g -i /tmp/a.out.perf&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# Events: 1K cycles&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# Overhead &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Command &amp;nbsp;Shared Object &amp;nbsp;Symbol&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# ........ &amp;nbsp;............. &amp;nbsp;............. &amp;nbsp;......&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 99.90% &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;a.out &amp;nbsp;a.out &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[.] main&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; |&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; --- main&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; __libc_start_main&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;0.10% &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;a.out &amp;nbsp;[l2cap] &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[k] 0xffffffff8103804a&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; |&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; --- 0xffffffff8105f438&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0xffffffff8105f675&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Once perf data has been recorded, the &lt;b&gt;perf annotate&lt;/b&gt; utility can be used to display a disassembly of the instructions that were executed:&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;&amp;nbsp;perf annotate -i /tmp/a.out.perf |more&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;Percent | &amp;nbsp; &amp;nbsp; &amp;nbsp;Source code &amp;amp; Disassembly of a.out&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;: &amp;nbsp; &amp;nbsp; &amp;nbsp;Disassembly of section .text:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;: &amp;nbsp; &amp;nbsp; &amp;nbsp;0000000000400554 &lt;main&gt;:&lt;/main&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;400554: &amp;nbsp; &amp;nbsp; &amp;nbsp; 55 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;push &amp;nbsp; %rbp&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;400555: &amp;nbsp; &amp;nbsp; &amp;nbsp; 48 89 e5 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;%rsp,%rbp&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;400558: &amp;nbsp; &amp;nbsp; &amp;nbsp; 48 81 ec 30 00 0c 00 &amp;nbsp; &amp;nbsp;sub &amp;nbsp; &amp;nbsp;$0xc0030,%rsp&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;40055f: &amp;nbsp; &amp;nbsp; &amp;nbsp; 48 8d 85 d0 ff fb ff &amp;nbsp; &amp;nbsp;lea &amp;nbsp; &amp;nbsp;-0x40030(%rbp),%rax&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;400566: &amp;nbsp; &amp;nbsp; &amp;nbsp; ba 00 00 04 00 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;$0x40000,%edx&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;40056b: &amp;nbsp; &amp;nbsp; &amp;nbsp; be 00 00 00 00 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;$0x0,%esi&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;400570: &amp;nbsp; &amp;nbsp; &amp;nbsp; 48 89 c7 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;%rax,%rdi&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;400573: &amp;nbsp; &amp;nbsp; &amp;nbsp; e8 b0 fe ff ff &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;callq &amp;nbsp;400428 &amp;lt;memset@plt&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;400578: &amp;nbsp; &amp;nbsp; &amp;nbsp; c7 45 fc 00 00 00 04 &amp;nbsp; &amp;nbsp;movl &amp;nbsp; $0x4000000,-0x4(%rbp)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 4.21 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006a5: &amp;nbsp; &amp;nbsp; &amp;nbsp; 8b 45 d0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;-0x30(%rbp),%eax&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;15.54 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006a8: &amp;nbsp; &amp;nbsp; &amp;nbsp; 83 c0 01 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;add &amp;nbsp; &amp;nbsp;$0x1,%eax&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 4.97 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006ab: &amp;nbsp; &amp;nbsp; &amp;nbsp; 89 45 d0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;%eax,-0x30(%rbp)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 4.87 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006ae: &amp;nbsp; &amp;nbsp; &amp;nbsp; 8b 45 d0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;-0x30(%rbp),%eax&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;17.79 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006b1: &amp;nbsp; &amp;nbsp; &amp;nbsp; 83 c0 01 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;add &amp;nbsp; &amp;nbsp;$0x1,%eax&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 4.36 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006b4: &amp;nbsp; &amp;nbsp; &amp;nbsp; 89 45 d0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;mov &amp;nbsp; &amp;nbsp;%eax,-0x30(%rbp)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 4.72 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006b7: &amp;nbsp; &amp;nbsp; &amp;nbsp; 48 83 45 f0 01 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;addq &amp;nbsp; $0x1,-0x10(%rbp)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; 0.00 :&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; &amp;nbsp;4006bc: &amp;nbsp; &amp;nbsp; &amp;nbsp; 48 8b 45 f0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mov &amp;nbsp; &amp;nbsp;-0x10(%rbp),%rax&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: 'Courier New', Courier, monospace; font-size: small;"&gt;&amp;nbsp; &amp;nbsp; ...&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;As to be expected from Torvalds and company, the utilities include a number of options for generating parser-friendly output, limiting reporting to specified events and symbols, and so forth. Check the man pages for details.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-1870560740300961891?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/1870560740300961891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/08/perf-backed-disassembly.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1870560740300961891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1870560740300961891'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/08/perf-backed-disassembly.html' title='perf-backed disassembly'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2098922094808581787</id><published>2011-07-31T13:46:00.009-04:00</published><updated>2011-08-07T18:31:37.986-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='e17'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Easy E(17)</title><content type='html'>Ubuntu finally ships with a working E17, but there is a lingering problem: most of the modules are gone!&lt;br /&gt;&lt;br /&gt;To get a full-featured install of E17, replete with modules and UI enhancements (finally! the settings panel entries re available from the main menu!), it is best to install from SVN.&lt;br /&gt;&lt;br /&gt;Building E17 has always been an ordeal. Fortunately, the script easy_e17.sh makes downloading, building, and installing E17 from SVN to be quite a simple affair, once all of the dependencies are met.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To begin with, download the&amp;nbsp;&lt;a href="http://omicron.homeip.net/projects/#easy_e17.sh"&gt;easy_e17&lt;/a&gt;&amp;nbsp;script from&lt;a href="http://omicron.homeip.net/"&gt; &lt;span id="goog_1235893738"&gt;&lt;/span&gt;&lt;span id="goog_1235893739"&gt;&lt;/span&gt;http://omicron.homeip.net&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There is a bug in the script which must be fixed if packages are going to be built. According to &lt;a href="http://trac.enlightenment.org/e/ticket/600"&gt;this bug report&lt;/a&gt;, the line (around 36 or 37)&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;packages_full="$efl_basic $bin_basic $e_modules_bin $e_modules_extra $efl_extra&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;$bin_extra"&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;should be&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; packages_full="$efl_basic $bin_basic $e_modules_bin $efl_extra $bin_extra $e_modules_extra"&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;... in order to get all the internal dependencies straight. Failure to do this results in the following error:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;e_mod_main.c:1527:32: error: ‘ethumb_client’ undeclared (first use in this function)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Next, install all necessary dependencies to build E17 and the modules:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$&amp;nbsp;sudo apt-get install autopoint libudev-dev libgcrypt11 libgcrypt11-dev&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo apt-get install libasound2-dev libasound2 libxine-dev&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo apt-get install paman padevchooser paprefs pavucontrol pavumeter&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo apt-get install libiptcdata-dev libmpd-dev cython&amp;nbsp;libxcb-shape0-dev&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;According to &lt;a href="http://jeffhoogland.blogspot.com/2010/05/howto-install-e17-from-svnsource-on.html"&gt;this guide&lt;/a&gt;, the full list of dependencies is as follows:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo apt-get install xterm make gcc bison flex subversion cvs automake1.10 autoconf autotools-dev autoconf-archive libtool gettext libpam0g-dev libfreetype6-dev libpng12-dev zlib1g-dev libjpeg62-dev libtiff4-dev libungif4-dev librsvg2-dev libx11-dev libxcursor-dev libxrender-dev libxrandr-dev libxfixes-dev libxdamage-dev libxcomposite-dev libxss-dev libxp-dev libxext-dev libxinerama-dev libxft-dev libxfont-dev libxi-dev libxv-dev libxkbfile-dev libxres-dev libxtst-dev libltdl7-dev libglu1-xorg-dev libglut3-dev xserver-xephyr libdbus-1-dev liblua5.1-0-dev libasound2-dev libudev-dev&amp;nbsp;autopoint&amp;nbsp;&amp;nbsp;libxml2-dev&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Chances are, however, that most of these are already installed.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The E17 build makes some assumptions about the location of the gcrypt libraries, so they must be symlinked:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo ln -s /usr/lib/x86_64-linux-gnu/libgcrypt.a /lib/x86_64-linux-gnu&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo&amp;nbsp;ln -s /usr/lib/x86_64-linux-gnu/libgcrypt.la /lib/x86_64-linux-gnu&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo&amp;nbsp;ln -s /usr/lib/x86_64-linux-gnu/libgcrypt.so /lib/x86_64-linux-gnu&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...other wise linker errors such as the following will appear:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;../../src/lib/.libs/libeet.so: undefined reference to `gcry_cipher_setiv'&lt;br /&gt;../../src/lib/.libs/libeet.so: undefined reference to `gcry_cipher_setkey'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Xine development headers (libxine-dev) must be installed in order for emotion to compile; otherwise an error like this will appear:&lt;br /&gt;&lt;span class="Apple-style-span" style="white-space: pre;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;configure: error: Xine, Gstreamer or VLC backends must be selected to build Emotion&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Naturally, Gstreamer or VLC headers could be installed instead.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The asound2-dev and the pulseaudio tools are required to get the mixer module working. Without them, the error&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;No ALSA mixer found!&lt;/span&gt;&lt;br /&gt;&lt;div&gt;will appear in the mixer module settings. Note that pulseaudio requires that the current user be added to the audio group; this can be done with the following command:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;sudo adduser $USERNAME audio&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Once all this is done, it is a simple matter to install E17 with the following command:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;./easy_e17.sh --instpath=/usr/local/e17 --srcpath=/home/$USER/src/e17 -i&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This downloads the E17 source code to ~/src/e17, and sets the install directory to /usr/local/e17. There should be a nice success message if everything goes well.&lt;br /&gt;&lt;br /&gt;Next, build all of the modules:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&lt;br class="Apple-interchange-newline" /&gt;./easy_e17.sh --instpath=/usr/local/e17 --srcpath=/home/$USER/src/e17 --packagelist=full -i&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Upgrading is just as straightforward:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;./easy_e17.sh --instpath=/usr/local/e17 --srcpath=/home/$USER/src/e17 --packagelist=full -u&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Final notes:&lt;br /&gt;&lt;br /&gt;&amp;nbsp; * The &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;composite&lt;/span&gt; module causes problems: when using the &lt;i&gt;Software&lt;/i&gt; engine, it causes typing delays that make terminals near unusable; when using the &lt;i&gt;OpenGL&lt;/i&gt; engine, windows do not update until there is a focus change (BIG problem). It should be disabled.&lt;br /&gt;&lt;br /&gt;&amp;nbsp; * The shelf containing the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;systray&lt;/span&gt; module &lt;b&gt;must&lt;/b&gt; be given the stacking order &lt;i&gt;Above Everything&lt;/i&gt;; otherwise, the applications in the E17 systray will not receive mouse clicks (VERY BAD).&lt;br /&gt;&lt;br /&gt;&amp;nbsp; * Determining the fonts used by various modules is easier when the source is available. The font classes used in the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;tclock&lt;/span&gt; module, for example, are in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;E-MODULES-EXTRA/tclock/tclock.edc&lt;/span&gt;. The time is displayed in&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;text_class: "module_large"&lt;/span&gt;, and the date is displayed in&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;text_class: "module_small"&lt;/span&gt;. The &lt;i&gt;Settings-&amp;gt;Look-&amp;gt;Fonts&lt;/i&gt; dialog can be used to determine the font used for these classes by enabling (via the checkbox at bottom-right) the font class &lt;i&gt;Modules::Small&lt;/i&gt; or &lt;i&gt;Modules::Large&lt;/i&gt;, and setting the font (e.g. &lt;i&gt;Sans/Regular/12 pixels&lt;/i&gt;) to be used for each.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UPDATE&lt;/b&gt; : Recent builds now fail when using &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;--packagelist=full.&lt;/span&gt; Best stick with &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;--packagelist=half &lt;/span&gt;if the full build fails.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2098922094808581787?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2098922094808581787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/07/easy-e17.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2098922094808581787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2098922094808581787'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/07/easy-e17.html' title='Easy E(17)'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8286181704378492718</id><published>2011-07-31T00:27:00.000-04:00</published><updated>2011-07-31T00:27:38.637-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='tcp/ip'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Static DNS servers in /etc/resolv.conf</title><content type='html'>It's an old problem: NetworkManager overwrites /etc/resolv.conf with each connection, forcing one to use the local ISP's crappy DNS servers instead of one's tried-and-true public DNS servers.&lt;br /&gt;&lt;br /&gt;The fixes for this are legion: chattr resolv.conf, disable dhclient, replace NetworkManager.&lt;br /&gt;&lt;br /&gt;It turns out that dhclient can be configured to include specified DNS servers (up to twp before a warning is displayed) in the resolv.conf.&lt;br /&gt;&lt;br /&gt;Simply add the following line to /etc/dhcp/dhclient.conf :&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;prepend domain-name-servers 4.2.2.1, 4.2.2.4; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Obviously, replace 4.2.2.1 and 4.2.2.4 with whatever name servers are desired. The semicolon, by the way, is extremely important -- don't leave it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8286181704378492718?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8286181704378492718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/07/static-dns-servers-in-etcresolvconf.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8286181704378492718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8286181704378492718'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/07/static-dns-servers-in-etcresolvconf.html' title='Static DNS servers in /etc/resolv.conf'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2151598536563024668</id><published>2011-07-29T14:49:00.005-04:00</published><updated>2011-07-29T14:55:32.428-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='upgrade rage'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='grub2'/><title type='text'>Ubuntu, you're fired.</title><content type='html'>Attempted to upgrade a workstation (dual-xeon, sata3 ssd for &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/&lt;/span&gt;; sata 3 hdd for &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/usr&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/var&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/opt&lt;/span&gt;, &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/usr/local&lt;/span&gt;; sata2 mirrored raid (via &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;dmraid&lt;/span&gt;) for &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/home&lt;/span&gt;) from 10.04 to 11.04 via &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;do-release-upgrade&lt;/span&gt; (in &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;update-manager-core&lt;/span&gt;) last night. Why do that? Because Ubuntu only updates software packages for its newest release, so if you need, say, Python 2.7 in order to work on a project, you're SOL unless you upgrade.&lt;br /&gt;&lt;br /&gt;Everything went well until the final reboot, which left the machine at:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;error: the symbol 'grub_xputs' not found.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Verified that the grub software is all present:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt; ls (hd0,1)/boot&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;... lots of modules and such...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Everything seemed to be there, so attempted to boot manually, under the assumption it was all due to misconfiguration:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt; set root=(hd0,1)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt;&amp;nbsp;set kernel=/boot/vmlinuz-2.6.32-33-generic&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt;&amp;nbsp;set initrd=/boot/initrd.img-2.6.32-33-generic&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt;&amp;nbsp;boot&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;error: no loaded kernel&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Second attempt, loading the grub modules manually (per&amp;nbsp;&lt;a href="https://help.ubuntu.com/community/Grub2#Rescue%20Mode%20%28%27%27grub%20rescue%3E%27%27%29%20Booting"&gt;Ubuntu Grub2 Docs&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://ubuntuforums.org/showthread.php?t=1599293"&gt;this thread&lt;/a&gt;):&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt;&amp;nbsp;set prefix=(hd0,1)/boot/grub&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt; insmod normal&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;error: the symbol 'grub_xputs' not found.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt; insmod linuxgrub&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;error: the symbol 'grub_xputs' not found.&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For some reason, the symbols in the grub2 bootstrap code do not match the ones expected by the modules. One of them is the wrong version. Looks to be a&amp;nbsp;&lt;a href="https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/756564"&gt;reported bug&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The solution, per &lt;a href="https://help.ubuntu.com/community/Grub2#Selected%20Problems%20and%20Bugs"&gt;Ubuntu Grub2 Docs&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://opensource-sidh.blogspot.com/2011/06/recover-grub-live-ubuntu-cd.html"&gt;this fix&lt;/a&gt;, is to boot a LiveCD (which must be the same version as the newly-installed-but-broken Ubuntu) and run grub-install. This turns out to be a huge delay, as the upgrade took place without a LiveCD, so an ISO must be downloaded. It is also a huge pain when one is out of blank CDs, and has to press a usbstick into service (accomplished per &lt;a href="https://help.ubuntu.com/community/Installation/FromUSBStick#From%20Ubuntu%20Linux"&gt;Ubuntu Install Docs&lt;/a&gt;).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It also doesn't work. The new error on reboot is&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;error: file not found&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;grub rescue&amp;gt; insmod linux&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;ELF header smaller than expected&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It turns out that 10.04 upgrades to 10.10, NOT 11.04, so the LiveCD contained the wrong version of grub!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Fortunately, there is&amp;nbsp;&lt;a href="http://ubuntuforums.org/showthread.php?t=1581099"&gt;a guide&lt;/a&gt;&amp;nbsp;to purging and reinstalling grub once booted from &lt;b&gt;any&lt;/b&gt; LiveCD.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The steps are (details such as hdd devices and mount points are specific to this workstation):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ sudo bash&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# mount /dev/sdk1 /mnt&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# mount /dev/sdf3 /usr&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# mount /dev/sdf4 /var&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# mount --bind /dev /mnt/dev&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# mount --bind /dev/pts /mnt/dev/pts&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# mount --bind /proc /mnt/proc&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# mount --bind /sys /mnt/sys&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# chroot /mnt&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash-chroot&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# apt-get update&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash-chroot&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# apt-get purge grub grub-common grub-pc&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash-chroot&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# apt-get install grub-common grub-pc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash-chroot&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# #NOTE: Be sure to select /dev/sdk for Grub2 install location&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash-chroot# exit&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# umount /mnt/dev/pts&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# umount /mnt/dev&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash#&amp;nbsp;umount /dev/sys&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# umount /mnt/proc&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# umount /mnt/var&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# umount /mnt/usr&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# umount /mnt&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash# reboot&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That does the trick.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But you know what would really be nice? A way to upgrade the OS, including the kernel and all packages, &lt;b&gt;WITHOUT TOUCHING THE GODDAMN BOOT-LOADER&lt;/b&gt;. Seriously, it's an &lt;i&gt;upgrade&lt;/i&gt;, the bootloader already works -- don't mess with it!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2151598536563024668?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2151598536563024668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/07/ubuntu-youre-fired.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2151598536563024668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2151598536563024668'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/07/ubuntu-youre-fired.html' title='Ubuntu, you&apos;re fired.'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-1724883691262408924</id><published>2011-07-18T15:31:00.001-04:00</published><updated>2011-07-18T15:32:16.541-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gcc'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='c/c++'/><title type='text'>Pre-defined preprocessor definitions (GCC)</title><content type='html'>The&amp;nbsp;&lt;a href="http://predef.sourceforge.net/index.php"&gt;predef project&lt;/a&gt;&amp;nbsp;is extremely convenient for looking up architecture-dependent, os-dependent, compiler-dependent, or standard C/C++ compiler macros, but it is not available when working offline.&lt;br /&gt;&lt;br /&gt;To get around this, the preprocessor definitions defined automatically by GCC can be viewed with this command line:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ gcc -dM &amp;lt; /dev/null&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#define __DBL_MIN_EXP__ (-1021)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#define __UINT_LEAST16_MAX__ 65535&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#define __FLT_MIN__ 1.17549435082228750797&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This will output the #define commands as they would be executed by the preprocessor. To get a list of the non-expanded macros, use -dN :&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;bash$ gcc -dN &amp;lt; /dev/null&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# 1 "&lt;stdin&gt;"&lt;/stdin&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;# 1 "&lt;built-in&gt;"&lt;/built-in&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#define __STDC__&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#define __STDC_HOSTED__&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#define __GNUC__&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#define __GNUC_MINOR__&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is a bit more noisy, as undefined macros (e.g. "# 1") are included, but may be more suitable for parsing.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-1724883691262408924?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/1724883691262408924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/07/pre-defined-preprocessor-definitions.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1724883691262408924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1724883691262408924'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/07/pre-defined-preprocessor-definitions.html' title='Pre-defined preprocessor definitions (GCC)'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2959342447364703946</id><published>2011-07-17T18:16:00.000-04:00</published><updated>2011-07-17T18:16:24.398-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>/usr/bin/say linux</title><content type='html'>Anyone who has spent any time fiddling with OS X on the command line will have discovered /usr/bin/say, and incorporated it into a few shell scripts to provide background notifications ("build complete", etc).&lt;br /&gt;&lt;br /&gt;While Linux doe snot provide this command out of the box, there is a package called festival which provides text-to-speech capability:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;apt-get install festival&amp;nbsp;festival-freebsoft-utils&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The usage is quite straightforward:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;echo "this is only a test" | festival --tts&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A simple alias can be used to replace /usr/bin/say on the command line:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;alias say='festival -tts'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A more script-friendly solution would be to create a small wrapper script that passes /usr/bin/say arguments (filenames or STDIN) to festival:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;#!/usr/bin/env ruby &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;def say(str)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; `echo '#{str}' | festival --tts`&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;if __FILE__ == $0&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;&amp;nbsp; say(ARGF.read)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: xx-small;"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2959342447364703946?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2959342447364703946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/07/usrbinsay-linux.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2959342447364703946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2959342447364703946'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/07/usrbinsay-linux.html' title='/usr/bin/say linux'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8072773373973212790</id><published>2011-07-08T01:05:00.000-04:00</published><updated>2011-07-08T01:05:52.639-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='r'/><title type='text'>RattleR</title><content type='html'>&lt;a href="http://rattle.togaware.com/"&gt;Rattle&lt;/a&gt;&amp;nbsp;is a useful data analysis UI for R.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Unfortunately, it's a bit hard to get started from a shortcut such as an XDG .desktop file. The usual method (ala Rkward and RCommander), `&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;R_DEFAULT_PACKAGES="$R_DEFAULT_PACKAGES rattle" R&lt;/span&gt;`, doesn't work. Instead, one is left doing the `&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;library(rattle); rattle()&lt;/span&gt;` commands in an R terminal.&lt;br /&gt;&lt;br /&gt;A first stab with Rscript fails:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#!/usr/bin/env Rscript&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;library(rattle)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;rattle()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This starts Rattle, then exits both Rattle and R. Obviously, rattle() is spawning a thread and returning immediately, causing RScript to reach EOF and exit. The same happens when the commands are sent to R, r, or Rscript via STDIN.&lt;br /&gt;&lt;br /&gt;Adding a line to wait for an enter keypress seems a likely workaround:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;#!/usr/bin/env Rscript&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;library(rattle)&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;rattle()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;system('bash -c read')&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This starts Rattle, and pauses R waiting for an enter key as expected, but Rattle does not receive input. Whatever it's doing for its UI, it is not properly threaded as locking up the parent also locks up the child --- probably some sort of "greenthreads" ala Ruby or Python.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It turns out, however, that this is not an unknown problem. A query of "R_DEFAULT_PACKAGES rattle" turns up pages that provide a fix for the original method: specifying Rattle's dependencies.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The correct command is therefore&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;R_DEFAULT_PACKAGES="datasets,utils,grDevices,graphics,stats,rattle" R -q --save&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Note the -q, to suppress the startup text, and --save (or, alternately, --no-save) to suppress the confirm-quit message. This should be launched in a .desktop file with &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Terminal=true&lt;/span&gt;.&amp;nbsp;R will still wait around for a q() or Ctrl-D, but that is a small price to pay.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8072773373973212790?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8072773373973212790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/07/rattler.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8072773373973212790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8072773373973212790'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/07/rattler.html' title='RattleR'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-3969357138417190340</id><published>2011-05-14T17:35:00.000-04:00</published><updated>2011-05-14T17:35:29.237-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Google Go'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Installing Google Go system-wide on Ubuntu</title><content type='html'>Just some quick notes. This assumes all necessary packages for C dev are installed (&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;sudo apt-get install bison ed gawk gcc libc6-dev make&lt;/span&gt;&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;As root (sudo bash): &lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bash# apt-get install mercurial mercurial-common mercurial-git &lt;/span&gt;&lt;/span&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash# cd /usr/local&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash# hg clone -u release https://go.googlecode.com/hg/ go&lt;br /&gt;bash# cd go/src&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash# export GOBIN=/usr/local/bin&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash# ./all.bash&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;After this, the Go toolchain will be installed in /usr/local/bin, with all supporting files in /usr/local/go. Ubuntu should already have /usr/local/bin in the path for all users.&lt;br /&gt;&lt;br /&gt;To test (as a normal user):&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ cd /tmp&lt;br /&gt;bash$ cat &amp;gt;hello.go &amp;lt;&lt;eof&gt;&lt;br /&gt;&amp;gt; package main&lt;br /&gt;&amp;gt; &lt;br /&gt;&amp;gt; import "fmt"&lt;br /&gt;&amp;gt; &lt;br /&gt;&amp;gt; func main() {&lt;br /&gt;&amp;gt; fmt.Printf("hello, world\n")&lt;br /&gt;&amp;gt; }&lt;br /&gt;&amp;gt; EOF&lt;br /&gt;bash$ 6g hello.go&lt;br /&gt;bash$ 6l hello.6&lt;br /&gt;bash$ ./6.out&lt;br /&gt;hello, world&lt;br /&gt;bash$ rm hello.go hello.6 6.out&lt;/eof&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-3969357138417190340?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/3969357138417190340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/05/installing-google-go-system-wide-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3969357138417190340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3969357138417190340'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/05/installing-google-go-system-wide-on.html' title='Installing Google Go system-wide on Ubuntu'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2600974205782370391</id><published>2011-05-14T16:36:00.000-04:00</published><updated>2011-05-14T16:36:50.960-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><title type='text'>More on OSS vs Commercial users</title><content type='html'>The striking difference in attitude between commercial users and open source users (e.g. more professionalism, gratitude and patience in the former than the latter) is most likely due to one of &lt;a href="http://chicagopsychology.org/influence/robert-cialdinis-6-principles-of-influence/"&gt;Cialdini's &lt;i&gt;Influences&lt;/i&gt;&lt;/a&gt;: Commitment (in the book, "commitment and consistency").&lt;br /&gt;&lt;br /&gt;It seems that the initial decision to spend money on software causes an emotional investment in the user. One would therefore expect that when a user has had the choice of software forced upon them (e.g. the use of Windows in a corporate environment, the use of an unfamiliar version control utility in a dev department, etc), they would be as impatient, rude, and generally unprofessional as a typical open source user.&lt;br /&gt;&lt;br /&gt;Informal observation bears this out, but it would be nice to see some studies demonstrating the effects -- a simple review of ticketing systems for similar projects (Apache/IIS, Eclipse/VisualStudio, etc) should bear some fruit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2600974205782370391?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2600974205782370391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/05/more-on-oss-vs-commercial-users.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2600974205782370391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2600974205782370391'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/05/more-on-oss-vs-commercial-users.html' title='More on OSS vs Commercial users'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4414841055242479841</id><published>2011-03-24T00:18:00.001-04:00</published><updated>2011-03-24T00:19:36.345-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Not as constant as one might think</title><content type='html'>One of those Ruby gotchas that has to lead to a bug report before it finally burns itself to memory:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;irb(main):001:0&amp;gt; TMP="1234"&lt;br /&gt;=&amp;gt; "1234"&lt;br /&gt;irb(main):002:0&amp;gt; t = TMP&lt;br /&gt;=&amp;gt; "1234"&lt;br /&gt;irb(main):003:0&amp;gt; t.succ!&lt;br /&gt;=&amp;gt; "1235"&lt;br /&gt;irb(main):004:0&amp;gt; TMP&lt;br /&gt;=&amp;gt; "1235"&lt;/div&gt;&lt;br /&gt;Eh? Isn't TMP a &lt;b&gt;constant&lt;/b&gt;, after all?&lt;br /&gt;&lt;br /&gt;Apparently this has to do with &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;t&lt;/span&gt; being assigned a reference to a (shared) String:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;irb(main):005:0&amp;gt; a="456"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;=&amp;gt; "456"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;irb(main):006:0&amp;gt; b=a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;=&amp;gt; "456"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;irb(main):007:0&amp;gt; b.succ!&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;=&amp;gt; "457"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;irb(main):008:0&amp;gt; a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;=&amp;gt; "457"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Needless to say, this will do the trick:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;t = TMP.dup&lt;/div&gt;&lt;br /&gt;This will raise an error on&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; t.succ!&lt;/span&gt;, for obvious reasons :&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;TMP='123'&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;TMP.freeze&lt;/div&gt;&lt;br /&gt;Moral: "Constants" are NOT frozen and their values are not copied on assignment!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4414841055242479841?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4414841055242479841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/03/not-as-constant-as-one-might-think.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4414841055242479841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4414841055242479841'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/03/not-as-constant-as-one-might-think.html' title='Not as constant as one might think'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2333925719179866198</id><published>2011-03-23T01:09:00.003-04:00</published><updated>2011-03-24T00:11:39.340-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Using git to synchronize and backup home directories</title><content type='html'>With git-home-history gone, and nothing suitable to take its place, one must handroll a decent solution to version controlling a home directory.&amp;nbsp; Since git can synchronize repositories, it should also be possible to use it to synchronize the contents of a home directory on multiple machines.&lt;br /&gt;&lt;br /&gt;This example will assume that a desktop computer (hostname 'desktop')contains the master ('remote') repository, and that a laptop (hostname 'laptop') contains the slave (local) repository.&lt;br /&gt;&lt;br /&gt;NOTE: It is important to be aware of which files should be left out of the repo. Because a laptop and a desktop will have different graphics hardware, the settings for GUI applications such as web browsers and window managers should not be in the repo. Also, files which change a lot (cache files, .bash_history, etc) should be kept out of the repo as they will always cause merge conflicts. Finally, private keys should be left out of the repo.&lt;br /&gt;&lt;br /&gt;Finally, need it be said that the home directories of both machines should be backed up before trying this? &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Desktop: Create and fill the repository&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;To begin with, create an empty git repo:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ cd ~&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git init .&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ touch .gitignore&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git add .gitignore&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Next, modify the .gitignore file to select which files or directories to leave out of the repo:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ vi .gitignore&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;.*&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Desktop&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Downloads&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Templates&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;tmp&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;mnt&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;*.log&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;*core&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;*.swp&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;*.swo&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;*.bak&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This example ignores backup, swap, and core files, as well as directories that probably shouldn't be shared between the two machines (Desktop, Downloads, Templates, tmp, mnt). Note that all hidden files are left out of the repo by default (.*).&lt;br /&gt;&lt;br /&gt;Now add all allowed files into the repo:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git add .&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Now add exceptions to the .gitignore file. These will be config files that are shared between the two machines:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git add -f .bashrc .xsessionrc .vimrc .gvimrc .gdbinitrc&amp;nbsp; .ssh/config .local/share/applications&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;If Firefox was configured correctly (i.e. by making a tarball of ~/.mozille/firefox on the desktop machine and extracting it to ~ on the laptop machine, instead of letting Firefox generate its own config), then the bookmarks file can be added as well:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git add -f .mozilla/firefox/*.default/bookmarks.html&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This of course holds true for non-config data in the Firefox dir, such as .mozilla/firefox/*.default/ReadItLater (UPDATE: but not zotero, as it updates itself even while it is not being modified) .&lt;br /&gt;&lt;br /&gt;Finally, commit all of the contents to the repo:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git commit -m 'Initial home dir checkin'&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;The git directory now has a starting version of the home directory checked in. It can be reviewed with a tool such as QGit to&amp;nbsp; ensure nothing is missing or unwanted:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ qgit &amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;NOTE: To make the following operations go smoothly, the following line must be added to .git/config : &lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;[receive]&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; denyCurrentBranch = false&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Desktop : Create a script to auto-commit &lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;At this point. it is useful to create a shell script that performs a commit in the background.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ mkdir -p bin&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ vi bin/git_commit_homedir.sh&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#!/bin/sh&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;cd ~&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;git add .&lt;br /&gt;git commit -m 'automated backup' .&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$chmod +x bin/git_commit_homedir.sh&lt;/span&gt; &lt;/div&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Laptop : Clone the repository&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;On the laptop, clone the repository from the desktop:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ cd ~&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ mkdir -p tmp/git-repo&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ cd tmp/git-repo&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git clone desktop:/home/$USER&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Note that the repo was cloned to a temporary directory so that it will not overwrite any local files. This is important! &lt;br /&gt;&lt;br /&gt;Move the git metadata directory to the home directory:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ cd $USER&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ mv .git ~&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Retrieve any missing files (i.e. that exist on the desktop but not on the laptop, such as .gitignore) from the repository:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ cd ~&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git checkout \*&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Laptop: Add local changes&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;Create a branch for the changes that will be made next:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git checkout -b laptop&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Add any local exclusions to the .gitignore file:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ echo .pr0n &amp;gt;&amp;gt; .gitignore&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Add any additional files to git:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git add TODO NOTES&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Commit the branch:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;git commit -m 'laptop additions' &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now merge the branch into master:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git checkout master&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git merge laptop&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Verify that the changes are suitable:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ qgit &amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Finally, push the changes to the desktop:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git push &lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Desktop: Generate canonical file versions&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;The desktop will now have all its files set to the laptop versions. &lt;br /&gt;&lt;br /&gt;At this point, files that have been modified should be reviewed and editted, so that a canonical version will be stored in the repo and used by both the desktop and the laptop. QGit makes the review process fairly simple.&lt;br /&gt;&lt;br /&gt;Note that some config files will have to source local config files that lie outside the repository (i.e they are excluded in /gitignore). For example, .bashrc might have a line like&lt;br /&gt;&lt;br /&gt;[&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; -f ~/.bash_local.rc ] &amp;amp;&amp;amp; . ~/.bash_local.rc&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...and .vimrc might have a line like&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;if filereadable(expand("$HOME/.vim_local.rc"))&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; source ~/.vim_local.rc&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The files .bashrc_local.rc and .vim_local.rc will be listed in .gitignore, and will have machine-specific configuration such as custom prompts, font size (e.g. in .gvimrc), etc.&lt;br /&gt;&lt;br /&gt;Once the canonical versions of the files have been created, they are committed :&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ git commit -, 'canonical version' .&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bash$ git tag 'canonical'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Laptop: Pull canonical versions&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;The canonical versions can now be pulled down to the laptop. Note that any supporting files (e.g. .bash_local.rc) will have to be created on the laptop.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bash$ git pull&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Laptop &amp;amp; Desktop : Add cron job&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;In order for git to automatically track changes to the home directory, both the laptop and the desktop will need to add a cron job for running git_commit_homedir.sh .&lt;br /&gt;&lt;br /&gt;The following crontab will run git every two hours:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash$ crontab -e&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;0 */2 * * * /home/$USER/bin/git_commit_homedir.sh 2&amp;gt;&amp;amp;1 &amp;gt; /dev/null&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;...of course $USER must be replaced with the actual username.&lt;br /&gt;&lt;br /&gt;Note: Some provision must be made for pushing the laptop repo to the desktop. This can be done in a cron job, but is probably better suited to an if-up (on network interface up) script.&lt;br /&gt;&lt;br /&gt;UPDATE: Be careful when pushing; the desktop must be forced to update its working tree, or its next commit will delete files on the laptop. The following script will do the trick:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &lt;span style="font-size: x-small;"&gt;#!/bin/sh&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;cd $HOME&lt;br /&gt;&lt;br /&gt;git push &amp;amp;&amp;amp; ssh desktop 'git reset --merge `git rev-list --max-count=1 master`'&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Of course passwordless ssh should be set up for this to work. A similar problem exists when pulling from the server: a "&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;git checkout \*&lt;/span&gt;" must be performed to create any missing files.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;Desktop: Add backup script and cron job&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;At this point, a backup script and cron job can be added to the desktop server. The directory ~/.git is all that needs to be backed up; a shell script can rsync it to a server.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2333925719179866198?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2333925719179866198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/03/using-git-to-synchronize-and-backup.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2333925719179866198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2333925719179866198'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/03/using-git-to-synchronize-and-backup.html' title='Using git to synchronize and backup home directories'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-6053395417094021327</id><published>2011-03-10T02:48:00.000-05:00</published><updated>2011-03-10T02:48:33.902-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='kde'/><title type='text'>Running .desktop files from the command line</title><content type='html'>After making a few .desktop files with custom commands in them (e.g.RXVT settings), it is useful to be able to run those same commands from the occasional terminal.&lt;br /&gt;&lt;br /&gt;When KDE is installed, the kioclient can be used for this purpose:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;kioclient exec file:/PATH_TO_DESKTOP_FILE&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Custom desktop files are stored in ~/.local/share/applications, so this is easy to wrap in a shell function for inclusion in ~/.bashrc :&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;xdg-exec () {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; kioclient exec file:${HOME}/.local/share/applications/${1}.desktop&lt;br /&gt;}&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Example:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bash$ xdg-exec rxvt-unicode&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-6053395417094021327?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/6053395417094021327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/03/running-desktop-files-from-command-line.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6053395417094021327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6053395417094021327'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/03/running-desktop-files-from-command-line.html' title='Running .desktop files from the command line'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8341648721415553743</id><published>2011-03-03T00:13:00.000-05:00</published><updated>2011-03-03T00:13:19.803-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='mvc'/><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><title type='text'>Ruby, Qt4, and AbstractItemModel</title><content type='html'>There are no good examples of using QAbstractItemModel in Ruby. For most purposes, QStandardItemModel will suffice. In this particular case, which calls for a lazily-loaded tree, QStandardItemModel will not cut it.&lt;br /&gt;&lt;br /&gt;What follows is a simple implementation. The number of columns is limited to 1, items are read-only, there is no DnD support, and the model only supports the Display role for item data. Needless to say, these features are easy enough to implement, and would only distract from the example of subclassing Qt::AbstractItemModel.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Model&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;A basic tree model. The contents of all tree nodes are determined by the ModelItems, not the Model, so they may be loaded lazily.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;class Model &amp;lt; Qt::AbstractItemModel&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; signals 'dataChanged(const QModelIndex &amp;amp;, const QModelIndex &amp;amp;)'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;The invisible root item in the tree.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; attr_reader :root &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def initialize(parent=nil)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; super&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @root = nil&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Load data into Model. This just creates a few fake items as an example. A full implementation would create and fill the top-level items after creating.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;Note: @root is created here in order to make clearing easy. A clear() method just needs to set root to ModelItem.new('').&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def load()&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @root = ModelItem.new('',nil)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ModelItem.new('First', @root)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ModelItem.new('Second', @root)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;This treats an invalid index (returned by Qt::ModelIndex.new) as the index of @root.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;All other indexes have the item itself stored in the 'internalPointer' field.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;See AbstractItemModel#createIndex.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def itemFromIndex(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return @root if not index.valid?&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; index.internalPointer&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Return the index of the parent item for 'index'.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;The  key here is to treat the invalid index (returned by Qt::ModelIndex.new)  as the index of @root. All other (valid) indexes are generated by  AbstractItemModel#createIndex. Note that the item itself is passed as the third parameter (internalPointer) to createIndex.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;See ModelItem#parent and ModelItem#childRow. &lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;=end&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def index(row, column=0, parent=Qt::ModelIndex.new)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item = itemFromIndex(parent)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if item&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; child = item.child(row)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return createIndex(row, column, child) if child&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Qt::ModelIndex.new&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;=begin rdoc&lt;br /&gt;Return the index of the parent item for 'index'.&lt;br /&gt;This is made a bit complicated by the fact that the ModelIndex must be created by AbstractItemModel. &lt;br /&gt;&lt;br /&gt;The parent of the parent is used to obtain the 'row' of the parent. If the parent is root, the invalid Modelndex is used as usual.&lt;br /&gt;=end&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def parent(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Qt::ModelIndex.new if not index.valid?&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item = itemFromIndex(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; parent = item.parent&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Qt::ModelIndex.new if parent == @root&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; pparent = parent.parent&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return Qt::ModelIndex.new if not pparent&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; createIndex(pparent.childRow(parent), 0, parent)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Return data for ModelItem. This only handles the case where Display Data (the text in the Tree) is requested.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def data(index, role)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; return Qt::Variant.new if (not index.valid?) or role != Qt::DisplayRole&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item = itemFromIndex(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item ? item.data : Qt::Variant.new&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Set data in a ModelItem. This is just an example to show how the signal is emitted.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def data=(index, value, role)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false if (not index.valid?) or role != Qt::DisplayRole&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item = itemFromIndex(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; return false if not item &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item.data = value.to_s&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; emit dataChanged(index, index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; true&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; alias :setData :data=&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Delegate rowCount to item.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;See ModelItem#rowCount.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def rowCount(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp; item = itemFromIndex(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item ? item.rowCount : 0&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Only support 1 column&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def columnCount(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;All items can be enabled only.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def flags(index)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Qt::ItemIsEnabled&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Don't supply any header data.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def headerData(section, orientation, role)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Qt::Variant.new&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;end &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;b&gt;The ModelItem&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;An example of a ModelItem for use in the above Model. Note that it does not need to descend from QObject.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;The ModelItem consists of a data member (the text displayed in the tree), a parent ModelItem, and an array of child ModelItems. This array corresponds directly to the Model 'rows' owned by this item.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;class ModelItem&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; attr_accessor :data&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; attr_accessor :parent&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; attr_reader :children&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def initialize(data, parent=nil)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @data = data&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @parent = parent&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @children = []&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; parent.addChild(self) if parent&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;Return the ModelItem at index 'row' in @children.&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def child(row)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @children[row]&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;Return row of child that matches 'item'.&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def childRow(item)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @children.index(item)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;Return number of children. This can be made lazy by using a data source (e.g. database, filesystem) instead of an array for @children.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def rowCount&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @children.size&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;Used to determine if the item is expandible.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def hasChildren&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; childCount &amp;gt; 0&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=begin rdoc&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;Add a child to this ModelItem. This puts the item into @children.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;=end&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; def addChild(item)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; item.parent=self&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @children &amp;lt;&amp;lt; item&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This should serve as a basic implementation of an AbstractItemModel.&lt;br /&gt;&lt;br /&gt;Realistically, ModelItem would be subclassed to represent different types of items in the data source, each of which would also (likely) be subclassed from ModelItem. This allows a browsable tree to be created for navigating data hierarchies.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8341648721415553743?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8341648721415553743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/03/ruby-qt4-and-abstractitemmodel.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8341648721415553743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8341648721415553743'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/03/ruby-qt4-and-abstractitemmodel.html' title='Ruby, Qt4, and AbstractItemModel'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4808902568407627365</id><published>2011-02-08T20:34:00.000-05:00</published><updated>2011-02-08T20:34:28.540-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='words'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The way it goes</title><content type='html'>&lt;b&gt;Toy Code&lt;/b&gt; : Working source code which is extensively peer-reviewed, obsessively tested, well-documented, and released once. Often used in articles, examples, mailing lists, and blog posts/comments.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Production Code&lt;/b&gt;: Working source code which is rarely reviewed, infrequently tested, poorly documented, and regularly released. Responsible for the majority of the world's electronic infrastructure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4808902568407627365?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4808902568407627365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/02/way-it-goes.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4808902568407627365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4808902568407627365'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/02/way-it-goes.html' title='The way it goes'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8606915097772223599</id><published>2011-01-16T15:48:00.002-05:00</published><updated>2011-01-16T15:52:28.093-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Ruby: Array of Hashes to 2-D Array</title><content type='html'>Quick Ruby trick.&lt;br /&gt;&lt;br /&gt;It is often useful to convert an Array of Hashes (representing a table of objects) to an Array of column names (table header) and an Array of rows (table data). The canonical example would be taking the result of a DB query (ala Sequel) and displaying it in an HTML table (ala DataTables).&lt;br /&gt;&lt;br /&gt;Without further ado, here is a proper map/inject one-liner:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;array.inject( [data.first.keys, []] ) { |memo, row| memo[1] &amp;lt;&amp;lt; memo[0].map { |key| row[key] } ; memo }&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A 2-dimensional Array is returned. The first dimension contains the column names, and the second contains the row data (in the same order as the column names, which is the tricky part as &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Hash#keys&lt;/span&gt; can return the keys in any order).&lt;br /&gt;&lt;br /&gt;The columns can be sorted (or otherwise ordered specifically) by manipulating &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;data.first.keys&lt;/span&gt; when it is initially stored in &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;memo[0]&lt;/span&gt;. &lt;br /&gt;&lt;br /&gt;Example of usage:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;irb &amp;gt; data = [ { a: 1, b: 2, c: 3 }, { c: 9, b: 8, a: 7 } ]&lt;br /&gt;irb &amp;gt;&amp;nbsp; cols, rows = data.inject( [data.first.keys, []] ) { |arr, row| arr[1] &amp;lt;&amp;lt; arr[0].map { |key| row[key] } ; arr }&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;irb &amp;gt; cols&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;=&amp;gt; [:a, :b, :c]&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;irb &amp;gt; rows&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;=&amp;gt; [[1, 2, 3], [7, 8, 9]]&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8606915097772223599?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8606915097772223599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/01/ruby-array-of-hashes-to-2-d-array.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8606915097772223599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8606915097772223599'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/01/ruby-array-of-hashes-to-2-d-array.html' title='Ruby: Array of Hashes to 2-D Array'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-6152123399962341097</id><published>2011-01-06T03:11:00.001-05:00</published><updated>2011-01-06T03:13:12.035-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><title type='text'>QScintilla and getSelection()</title><content type='html'>&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;The recent Ubuntu upgrade and subsequent Ruby woes were caused, of course, by the desire to install libqscintilla-ruby, a package only available on 10.10 (and even then, only for 1.8).&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;For the most part, QScintilla works fine in Ruby... until one encounters methods like this:&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;void getCursorPosition(int *line, int *index)&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;void getSelection(int *lineFrom, int *indexFrom, int *lineTo, int *indexTo) &lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The docs provide some hint as to the problem:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;span style="font-size: xx-small;"&gt;If there is a selection, *lineFrom is set to the line number in which the selection begins and *lineTo is set to the line number in which the selection ends. (They could be the same.) *indexFrom is set to the index at which the selection begins within *lineFrom, and *indexTo is set to the index at which the selection ends within *lineTo. If there is no selection, *lineFrom, *indexFrom, *lineTo and *indexTo are all set to -1.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;How does one pass an integer by reference in Ruby?&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;The answer: one doesn't. These functions take integer arguments and return nil, making them entirely useless in Ruby.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: inherit;"&gt;&lt;span style="font-size: small;"&gt;The Python guys did it right:&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;line_fro, idx_fro, line_to, idx_to = getSelection&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;The Ruby guys, of course, were lazy, and routed all calls directly to libqscintilla.so regardless of the sanity of their argument lists.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;There is a way to make things work, however, thanks to &lt;b&gt;ScintillaBase&lt;/b&gt;.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: inherit;"&gt;This, the base class of the Scintilla widget, provides the following method:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;long SendScintilla(unsigned int msg, unsigned long wParam=0, long lParam=0)&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;At the top of&amp;nbsp; the base class&lt;/span&gt;&lt;span style="font-size: small;"&gt; documentation are a bunch of enums that look promising:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;... &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp;SCI_SETSELECTIONSTART =  2142,&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp;SCI_GETSELECTIONSTART =  2143,  &lt;br /&gt;&amp;nbsp;SCI_SETSELECTIONEND =  2144,  &amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp;SCI_GETSELECTIONEND =  2145,&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: xx-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;... &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;Sure enough, these turn out to be the values for the &lt;i&gt;msg&lt;/i&gt; parameter.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;It makes for short work to add the following methods to the &lt;b&gt;Scintilla&lt;/b&gt; object:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;def get_sel_start&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp; &amp;nbsp; # SCI_GETSELECTIONSTART &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.SendScintilla(2143), 0, 0)&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;def get_sel_end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt; &amp;nbsp;&amp;nbsp; # SCI_GETSELECTIONEND&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.SendScintilla(2145), 0, 0)&lt;br /&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;def get_current_pos&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; # SCI_GETCURRENTPOS&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.SendScintilla(2008), 0, 0)&lt;br /&gt;end&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;Yes, the messages have to be passed by number, as the symbols for the bulk of the messages have not been defined as constants in Ruby (lazy! bad! lazy!), as can be determined by examining &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Qsci::ScintilaBase.constants&lt;/span&gt;.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-6152123399962341097?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/6152123399962341097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/01/qscintilla-and-getselection.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6152123399962341097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6152123399962341097'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/01/qscintilla-and-getselection.html' title='QScintilla and getSelection()'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4537471704567422445</id><published>2011-01-03T03:40:00.003-05:00</published><updated>2011-01-04T15:18:35.237-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='upgrade rage'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Ubuntu getting crappier and crappier</title><content type='html'>Stupidly made the decision to upgrade to 10.10 (for a single package!) on the main laptop (mbpro 5,5) and poof! No wireless!&lt;br /&gt;&lt;br /&gt;Seriously, it's been three releases since an ethernet cable was required to do an upgrade.&lt;br /&gt;&lt;br /&gt;Piece.&lt;br /&gt;Of.&lt;br /&gt;Shit.&lt;br /&gt;&lt;br /&gt;Only question now is how many hours of productivity are going to be lost fixing what *was* a perfectly working system before the upgrade.&lt;br /&gt;&lt;br /&gt;The sad fact is that os x and windows are just as unreliable for upgrades (while being less usable), and FreeBSD refuses to even glance at this hardware.&lt;br /&gt;&lt;br /&gt;A decade ago, this shit used to *work*!&lt;br /&gt;&lt;br /&gt;Running log of the fixes:&lt;br /&gt;&lt;br /&gt;* apt-get install gnome-icon-theme to get NetworkManager running again. There is no fix for Wicd; it has apparently "stopped working".&lt;br /&gt;&lt;br /&gt;* apt-get install gnome-alsa-mixer to un-mute the sound. Kmixer has been reduced from 7 or so channels to 1 (without a mute option).&lt;br /&gt;&lt;br /&gt;* uninstall ruby 1.9 via apt-get. Download and install rvm (system-wide as this is a workstation), then do 'sudo rvm --default use 1.9.2' to make 1.9 the system-wide default (might roll that back later).&lt;br /&gt;&lt;br /&gt;* reinstall all 1.9 gems.&lt;br /&gt;&lt;br /&gt;* rebuild the passenger apache module ('sudo passenger-install-apache2-module') and update the config files to point to the new location.&lt;br /&gt;&lt;br /&gt;* fix all rack-based webapps to include the line&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;: &amp;lt;&amp;lt; File.dirname(__FILE__)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;in config.ru and in each Sinatra::Base application file, as something got screwed in the ruby-passenger-rack environment, and PassengerRoot is no longer in the Ruby module path.&lt;br /&gt;&lt;br /&gt;See? A smooth, seamless upgrade! You almost don't even notice that it happened!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4537471704567422445?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4537471704567422445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2011/01/ubuntu-getting-crappier-and-crappier.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4537471704567422445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4537471704567422445'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2011/01/ubuntu-getting-crappier-and-crappier.html' title='Ubuntu getting crappier and crappier'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4674777589405418458</id><published>2010-12-16T00:51:00.001-05:00</published><updated>2010-12-16T01:01:04.853-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='sinatra'/><category scheme='http://www.blogger.com/atom/ns#' term='passenger'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Apache + Passenger + Sinatra</title><content type='html'>OK, this seems like a pretty normal situation, and like most normal situations it apparently never occurs in nature (judging by the available docs).&lt;br /&gt;&lt;br /&gt;An existing Apache webserver is to have a new webapp added to it, in a subdirectory of the DocumentRoot. Sinatra is the framework to be used, and Passenger is going to route requests from Apache to Sinatra without going through any mod_proxy or mod_rewrite business.&lt;br /&gt;&lt;br /&gt;Assume the following: Apache2, an Ubuntu system, and all of the relevant gems have been apt-get installed. The web server root directory is /var/www, and the webapp will be in /var/www/timon.&lt;br /&gt;&lt;br /&gt;First, update the Apache config file (/etc/apache2/apache2/conf):&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&amp;lt;VirtualHost *:80&amp;gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp; ServerName Apemantus&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp; DocumentRoot /var/www&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp; &amp;lt;Directory /&amp;gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; Options -Indexes&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; AllowOverride None&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp; &amp;lt;/Directory&amp;gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp; &amp;lt;Directory /var/www/&amp;gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; AllowOverride AuthConfig&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Order allow,deny&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;nbsp; &amp;lt;/Directory&amp;gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; &lt;b&gt;SetEnv RUBYLIB '/var/www/timon'&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; PassengerEnabled on&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; PassengerAppRoot /var/www/timon&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; RackBaseURI /timon&lt;/span&gt;&lt;/b&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; &amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;The bold lines indicate what must be added.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;SetEnv&lt;/b&gt; : This just allows the code in subdirectories of the webapp to be easily required. &lt;/li&gt;&lt;li&gt;&lt;b&gt;PassengerEnabled&lt;/b&gt; : Seems fairly obvious.&lt;/li&gt;&lt;li&gt;&lt;b&gt;PassengerAppRoot&lt;/b&gt; : The full filesystem path to the webapp directory.&lt;/li&gt;&lt;li&gt;&lt;b&gt;RackBaseURI&lt;/b&gt; : The relative (to DocumentRoot) path to the webapp directory.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Next, verify that the Passenger options (/etc/apache2/mods-enabled/passenger.conf) are correct:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: xx-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&amp;lt;IfModule mod_passenger.c&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; PassengerRoot /usr&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; PassengerRuby /usr/bin/ruby&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;b style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; PassengerMaxPoolSize 10&lt;br /&gt;&amp;nbsp; PassengerDefaultUser www-data&lt;/b&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;lt;/IfModule mod_passenger.c&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;These options are pretty straightforward. The only thing to note is that &lt;b&gt;PassengerRuby&lt;/b&gt; can be set to a specific version of Ruby, e.g. jruby or ruby1.9.&lt;br /&gt;&lt;br /&gt;Now, create an empty Rack-friendly directory structure for the webapp:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;bash# cd /var/www&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;bash# mkdir timon&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;bash# mkdir timon/public&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;bash# mkdir timon/tmp &lt;/span&gt;&lt;/div&gt;&lt;br /&gt;The use of &lt;b&gt;public&lt;/b&gt; for static pages and &lt;b&gt;tmp&lt;/b&gt; for restart.txt is well-documented.&lt;br /&gt;&lt;br /&gt;Next, a Rack config file must be provided. This will be named &lt;b&gt;config.ru&lt;/b&gt; (&lt;i&gt;/var/www/timon/config.ru&lt;/i&gt;) and will have the following contents:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'rubygems'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'sinatra'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;# Disable Sinatra's default Webrick instance&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;set :run, false&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;# Include timon.rb, the main webapp script&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'timon'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;run Sinatra::Application&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Finally, the app itself must be provided. This will be named &lt;b&gt;timon.rb&lt;/b&gt; (&lt;i&gt;/var/www/timon/timon.rb&lt;/i&gt;) and will have the following contents:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'rubygems'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'sinatra'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;get '/' do&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp; "&amp;lt;b&amp;gt;TIMON!&amp;lt;/b&amp;gt;"&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;# Local 404 handler&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;not_found do&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp; "Timon NotFound exception"&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;# Local error handler&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;error do&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&amp;nbsp; "Timon Error: " + env['sinatra_error'].name&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Debugging can be made a bit more straightforward by adding some basic logging code to &lt;b&gt;config.ru&lt;/b&gt;:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'rubygems'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'sinatra'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;# Disable Sinatra's default Webrick instance&lt;/span&gt; &lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;set :run, false&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;b&gt;&lt;span style="font-size: xx-small;"&gt;# Local logging&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;b&gt;&lt;span style="font-size: xx-small;"&gt;FileUtils.mkdir_p 'log' unless File.exists?('log')&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;b&gt;&lt;span style="font-size: xx-small;"&gt;log = File.new('log/sinatra.log', 'a')&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;b&gt;&lt;span style="font-size: xx-small;"&gt;$stdout.reopen(log)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;&lt;b&gt;$stderr.reopen(log)&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;# Include timon.rb, the main webapp script&lt;/span&gt; &lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;require 'timon'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: xx-small;"&gt;run Sinatra::Application&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;That's it.&lt;br /&gt;&lt;br /&gt;Nothing much to it, really, but the lack of &lt;b&gt;RackBaseURI&lt;/b&gt; in the relevant examples really makes debugging this kind of thing difficult.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4674777589405418458?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4674777589405418458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/12/apache-passenger-sinatra.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4674777589405418458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4674777589405418458'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/12/apache-passenger-sinatra.html' title='Apache + Passenger + Sinatra'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-5928463051725196624</id><published>2010-11-30T22:41:00.001-05:00</published><updated>2010-11-30T22:44:20.615-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Ruby CGI and Javascript</title><content type='html'>After a brief perusal of the Ruby CGI module documentation, it doesn't seem like there is a good way to generate Javascript from within it.&lt;br /&gt;&lt;br /&gt;Due to the &lt;b&gt;method_missing&lt;/b&gt; way that the CGI module handles HTML tags, however, it turns out to be quite simple: invoke &lt;b&gt;cgi.script&lt;/b&gt;, passing all tag parameters as a hash, and pass the Javascript code as a string in the block:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; cgi.out {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.html {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.head {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.title {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Test"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } +&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Javascript library to include&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.script( 'src' =&amp;gt; 'dygraph-combined.js', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'type' =&amp;gt; 'text/javascript') {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } +&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.body {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.br +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.div( 'id' =&amp;gt; 'graphdiv' ) +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.br +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; cgi.script( 'type' =&amp;gt; 'text/javascript') {&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Javascript to execute&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'g = new Dygraph(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; document.getElementById("graphdiv"),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Date,Temperature\n" +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "2008-05-07,75\n" +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "2008-05-08,70\n" +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "2008-05-09,80\n"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; '&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-5928463051725196624?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/5928463051725196624/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/11/ruby-cgi-and-javascript.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5928463051725196624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5928463051725196624'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/11/ruby-cgi-and-javascript.html' title='Ruby CGI and Javascript'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-5309165741785323825</id><published>2010-10-18T17:38:00.000-04:00</published><updated>2010-10-18T17:38:06.812-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='regex'/><title type='text'>internationalization regex</title><content type='html'>Just a quick vim regex that's useful when adding internationalization to a file:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;:%s/\("\([^\\]\\"\|[^"]\)\+"\|'\([^\\]\\'\|[^']\)\+'\)/gettext(\1)/&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Wraps all quoted strings in a gettext() call, preserving inner (and escaped) quotes. Doing this in vim instead of sed allows a cursory review of the changes in case any of them * shouldn't* be changed (e.g. log strings).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-5309165741785323825?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/5309165741785323825/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/10/internationalization-regex.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5309165741785323825'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5309165741785323825'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/10/internationalization-regex.html' title='internationalization regex'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-2912728031391747210</id><published>2010-08-16T02:59:00.000-04:00</published><updated>2010-08-16T02:59:13.538-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Indeed.</title><content type='html'>&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;I was astonished to learn how accomplished both of my children are in programming. It is a skill that an entire generation of adolescents is learning underground, much the way we used to pick up dirty words.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;-- S. Milgram, 1982&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-2912728031391747210?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/2912728031391747210/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/08/indeed.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2912728031391747210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/2912728031391747210'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/08/indeed.html' title='Indeed.'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-5915730762678447696</id><published>2010-08-15T02:30:00.001-04:00</published><updated>2010-08-15T03:12:10.462-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='praat'/><title type='text'>Controlling a command-line interpreter in Ruby</title><content type='html'>There seem to be a lot of posts to mailing lists, forums, etc in regards to controlling a child process in Ruby via &lt;b&gt;STDIN&lt;/b&gt; and &lt;b&gt;STDOUT&lt;/b&gt;. Most of the discussion (with &lt;a href="http://devver.wordpress.com/2009/10/12/ruby-subprocesses-part_3"&gt;this&lt;/a&gt; notable exception) ends with "use &lt;b&gt;open3&lt;/b&gt;!" or "use &lt;b&gt;open4&lt;/b&gt;!".&lt;br /&gt;&lt;br /&gt;Anyone who has ever tried to control (i.e., tried to send more than one command to) an interpreter using either of these recognise their shortcomings immediately: the child process &lt;b&gt;STDOUT&lt;/b&gt; cannot be read until &lt;b&gt;STDIN&lt;/b&gt; has been closed.&lt;br /&gt;&lt;br /&gt;Fortunately, the &lt;b&gt;pty&lt;/b&gt; module (helpfully mentioned in the notable exception) allows the interpreter to be controlled properly as long as the OS supports psuedo-terminals -- that is, as long as it is a UNIX-like OS (i.e. Linux, OS X, *BSD... basically every desktop/server OS but Windows). &lt;br /&gt;&lt;br /&gt;It is a bit tricky to get working well, due to the problem of not knowing for sure whether the child process is preparing more data to write to &lt;b&gt;STDOUT&lt;/b&gt;, or is waiting for another command on &lt;b&gt;STDIN&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;The following implementation wraps the &lt;b&gt;praat&lt;/b&gt; speech analysis software. It uses the &lt;b&gt;praat&lt;/b&gt; prompt ('Praat &amp;gt; ') to determine when the child process is ready for more input (i.e., when it can stop reading from &lt;b&gt;STDOUT&lt;/b&gt;).&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#!/usr/bin/env ruby&lt;br /&gt;&lt;br /&gt;require 'pty'&lt;br /&gt;&lt;br /&gt;module Praat&lt;br /&gt;&lt;br /&gt;&amp;nbsp; class Interpreter&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; attr_reader :stdout, :stdin, :pid&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; PROMPT='Praat &amp;gt; '&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def initialize(program='praat')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @stdout, @stdin, @pid = PTY.spawn( 'praat', '-' )&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Read initial prompt from pipe&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; read_until_prompt&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if block_given?&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; yield self&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @stdin.close&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @stdout.close&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @stdin = @stdout = @pid = nil&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def read_until_prompt&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; outbuf = buf = ''&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Read from child STDOUT until &amp;gt; 0 bytes have been read&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # (i.e. wait for child process to finish reading input)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while buf.length == 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IO.select([@stdout])&amp;nbsp; # block until child process is ready&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @stdout.read_nonblock( 1024, buf )&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rescue Exception =&amp;gt; e&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; buf = ''&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # READ failure! Try again.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Read from child STDOUT until 0 bytes are read or a line ending in a&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # prompt (i.e. next input prompt) was encountered.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while buf.length &amp;gt; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; outbuf &amp;lt;&amp;lt; buf&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # complete read if next input prompt is encountered&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break if outbuf =~ /#{PROMPT}$/&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; begin&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; buf = ''&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @stdout.read_nonblock( 1024, buf )&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rescue Errno::EAGAIN =&amp;gt; e&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IO.select([@stdout])&amp;nbsp; # block until child process is ready&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; retry&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rescue Exception =&amp;gt; e&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; buf=''&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # READ failure. Exit loop.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Return output of interpreter as an array of lines&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return outbuf.split("\n").each { |x| x.chomp! }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Send a command to the interpreter. Returns an array of the output.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; # If include_prompts is true, lines beginning with a prompt will NOT be&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; # stripped from the output.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def send( command, include_prompts=false )&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @stdin.write(command + "\n")&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; outbuf = read_until_prompt&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Ignore all ECHOed lines before the first (input) prompt&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; first_prompt = outbuf.find_index { |x| x =~ /^#{PROMPT}/ }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result = outbuf.slice(first_prompt, outbuf.length-first_prompt)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # Return full results, or the results with prompt lines removed&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result = result.select {|x| x !~ /^#{PROMPT}/} if not include_prompts&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; yield result if block_given?&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return result&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;if __FILE__ == $0&lt;br /&gt;&lt;br /&gt;&amp;nbsp; puts 'Testing block implementation'&lt;br /&gt;&amp;nbsp; Praat::Interpreter.new() { |p| puts p.send('echo BLOCK TEST') }&lt;br /&gt;&lt;br /&gt;&amp;nbsp; Praat::Interpreter.new() do |p| &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; p.send('echo Full Output', true).each { |line| puts "\t" + line }&lt;br /&gt;&amp;nbsp; end&lt;br /&gt;&lt;br /&gt;&amp;nbsp; puts 'Testing object implementation'&lt;br /&gt;&amp;nbsp; praat = Praat::Interpreter.new()&lt;br /&gt;&lt;br /&gt;&amp;nbsp; lines = []&lt;br /&gt;&amp;nbsp; lines.concat praat.send('echo OBJ TEST 1' )&lt;br /&gt;&amp;nbsp; lines.concat praat.send('echo OBJ TEST 2' )&lt;br /&gt;&amp;nbsp; lines.concat praat.send('echo OBJ TEST 3' )&lt;br /&gt;&lt;br /&gt;&amp;nbsp; puts lines.inspect&lt;br /&gt;end&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-5915730762678447696?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/5915730762678447696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/08/controlling-command-line-interpreter-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5915730762678447696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5915730762678447696'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/08/controlling-command-line-interpreter-in.html' title='Controlling a command-line interpreter in Ruby'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-3446148741955538536</id><published>2010-08-15T02:11:00.000-04:00</published><updated>2010-08-15T02:11:15.960-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Examining .gem files</title><content type='html'>The gem(1) utility has a lot of useful commands for managing gem repositories. It only provides two commands for manipulating .gem files, however: &lt;b&gt;build&lt;/b&gt; and &lt;b&gt;install&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;When building your own gems, or examining a gem before install, it is useful to be able to take a look at the contents without extracting the gem to a temporary directory.&lt;br /&gt;&lt;br /&gt;The structure of the gem is simple: a tar file containing a tarball of the gem contents, and a compressed file containing the metadata:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;bash#  tar -tf /tmp/test.gem &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;tar: Record size = 19 blocks&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;data.tar.gz&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;metadata.gz&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Both tar and gzip support input and output on STDOUT, so it is easy enough to write shell functions to access the contents and metadata:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem_contents () {&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; tar -xOf $1 data.tar.gz | tar -ztf -&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; return $?&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem_metadata () {&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; tar -xOf $1 metadata.gz | gzip -d&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; return $?&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This provides quick access to the contents and metadata of a gem file:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem_contents /tmp/test.gem&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem_metatdata /tmp/test.gem&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-3446148741955538536?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/3446148741955538536/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/08/examining-gem-files.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3446148741955538536'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3446148741955538536'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/08/examining-gem-files.html' title='Examining .gem files'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8678875678342866473</id><published>2010-08-15T02:10:00.003-04:00</published><updated>2010-08-15T02:17:31.354-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c/c++'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><title type='text'>Unit Testing in C</title><content type='html'>There are a number of unit test frameworks for C, but they all seem  to lack something.&lt;br /&gt;&lt;br /&gt;Sure, each can be used to perform  automated testing, and some will even generate test stubs for you. But  all of them are limited to &lt;i&gt;API testing&lt;/i&gt;; that is, the only  functions that can be unit-tested are the public API exported in the  header files. &lt;br /&gt;&lt;br /&gt;C differs from object-oriented languages  in that most of the work -- the &lt;i&gt;units&lt;/i&gt; to be tested, as it were  -- is done in static functions that are not exported. This makes  unit-testing, as it stands, all but useless from a C programmer's  perspective; there is no compelling reason to use unit tests over the  usual "test programs running on test data and returning 0 or 1 to the  makefile" approach.&lt;br /&gt;&lt;br /&gt;With some small work, however, it  is possible to apply a unit test framework to a C project, and to  perform actual unit tests (i.e. on static functions). It is a bit ugly,  it is a bit invasive (naturally each .c file must contain its own unit  tests), but it is scalable and maintainable.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Unit Testing with CHECK&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://check.sourceforge.net/"&gt;check&lt;/a&gt;&lt;/b&gt; is a unit testing framework for C (&lt;a href="http://check.sourceforge.net/doc/check_html/index.html"&gt;manual&lt;/a&gt;). It is primarily designed to be used with the GNU autotools (autoconf, automake, etc), and the documentation is not clear on integrating &lt;b&gt;check&lt;/b&gt; with a standard Makefile-based project. The unit tests developed here will serve as an example.&lt;br /&gt;&lt;br /&gt;To begin, assume a project with the following directory structure:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Makefile&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;stuff.c&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;stuff.h&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;tests/&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;tests/Makefile &lt;/span&gt;&lt;/div&gt;&lt;br /&gt;The main program, the shared library &lt;i&gt;libstuff.so&lt;/i&gt;, has its API in &lt;i&gt;stuff.h&lt;/i&gt;, its code in &lt;i&gt;stuff.c&lt;/i&gt;, and is built by the &lt;i&gt;Makefile&lt;/i&gt;. The unit tests run by &lt;b&gt;check&lt;/b&gt; will be in the tests director.y&lt;br /&gt;&lt;br /&gt;&lt;b&gt;stuff.h&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#ifndef STUFF_H&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#define STUFF_H&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;double do_stuff( double i  );&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#endif&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;stuff.c&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#include &amp;lt;math.h&amp;gt;&lt;br /&gt;#include "stuff.h"&lt;br /&gt;&lt;br /&gt;static double step1( double i ) { return i * i; }&lt;br /&gt;static double step2( double i ) { return i + i; }&lt;br /&gt;static double step3( double i ) { return pow( i, i ); }&lt;br /&gt;&lt;br /&gt;double do_stuff( double i ) { return step3( step2( step1( i ) ) ); }&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Makefile&lt;/b&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# Library Makefile&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#  ------------------------------------------------------------------- &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;NAME&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; stuff&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LIBNAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; lib$(NAME).so&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;ARCHIVE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; lib$(NAME).a&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;DEBUG&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  -DDEBUG -ggdb&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;OPT&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; -O2 &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;ERR&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; -Wall&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;INC_PATH&amp;nbsp;&amp;nbsp;&amp;nbsp;  =&amp;nbsp;&amp;nbsp;&amp;nbsp; -I. &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LIB_PATH&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#----------------------------------------------------------------&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;CC &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp; gcc&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LD&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  =&amp;nbsp;&amp;nbsp;&amp;nbsp; ld&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;AR&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; ar rc&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;RANLIB&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; ranlib&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;RM&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; rm -f&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LIBS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; -lm &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;CC_FLAGS&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp; $(INC_PATH) $(DEBUG) $(OPT) $(ERR) -fPIC&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LD_FLAGS&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; $(LIB_PATH) $(LIBS) -shared  -soname=$(LIBNAME)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;SRC&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp; stuff.c&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;OBJ&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp; stuff.o&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#----------------------------------------------------------  Targets&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;all: $(LIBNAME)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;.PHONY: all clean check&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;$(ARCHIVE): $(OBJ)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(AR) $(ARCHIVE) $^&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(RANLIB) $(ARCHIVE)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;$(LIBNAME): $(ARCHIVE)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(LD) $(LD_FLAGS) --whole-archive $&amp;lt;  --no-whole-archive -o  $@ &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;.c.o: $(SRC)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(CC) $(CC_FLAGS) -o $@ -c $&amp;lt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;clean:&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ -f $(LIBNAME) ] &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; $(RM) $(LIBNAME)|| [ 1  ]&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ -f $(ARCHIVE) ] &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; $(RM) $(ARCHIVE)|| [ 1 ]&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ -f $(OBJ) ] &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; $(RM) $(OBJ) || [ 1 ]&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; cd tests &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; make clean&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;check: $(LIBNAME)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; cd tests &amp;amp;&amp;amp; make &lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; make  check&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;So far, this is all pretty straightforward stuff. The &lt;i&gt;Makefile&lt;/i&gt; in the &lt;i&gt;tests&lt;/i&gt; directory will contain all of the &lt;b&gt;check&lt;/b&gt;-specific settings.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;tests/Makefile&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# Unit-test Makefile&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#--------------------------------------------------------- Definitions&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TGT_NAME&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; stuff&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TGT_SRC&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; ../stuff.c&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;OPT&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; -O2  -fprofile-arcs -ftest-coverage&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;ERR&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  =&amp;nbsp;&amp;nbsp;&amp;nbsp; -Wall&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;INC_PATH&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; -I.  -I..&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LIB_PATH&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; -L..&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LD_PATH&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp; ..&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#--------------------------------------------------------- &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;CC &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp; gcc&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;RM&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  =&amp;nbsp;&amp;nbsp;&amp;nbsp; rm -f&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;CHECK_LIBS&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  -lcheck&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LIBS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  -l$(TGT_NAME)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# NOTE: UNIT_TEST  enables the static-function test case in stuff.c&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;CC_FLAGS&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp; $(INC_PATH) $(OPT) $(ERR) -DUNIT_TEST&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# NOTE: check libs must be enclosed by --whole-archive  directives&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;LD_FLAGS&amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  $(LIB_PATH) -Wl,--whole-archive \&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(LIBS) $(CHECK_LIBS) \&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; -Wl,--no-whole-archive&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;# Test Definitions (to be added later)&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TESTS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; =&amp;nbsp; &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#----------------------------------------------------------  Targets&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;all: $(TESTS)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;.PHONY: all clean check&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;clean:&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(RM) $(TESTS) *.gcno *.gcda&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;check:&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @for t in $(TESTS); do&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LD_LIBRARY_PATH='$(LD_PATH)' ./$$t;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; done&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There are a few things to take note of here. &lt;br /&gt;&lt;br /&gt;First, the compiler options &lt;b&gt;profile-arcs&lt;/b&gt; and &lt;b&gt;test-coverage&lt;/b&gt; cause &lt;b&gt;check&lt;/b&gt; to perform test coverage profiling. See the &lt;a href="http://check.sourceforge.net/doc/check_html/check_4.html#SEC20"&gt;the manual&lt;/a&gt; for details.&lt;br /&gt;&lt;br /&gt;Next, &lt;i&gt;libcheck.a&lt;/i&gt; (there is no .so) is added to the linker options, enclosed in &lt;b&gt;--whole-archive&lt;/b&gt; directives so that the linker will not discard what it thinks are unused object files. This last point is important; when linking a static library into a dynamic library, &lt;b&gt;--whole-archive&lt;/b&gt; must be used.&lt;br /&gt;&lt;br /&gt;Finally, the preprocessor definition &lt;b&gt;UNIT_TEST&lt;/b&gt; is added to the compiler options. This will be used to ensure that unit tests do not get built into a distribution version of &lt;i&gt;libstuff.so&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Simple Unit Test&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The first unit test will be a simple one that ensures the library links with no errors. This is a common problem when developing a shared library; unresolved symbols will not be reported until an executable is linked to the library.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;tests/test_link.c&lt;/b&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#include &amp;lt;stuff.h&amp;gt;&lt;br /&gt;&lt;br /&gt;int main(void) { return (int) do_stuff( 32.0 ); }&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Add a make target for this first test.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;tests/Makefile&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# Test 1 : Simple test to ensure that linking against the  library  succeeds&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TEST1&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  test_link&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TEST1_SRC&amp;nbsp;&amp;nbsp;&amp;nbsp; = &amp;nbsp;&amp;nbsp;&amp;nbsp;  test_link.c &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TEST1_FLAGS&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  -l$(TGT_NAME)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TESTS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  $(TEST1 &lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;$(TEST1): $(TEST1_SRC)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(CC) $(CC_FLAGS) $(LD_FLAGS) $(TEST1_FLAGS) -o $@ $^&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Note that the lines before &lt;b&gt;&lt;span style="background-color: #eeeeee;"&gt;# ...&lt;/span&gt;&lt;/b&gt; go in the &lt;i&gt;definitions&lt;/i&gt; (top) part of the &lt;i&gt;Makefile&lt;/i&gt;, while the lines after it go in the &lt;i&gt;targets&lt;/i&gt; (bottom) part of it.&lt;br /&gt;&lt;br /&gt;This first test can now be run:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #cccccc;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bash# make check&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;gcc -I.&amp;nbsp; -DDEBUG -ggdb -O2&amp;nbsp; -Wall -fPIC -o stuff.o -c stuff.c&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ar rc libstuff.a stuff.o&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ranlib libstuff.a&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ld&amp;nbsp; -lm&amp;nbsp; -shared -soname=libstuff.so --whole-archive libstuff.a --no-whole-archive -o libstuff.so &lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;cd tests &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make check&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Entering directory `stuff/tests'&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;gcc -I. -I.. -O2 -fprofile-arcs -ftest-coverage -Wall -DUNIT_TEST -L.. -Wl,--whole-archive -lstuff -lcheck -Wl,--no-whole-archive&amp;nbsp; -o test_link test_link.c &lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Leaving directory `stuff/tests'&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Entering directory `stuff/tests'&lt;/span&gt;&lt;br style="color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="background-color: #cccccc; color: black; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Leaving directory `stuff/tests'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Success! Note that the link test does not involve &lt;b&gt;check&lt;/b&gt;; &lt;b&gt;make &lt;/b&gt;will error out if the linking fails. Pedantic unit testers may want to take the route of making the test fail first (by invoking, say, &lt;b&gt;do_nothing()&lt;/b&gt; in &lt;i&gt;test_link.c&lt;/i&gt;) in order to convince themselves that it works.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Testing Static Functions&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Testing a static function requires embedding the test code in the file that contains the function. The &lt;b&gt;UNIT_TEST&lt;/b&gt; preprocessor directive will prevent the unit test from being compiled in non-unit-test binaries.&lt;br /&gt;&lt;br /&gt;First, append the test code to the end of the library code. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;stuff.c&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;/*  =================================================================  */&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;/* UNIT TESTS */&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#ifdef UNIT_TEST&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#include &lt;/span&gt;&amp;lt;check.h&amp;gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;#include &lt;/span&gt;&amp;lt;stdlib.h&amp;gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /*  for rand() */&lt;br /&gt;&lt;br /&gt;START_TEST (test_step1)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; double d =  (double) rand();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fail_unless( step1(d) == (d * d),  "Step 1 does not square" );&lt;br /&gt;}&lt;br /&gt;END_TEST&lt;br /&gt;&lt;br /&gt;START_TEST (test_step2)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; double d =  (double) rand();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fail_unless( step2(d) == (d + d),  "Step 2 does not double" );&lt;br /&gt;}&lt;br /&gt;END_TEST&lt;br /&gt;&lt;br /&gt;START_TEST (test_step3)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; double d =  (double) rand();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fail_unless( step3(d) == pow(d, d),  "Step 3 does not exponentiate" );&lt;br /&gt;}&lt;br /&gt;END_TEST&lt;br /&gt;&lt;br /&gt;TCase * create_static_testcase(void) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; TCase * tc =  tcase_create("Static Functions");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tcase_add_test(tc, test_step1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tcase_add_test(tc,  test_step2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tcase_add_test(tc, test_step3);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return tc;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#endif&lt;/span&gt;      &lt;/div&gt;&lt;br /&gt;The &lt;b&gt;START_TEST&lt;/b&gt; and &lt;b&gt;END_TEST&lt;/b&gt; macros are provided by &lt;b&gt;check&lt;/b&gt;, as are the &lt;b&gt;tcase_&lt;/b&gt; routines. The strategy here is to define a single exported function, &lt;b&gt;create_static_testcase&lt;/b&gt;, which the unit-test binaries will link to.&lt;br /&gt;&lt;br /&gt;The next step is to add a unit test suite to the test directory.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;tests/test_stuff.c&lt;/b&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;#include &amp;lt;check.h&amp;gt;&lt;br /&gt;#include &amp;lt;math.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;stuff.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#define SUITE_NAME "Stuff"&lt;br /&gt;&lt;br /&gt;/*  -----------------------------------------------------------------  */&lt;br /&gt;/* TESTS */&lt;br /&gt;&lt;br /&gt;START_TEST (test_stuff_diff)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; double d = (double) rand();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fail_unless ( do_stuff(d) != d, "do_stuff doesn't!" );&lt;br /&gt;}&lt;br /&gt;END_TEST&lt;br /&gt;&lt;br /&gt;START_TEST (test_stuff_rand)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; double d = (double) rand();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; double dd = d * d;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; double sumdd = dd + dd;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; fail_unless ( do_stuff(d) == pow(sumdd, sumdd), "Incorrect result"  );&lt;br /&gt;}&lt;br /&gt;END_TEST&lt;br /&gt;&lt;br /&gt;/*  -----------------------------------------------------------------  */&lt;br /&gt;/* SUITE */&lt;br /&gt;&lt;br /&gt;extern TCase * create_static_testcase(void);&lt;br /&gt;&lt;br /&gt;Suite * create_suite(void) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Suite *s = suite_create( SUITE_NAME );&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Create test cases against library API */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; TCase *tc_core = tcase_create ("Core");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tcase_add_test(tc_core, test_stuff_diff);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; tcase_add_test(tc_core, test_stuff_rand);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; suite_add_tcase(s, tc_core);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* Create test cases against static functions */&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; suite_add_tcase( s, create_static_testcase() );&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return s;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int main( void ) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int num_fail;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Suite *s = create_suite();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SRunner *sr = srunner_create(s);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; srunner_run_all (sr, CK_NORMAL);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; num_fail = srunner_ntests_failed (sr);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; srunner_free (sr);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return (num_fail == 0) ? EXIT_SUCCESS : EXIT_FAILURE;&lt;br /&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;This file creates a test suite that contains two tests of the library API (defined in &lt;b&gt;test_stuff.c&lt;/b&gt;) and three tests of the static functions (defined in &lt;b&gt;stuff.c&lt;/b&gt;). The entire suite will be run when the unit-test binary is executed.&lt;br /&gt;&lt;br /&gt;Finally, the new test must be added to &lt;b&gt;tests/Makefile&lt;/b&gt;, as discussed earlier.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;tests/Makefile&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# Test 2 : Actual unit tests against source code&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# NOTE: this cannot link against the library as it  incorporates the  source code&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TEST2&amp;nbsp;&amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp; test_$(TGT_NAME)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TEST2_SRC&amp;nbsp;&amp;nbsp;&amp;nbsp;  = &amp;nbsp;&amp;nbsp;&amp;nbsp; $(TEST2).c \&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;  &amp;nbsp;&amp;nbsp;&amp;nbsp; $(TGT_SRC)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TEST2_FLAGS&amp;nbsp; =&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;TESTS&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; =&amp;nbsp;&amp;nbsp;&amp;nbsp;  $(TEST1) $(TEST2)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# ...&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;$(TEST2): $(TEST2_SRC)&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $(CC) $(CC_FLAGS) $(LD_FLAGS) $(TEST2_FLAGS) -o $@ $^&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Now all of the tests will be run when the make target is invoked:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #cccccc; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #cccccc;"&gt;&lt;span font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;gcc -I.&amp;nbsp; -DDEBUG -ggdb -O2&amp;nbsp; -Wall -fPIC -o stuff.o -c stuff.c&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ar rc libstuff.a stuff.o&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ranlib libstuff.a&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ld&amp;nbsp; -lm&amp;nbsp; -shared -soname=libstuff.so --whole-archive libstuff.a --no-whole-archive &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;-o libstuff.so &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;cd tests &amp;amp;&amp;amp; make &amp;amp;&amp;amp; make check&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Entering directory `stuff/tests'&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;gcc -I. -I.. -O2 -fprofile-arcs -ftest-coverage -Wall -DUNIT_TEST -L.. -Wl,--whole-archive -lstuff -lcheck -Wl,--no-whole-archive&amp;nbsp; -o test_link test_link.c &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;gcc -I. -I.. -O2 -fprofile-arcs -ftest-coverage -Wall -DUNIT_TEST -L.. -Wl,--whole-archive -lstuff -lcheck -Wl,--no-whole-archive&amp;nbsp; -o test_stuff test_stuff.c ../stuff.c &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Leaving directory `stuff/tests'&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Entering directory `stuff/tests'&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Running suite(s): Stuff&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;100%: Checks: 5, Failures: 0, Errors: 0&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;make[1]: Leaving directory `stuff/tests'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8678875678342866473?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8678875678342866473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/08/unit-testing-in-c.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8678875678342866473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8678875678342866473'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/08/unit-testing-in-c.html' title='Unit Testing in C'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-6062156458780828189</id><published>2010-03-17T04:04:00.001-04:00</published><updated>2010-03-17T04:07:48.435-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Up and running with Ruport</title><content type='html'>The need to revamp an aging report generator at work led to a bit of research and thus to &lt;a href="http://www.rubyreports.org/"&gt;Ruport&lt;/a&gt;, an impressive Ruby framework for report generation.&lt;br /&gt;&lt;br /&gt;Ruport has suffered the same fate as many a young open source project: fast and frenzied development ("release early and often" and all that cal) has resulted in enough API changes that many of the examples and documentation useless. The best info to date is in the &lt;a href="http://ruportbook.com/"&gt;Ruport Book&lt;/a&gt;, but even this book does not get one up-and-running with anything more than the most trivial projects (e.g. printing reports for a CSV table).&lt;br /&gt;&lt;br /&gt;Let's define "up and running" as "generating reports from data in an existing database" -- the problem that most people are likely trying to solve, but which somehow doesn't appear as a ready example in most of the Ruport documentation. What follows is a quick demonstration of getting Ruport "up and running".&lt;br /&gt;&lt;br /&gt;First, install the requisite gems. Note: PostgreSQL is being used as the database in this example instead of MySQL, both because it is a better RDBMS and because the world has enough MySQL examples already.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem install ruport&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;# DBI support &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;gem install ruport-util&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem install pg&lt;br /&gt;gem install dbi&lt;br /&gt;gem install dbd-pg&lt;br /&gt;# ActiveRecord and AAR support &lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem install activerecord&lt;br /&gt;gem install acts_as_reportable&lt;br /&gt;# Extras - not covered in this example&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;gem install gruff&lt;br /&gt;gem install documatic&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/div&gt;The database is assumed to have the following configuration:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;* host : db&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;* port : 15434&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;* database: stuff&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;* user: dba&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;* password : secret&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;The 'stuff' database has the table 'complaint' which will the reports will pull data from. Note that the name is &lt;b&gt;not&lt;/b&gt; 'complaints', as ActiveRecord would expect (and create) -- the point here is to connect to an existing database, most likely managed by someone else (who, even more likely, is neither a Rails developer nor a fan of the 'database is subservient to the code' approach).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Generating a Report Using the Ruby DBI&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Connecting to a database table via the Ruby DBI requires the &lt;i&gt;Ruport::Query&lt;/i&gt; class, which has been moved to the &lt;b&gt;ruport-util&lt;/b&gt; gem. &lt;i&gt;Ruport::Query&lt;/i&gt; maintains a list of named database sources, with the default source being named, surprisingly, &lt;i&gt;:default&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Generating a report therefore becomes a matter of adding a database connection via &lt;i&gt;Ruport::Query#add_source&lt;/i&gt;, then instantiating a &lt;i&gt;Ruport::Query&lt;/i&gt; and printing its &lt;i&gt;result&lt;/i&gt;:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;require 'rubygems'&lt;br /&gt;require 'ruport'&lt;br /&gt;require 'ruport/util'&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;Ruport::Query.add_source( :default,&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :user =&amp;gt; 'dba',&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :password =&amp;gt; 'secret',&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :dsn =&amp;gt; 'dbi:Pg:database=stuff;host=db;port=15434'&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br /&gt;puts Ruport::Query.new( [['select * from complaint']] ).result&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;All in all, nice and straightforward, except for that argument to &lt;i&gt;Ruport::Query#new&lt;/i&gt;. According to the docs, this should be as simple as&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;Ruport::Query.new( 'select * from complaint' )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...but this fails, and a look at the source shows that &lt;i&gt;Ruport::Query&lt;/i&gt; invokes the &lt;i&gt;each() &lt;/i&gt;method of the &lt;i&gt;sql&lt;/i&gt; argument (which, being a String, as no &lt;i&gt;each()&lt;/i&gt; method). To make matters worse, if &lt;i&gt;sql&lt;/i&gt; is an array, then the &lt;i&gt;each()&lt;/i&gt; method of &lt;i&gt;sql.first()&lt;/i&gt; is invoked, so the nested array is needed. This is very likely a bug, and may be fixed in future versions, rendering this example and caveat obsolete.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Generating a Report Using ActiveRecord&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The ActiveRecord support in Ruport is a bit more complicated. In particular, the &lt;i&gt;acts_as_reportable&lt;/i&gt; (AAR) mixin must be added to every ActiveRecord table class.&lt;br /&gt;&lt;br /&gt;Note that with ActiveRecord, a table must be wrapped by a class derived from &lt;i&gt;ActiveRecord::Base&lt;/i&gt;. For existing databases, this class can be empty -- although it may be necessary to override the &lt;i&gt;table_name&lt;/i&gt; method if the database schema does not conform to ActiveRecord's expectations.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;require 'rubygems'&lt;br /&gt;require 'ruport'&lt;br /&gt;require 'ruport/acts_as_reportable'&lt;br /&gt;&lt;br /&gt;ActiveRecord::Base::establish_connection( :adapter='postgresql', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :host=&amp;gt;'db',&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :port='15434',&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :database=&amp;gt;'stuff'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :username =&amp;gt; 'dba', &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; :password =&amp;gt; 'secret' )&lt;br /&gt;&lt;br /&gt;class Complaint &amp;lt; ActiveRecord::Base&lt;br /&gt;&amp;nbsp;&amp;nbsp; acts_as_reportable&lt;br /&gt;&amp;nbsp;&amp;nbsp; def table_name()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 'complaint'&lt;br /&gt;&amp;nbsp; end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;puts Complaint.report_table&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The &lt;i&gt;report_table&lt;/i&gt; method is what AAR exposes to generate a Ruport report for the table.&lt;br /&gt;&lt;br /&gt;These quick examples should suffice to get Ruport connected to an existing database without spelunking through the docs and gem source. Once connected, it's simply a matter of playing with Ruport and following the (rest of the) docs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-6062156458780828189?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/6062156458780828189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2010/03/up-and-running-with-ruport.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6062156458780828189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6062156458780828189'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2010/03/up-and-running-with-ruport.html' title='Up and running with Ruport'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-43091961158441800</id><published>2009-12-22T17:39:00.004-05:00</published><updated>2010-08-15T03:21:34.241-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Using GIT as a hierarchical database</title><content type='html'>A quick look into the 'plumbing'' of git (the &lt;a href="http://progit.org/book/ch9-0.html"&gt;Pro Git&lt;/a&gt; book is good) reveals that it can easily store (versioned) data that is not based on the contents of files checked into the repository. This means that it can be used as a database that is based on trees rather than tables or key:value pairs.&lt;br /&gt;&lt;br /&gt;Git stores four kinds of objects: BLOBs, trees, commits, and tags. A BLOB is raw data : it is identified by the SHA1 digest of its contents, so the same data will never be stored twice. A tree is a hierarchy: it can contain BLOBs or other tree objects. A commit object is basically a snapshot of a hierarchy (tree) at a point in time, and a tag assigns a name to a BLOB, tree, or commit.&lt;br /&gt;&lt;br /&gt;In terms of using git as a database, these components can be considered as follows:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;BLOB&lt;/b&gt;: raw data (values).&lt;br /&gt;&lt;b&gt;Tree&lt;/b&gt;: Parent-child relations between data, and a mapping of a database entries with a raw value.&lt;br /&gt;&lt;b&gt;Commit&lt;/b&gt;: A version of the data.&lt;br /&gt;&lt;b&gt;Tag&lt;/b&gt;: A label, e.g. the name of a table, or a name for a particular version of the data.&lt;br /&gt;&lt;br /&gt;Consider the expression 'x * y + 2'. It can be expressed as a tree:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 85%;"&gt;&lt;span style="font-family: courier new;"&gt;+(* ( x, y), 2)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Another way to express this is in a filesystem-like tree:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;/root/&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/type                              # 'operator'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/value            # '+'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/type        # 'operator'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/value                  # '*'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/left/&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/left/type          # 'variable'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/left/value        # 'x'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/right/&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/right/type        # 'variable'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/left/right/value      # 'y'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/right/&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/right/type                  # 'integer'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/root/right/value                # '2'&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;In this schema, entries named 'left' and 'right' are trees, while entries named 'type' and 'value' are BLOBS. Each tree contains at least a type and value entry, with left and right entries existing only if children are present.&lt;br /&gt;&lt;br /&gt;To begin, create a Git repo:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# mkdir /tmp/expr&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# cd /tmp/expr&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git init&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Next, enter the raw data into git using &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-hash-object.html"&gt;git-hash-object&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# echo 'operator' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;e196e4b5d49f41231b184a124b3ff52bb00ef9f6&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# echo 'variable' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;6437a1374bfa97cfdac6611fc5d2d650abaf782b&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# echo 'integer' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;82539ed1cd5cbb2c26a500a228a865d8fbbbcd02&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# echo '*' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;72e8ffc0db8aad71a934dd11e5968bd5109e54b4&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# echo '+' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;fd38861632cb2360cb5acb6253e7e281647f034a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# echo 'x' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;587be6b4c3f93f93c489c0111bba5596147a26cb&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# echo 'y' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;975fbec8256d3e8a3797e7a3611380f27c49f4ac&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# echo '2' | git-hash-object -w --stdin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;0cfbf08886fca9a91cb753ec8734c84fcbe52c9f&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;            &lt;/div&gt;&lt;br /&gt;Note that git-hash-object can be run without the -w (write) option in order to generate the SHA1 digest of the data without adding it to git.&lt;br /&gt;&lt;br /&gt;The tree hierarchy can be created with &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-update-index.html"&gt;git-update-index&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# # Root: Operator '+'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;#git-update-index --add --cacheinfo 100644 e196e4b5d49f41231b184a124b3ff52bb00ef9f6 'root/type'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644  fd38861632cb2360cb5acb6253e7e281647f034a 'root/value' &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Root Left Child: Operator '*'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 e196e4b5d49f41231b184a124b3ff52bb00ef9f6 'root/left/type'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 72e8ffc0db8aad71a934dd11e5968bd5109e54b4 'root/left/value' &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # * Left Child: Variable 'x'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 1006446437a1374bfa97cfdac6611fc5d2d650abaf782b 'root/left/left/type'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 587be6b4c3f93f93c489c0111bba5596147a26cb 'root/left/left/value'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # * Right Child:  Variable 'y'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 1006446437a1374bfa97cfdac6611fc5d2d650abaf782b 'root/left/right/type'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 975fbec8256d3e8a3797e7a3611380f27c49f4ac 'root/left/right/value'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Root Right Child: Integer '2'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 82539ed1cd5cbb2c26a500a228a865d8fbbbcd02 'root/right/type' &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f 'root/right/value' &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-write-tree &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;7fe6589700060de813d529c48937b7678c297d42&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-ls-tree 7fe6589700060de813d529c48937b7678c297d42&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;040000 tree b430c300c59dd80764892644c637ba6e29785c09    root&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-ls-tree -r 7fe6589700060de813d529c48937b7678c297d42&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 587be6b4c3f93f93c489c0111bba5596147a26cb    root/left/left/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 975fbec8256d3e8a3797e7a3611380f27c49f4ac    root/left/right/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob e196e4b5d49f41231b184a124b3ff52bb00ef9f6    root/left/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 72e8ffc0db8aad71a934dd11e5968bd5109e54b4    root/left/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 82539ed1cd5cbb2c26a500a228a865d8fbbbcd02    root/right/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f    root/right/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob e196e4b5d49f41231b184a124b3ff52bb00ef9f6    root/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob fd38861632cb2360cb5acb6253e7e281647f034a    root/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-ls-files&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/left/left/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/left/right/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/left/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/left/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/right/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/value&lt;br /&gt;# git-ls-files --stage \*/value | cut -d ' ' -f 2 | git-cat-file --batch&lt;br /&gt;587be6b4c3f93f93c489c0111bba5596147a26cb blob 2&lt;br /&gt;x&lt;br /&gt;&lt;br /&gt;975fbec8256d3e8a3797e7a3611380f27c49f4ac blob 2&lt;br /&gt;y&lt;br /&gt;&lt;br /&gt;72e8ffc0db8aad71a934dd11e5968bd5109e54b4 blob 2&lt;br /&gt;*&lt;br /&gt;&lt;br /&gt;0cfbf08886fca9a91cb753ec8734c84fcbe52c9f blob 2&lt;br /&gt;2&lt;br /&gt;&lt;br /&gt;fd38861632cb2360cb5acb6253e7e281647f034a blob 2&lt;br /&gt;+&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 75%;"&gt;git-ls-files --stage \*/value | cut -d ' ' -f 2 | xargs git-show&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;x                                                                              &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;y&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;+&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;An ls of the repository will show only the .git directory: the data is stored as BLOBs and paths in the GIT database, not as a directory tree on the filesystem.&lt;br /&gt;&lt;br /&gt;In the first attempt, git-ls-files returns the database entries lexically ordered by path. A more database-like approach would be to store the nodes of the expression in a single directory (i.e. a table) and linking them via parent and child references. This highlights a small flaw in the plan: it is not possible to get the SHA1 of a tree without writing the index to disk.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 75%;"&gt;&lt;br /&gt;&lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# # Remove previous attempt&lt;br /&gt;# rm -r .git&lt;br /&gt;# git init&lt;br /&gt;&lt;br /&gt;# # Node  1 Operator '+'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo '+' | git-hash_object -w --stdin` 'nodes/1/value'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo 'operator' | git-hash_object -w --stdin` 'nodes/1/type'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Node 2: Operator '*'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo '*' | git-hash-object -w --stdin` 'nodes/2/value'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo 'operator' | git-hash-object -w --stdin` 'nodes/2/type'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Node 3: Variable 'x'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo 'x' | git-hash-object -w --stdin` 'nodes/3/value'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo 'variable' | git-hash-object -w --stdin` 'nodes/3/type'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Node 4: Variable 'y'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo 'y' | git-hash-object -w --stdin` 'nodes/4/value'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo 'variable' | git-hash-object -w --stdin` 'nodes/4/type'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Node 5: Integer '2'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="background-color: #eeeeee; font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;git-update-index --add --cacheinfo 100644 `echo '2' | git-hash-object -w --stdin` 'nodes/5/value'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 `echo 'integer' | git-hash-object -w --stdin` 'nodes/5/type'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git write-tree&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;d986912eb66eb446ddaaa188a5cc1068c8cf951b&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git ls-tree `git ls-tree d986912eb66eb446ddaaa188a5cc1068c8cf951b | grep nodes | awk '{print $3}'`&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;040000 tree 945999b7c15c9b745333847bf3d341b7f74a784f    1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;040000 tree 86a40cb9ccbc91a00ce06c9e9ca3e132f5e22ab6    2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;040000 tree 9a4b76bbc0b9a20185333ca44321ba438eb6d71c    3&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;040000 tree f556930b3cd058453894e0cc386fe6ca10908abd    4&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;040000 tree 36fde2340dd25814793c1346ebbb0175049665dd    5&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Set Node 1 children&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 945999b7c15c9b745333847bf3d341b7f74a784f 'nodes/1/left'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 36fde2340dd25814793c1346ebbb0175049665dd 'nodes/1/right'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Set Node 2 parent and children&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 945999b7c15c9b745333847bf3d341b7f74a784f 'nodes/2/parent'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 f556930b3cd058453894e0cc386fe6ca10908abd 'nodes/2/left'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 f556930b3cd058453894e0cc386fe6ca10908abd 'nodes/2/right'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Set Node 3 parent&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 86a40cb9ccbc91a00ce06c9e9ca3e132f5e22ab6 'nodes/3/parent'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Set Node 4 parent&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 86a40cb9ccbc91a00ce06c9e9ca3e132f5e22ab6 'nodes/4/parent'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# # Set Node 5 parent&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 945999b7c15c9b745333847bf3d341b7f74a784f 'nodes/5/parent'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-write-tree&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;e6b41858f6e7350914dbf1ddbdc2e457ee9bb4d3&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git ls-tree -r e6b41858f6e7350914dbf1ddbdc2e457ee9bb4d3&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 945999b7c15c9b745333847bf3d341b7f74a784f    nodes/1/left&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 36fde2340dd25814793c1346ebbb0175049665dd    nodes/1/right&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob e196e4b5d49f41231b184a124b3ff52bb00ef9f6    nodes/1/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob fd38861632cb2360cb5acb6253e7e281647f034a    nodes/1/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob f556930b3cd058453894e0cc386fe6ca10908abd    nodes/2/left&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 945999b7c15c9b745333847bf3d341b7f74a784f    nodes/2/parent&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob f556930b3cd058453894e0cc386fe6ca10908abd    nodes/2/right&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob e196e4b5d49f41231b184a124b3ff52bb00ef9f6    nodes/2/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 72e8ffc0db8aad71a934dd11e5968bd5109e54b4    nodes/2/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 86a40cb9ccbc91a00ce06c9e9ca3e132f5e22ab6    nodes/3/parent&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 6437a1374bfa97cfdac6611fc5d2d650abaf782b    nodes/3/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 587be6b4c3f93f93c489c0111bba5596147a26cb    nodes/3/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 86a40cb9ccbc91a00ce06c9e9ca3e132f5e22ab6    nodes/4/parent&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 6437a1374bfa97cfdac6611fc5d2d650abaf782b    nodes/4/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 975fbec8256d3e8a3797e7a3611380f27c49f4ac    nodes/4/value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 945999b7c15c9b745333847bf3d341b7f74a784f    nodes/5/parent&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 82539ed1cd5cbb2c26a500a228a865d8fbbbcd02    nodes/5/type&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 blob 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f    nodes/5/value&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Of course, pulling the contents of these files from the DB results in a list of expression tokens ordered by node number:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# &lt;/span&gt;&lt;span style="font-family: courier new;"&gt;git ls-files --stage '*/value' | awk '{print $2}' | xargs git-show&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;+                                                                              &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;x&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;y&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;While correct, this is not useful for building an infix expression. A new tree is needed that acts as an index for the first tree, so that listing the new tree will return the tokens in infix order. This is accomplished by using the names 'left', 'operator', and 'right' for node contents, so that lexical ordering of the index-tree will return tokens in that order.&lt;br /&gt;&lt;span style="font-size: 75%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# # Root: Operator '+'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 `echo '+' | git-hash-object -w --stdin` 'infix/operator'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# # Root Left Child: Operator '*'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 `echo '*' | git-hash-object -w --stdin` 'infix/left/operator'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# # * Left Child: Variable 'x'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 `echo 'x' | git-hash-object -w --stdin` 'infix/left/left/variable'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# # * Right Child: Variable 'y'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 `echo 'y' | git-hash-object -w --stdin` 'infix/left/right/variable'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# # Root Right Child: Integer '2'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# git-update-index --add --cacheinfo 100644 `echo '2' | git-hash-object -w --stdin` 'infix/right/value'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# git-ls-files --stage infix/\* | cut -d ' ' -f 2 | xargs git-show | awk '{printf $0 " "}'&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;x * y + 2 &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here, the 'infix' tree re-creates the hierarchy of  the original expression, much as the first attempt did -- only this time, the names of the files and directories are chosen to force an infix ordering when sorted by name. Only the files containing the values to display are included in this tree, and these map to the same SHA1 digest as those in the 'nodes' tree. Note that git only stores one copy of each BLOB, so the infix tree only uses as much space as is required to represent the tree: the value BLOBs are shared with the original table.&lt;br /&gt;&lt;br /&gt;Listing  of subtrees correctly returns sub-expressions:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# git-ls-files --stage infix/left/\* | cut -d ' ' -f 2 | xargs git-show | awk '{printf $0 " "}'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;x * y&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Once the data is has been created, the database can be committed, creating a base version:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# echo 'Node database created' | git commit-tree e6b41858f6e7350914dbf1ddbdc2e457ee9bb4d3&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;f1b038fb0ea444f7a05ef9c48d74aef6ac3d8b7e&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Searching the database relies on &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-grep.html"&gt;git-grep&lt;/a&gt;, using the --cached option to search the BLOBs instead of the filesystem. The search may be limited to specific paths:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# git-grep --cached 'operator' -- root&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;root/left/type:operator                                                        &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/type:operator&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-grep --cached '*' -- expr&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;expr/left/operator:*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-grep --cached 'x' -- '*/variable'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;expr/left/left/variable:x&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-grep --cached '*''*/left/*'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;expr/left/operator:*                                                           &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;root/left/value:*&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Removing entries involves removing the index entries, not the BLOB:&lt;br /&gt;&lt;br /&gt;&lt;div style="background-color: #eeeeee;"&gt;&lt;span style="font-size: 75%;"&gt;&lt;span style="font-family: courier new;"&gt;# git-update-index --force-remove 'infix/operator'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# git-ls-files --stage infix\*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 587be6b4c3f93f93c489c0111bba5596147a26cb 0    infix/left/left/variable&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 72e8ffc0db8aad71a934dd11e5968bd5109e54b4 0    infix/left/operator&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 975fbec8256d3e8a3797e7a3611380f27c49f4ac 0    infix/left/right/variable&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;100644 0cfbf08886fca9a91cb753ec8734c84fcbe52c9f 0    infix/right/value&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;This means that the values stay in the database, so that changes can be reversed. The actual 'database' therefore is nothing but a collection of named links to BLOBs. Searching and browsing of the database is done by operating on paths, the 'live' data, while inserting, updating, and deleting database entries requires changing what blobs the paths point to.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It goes without saying that the git commands provided above can be wrapped with shell functions (or Ruby, or Python, or any other scripting language) that create database entries, populate indexes, and commit changes to the data. With a small amount of work, a reasonable version-controlled hierarchical database can be put together, all contained in the .git dir.&lt;br /&gt;&lt;br /&gt;More complex projects could mix files managed by git and the above tree-only approach; this is especially useful when storing binary content, which is awkward to deal with when stored in BLOBs (generally requires unzipping). In such cases, finding the top level (root) of the Git repository is useful, and git makes this easy as well:&lt;br /&gt;&lt;span style="font-size: 75%;"&gt;&lt;br /&gt;&lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# mkdir stuff&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# cd stuff&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="background-color: #eeeeee; font-family: courier new;"&gt;# git-rev-parse --git-dir&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;span style="font-family: courier new;"&gt;&lt;span style="background-color: #eeeeee;"&gt;/tmp/expr/.git&lt;/span&gt;&lt;br style="background-color: #eeeeee;" /&gt; &lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-43091961158441800?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/43091961158441800/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/12/using-git-as-hierarchical-database.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/43091961158441800'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/43091961158441800'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/12/using-git-as-hierarchical-database.html' title='Using GIT as a hierarchical database'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-3613046743049433410</id><published>2009-12-21T00:49:00.000-05:00</published><updated>2009-12-21T01:13:18.748-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>The importance of documentation</title><content type='html'>Went back to Python after three or so years of Ruby, and found immediately that loading modules from . is now broken.&lt;br /&gt;&lt;br /&gt;Checked the docs, which are in a horrible state (&lt;span style="font-style: italic;"&gt;it's in the Language Ref, no wait it's in the Library Ref even though it's a language feature, no wait it's in a PEP, no wait that is out of date and the real documentation has been posted to a mailing list&lt;/span&gt;), and found stuff like this:&lt;br /&gt;&lt;br /&gt;From &lt;a href="http://docs.python.org/tutorial/modules.html#the-module-search-path"&gt;Python Docs: The Module Search Path&lt;/a&gt;:&lt;br /&gt;&lt;p id="index-1087"&gt;&lt;span style="font-size:85%;"&gt;"When a module named &lt;tt class="xref docutils literal"&gt;&lt;span class="pre"&gt;spam&lt;/span&gt;&lt;/tt&gt; is imported, the interpreter searches for a file named &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;spam.py&lt;/span&gt;&lt;/tt&gt; in the current directory, and then in the list of directories specified by the environment variable &lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;a class="reference external" href="http://docs.python.org/using/cmdline.html#envvar-PYTHONPATH"&gt;&lt;strong class="xref"&gt;PYTHONPATH&lt;/strong&gt;&lt;/a&gt;.  This has the same syntax as the shell variable &lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;strong class="xref"&gt;PATH&lt;/strong&gt;, that is, a list of directory names.  When &lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;a class="reference external" href="http://docs.python.org/using/cmdline.html#envvar-PYTHONPATH"&gt;&lt;strong class="xref"&gt;PYTHONPATH&lt;/strong&gt;&lt;/a&gt; is not set, or when the file is not found there, the search continues in an installation-dependent default path; on Unix, this is usually &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;.:/usr/local/lib/python&lt;/span&gt;&lt;/tt&gt;.&lt;/span&gt;&lt;/p&gt; &lt;p&gt;&lt;span style="font-size:85%;"&gt;"Actually, modules are searched in the list of directories given by the variable &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;sys.path&lt;/span&gt;&lt;/tt&gt; which is initialized from the directory containing the input script (or the current directory), &lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;a class="reference external" href="http://docs.python.org/using/cmdline.html#envvar-PYTHONPATH"&gt;&lt;strong class="xref"&gt;PYTHONPATH&lt;/strong&gt;&lt;/a&gt; and the installation- dependent default.&lt;/span&gt;"&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Well, that's the theory at any rate. Let's test it with actual code:&lt;br /&gt;&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;# mkdir /tmp/test-py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# cd /tmp/test-py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# mkdir -p snark/snob&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# touch snark/__init__.py snark/snob/__init__.py snark/snob/stuff.py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;#  echo -e '#!/usr/bin/env python2.5\nimport os\nprint(os.getcwd())\nimport snark.snob.stuff\n'&gt; a.py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# chmod +x a.py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# mkdir bin&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# cp a.py bin/b.py&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What would you expect to happen when this is run? Surely having the module in the current directory means that running either a.py or b.py from . will work, right?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;# ./a.py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/tmp/py-test&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# ./bin/b.py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/tmp/py-test&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Traceback (most recent call last):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  File "./bin/b.py", line 4, in &lt;/span&gt;&lt;/span&gt;&lt;module&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    import snark.snob.stuff&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;ImportError: No module named snark.snob.stuff&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Nope! And look at that -- according to Python's own system command, the current working directory (as mentioned in their docs) is the same for both scripts!&lt;br /&gt;&lt;br /&gt;Explicitly setting PYTHONPATH fixes this:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# PYTHONPATH=. bin/b.py&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;/tmp/py-test&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;..but really, shouldn't the docs be a bit less misleading?&lt;br /&gt;&lt;br /&gt;And, for that matter, why the hell is the location of the script used instead of the working directory anyways? Either be sane and use the current working directory, or be slightly less sane and check both.&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The whole reason for using Python on this project in the first place was to revert to a language and interpreter that is more stable and more professional (in terms of management style) than Ruby, but this kind of crap certainly gives one pause. If thirty minutes with the language turns up something as obviously broken as the module loader, one shudders to think what is going to come up over the course of the project.&lt;br /&gt;&lt;br /&gt;Sad days for the Python boys. The interest of Fowler and his crew must have really given Ruby a leg up in terms of quality.&lt;br /&gt;&lt;/module&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-3613046743049433410?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/3613046743049433410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/12/importance-of-documentation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3613046743049433410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3613046743049433410'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/12/importance-of-documentation.html' title='The importance of documentation'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-3232017972914951560</id><published>2009-12-06T15:49:00.000-05:00</published><updated>2009-12-21T15:48:02.564-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rattle'/><category scheme='http://www.blogger.com/atom/ns#' term='r'/><title type='text'>'standalone' rattle</title><content type='html'>&lt;a href="http://rattle.togaware.com/"&gt;Rattle&lt;/a&gt; is a decent data-mining utility built on top of GNU R, with one small problem: it is impossible to run outside of the R terminal (e.g. from the shell or a desktop icon/menu).&lt;br /&gt;&lt;br /&gt;What this means, on Linux, is that to run Rattle one must start R in a terminal and enter the following commands:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;&gt; library(rattle)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Loading required package: pmml&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Loading required package: XML&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Rattle: Graphical interface for data mining using R.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Version 2.5.3 Copyright (C) 2006-2009 Togaware Pty Ltd.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Type 'rattle()' to shake, rattle, and roll your data.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&gt; rattle()&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;A Ctrl-D to log out of R is also required.&lt;br /&gt;&lt;br /&gt;The big problem (ignoring R's inconsistent handling of stdin/out, which can mostly be solved by using &lt;a href="http://dirk.eddelbuettel.com/code/littler.html"&gt;littler&lt;/a&gt;) is the R Gtk module starts a separate GUI thread, and quitting R does not do a wait on  child threads -- it just kills them.&lt;br /&gt;&lt;br /&gt;A workaround to launch rattle from outside of R is to provide a simple wrapper script:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;#!/usr/bin/env ruby&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;require 'open3'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;r_prog = `which R`.chomp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Open3.popen3( "#{r_prog} --interactive --no-save --no-restore --slave" ) do | st&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;din, stdout, stderr |&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  stdin.puts "library(rattle)"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  stdin.puts "rattle()"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  puts stdout.read&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;end   &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This runs R and launches rattle(), but leaves an R process open in the background once rattle() has exited.&lt;br /&gt;&lt;br /&gt;Adding a q() call to the script merely terminates the Rattle GUI child thread. In addition, rattle provides no indication that it is running:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;&gt; ls()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;character(0)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&gt; library(rattle)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Loading required package: pmml&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Loading required package: XML&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Rattle: Graphical interface for data mining using R.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Version 2.5.3 Copyright (C) 2006-2009 Togaware Pty Ltd.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;Type 'rattle()' to shake, rattle, and roll your data.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&gt; ls()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[1] "Global_rattleGUI"        "crs"                    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[3] "crv"                     "on_aboutdialog_response"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[5] "rattleGUI"               "suppressRattleWelcome"  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[7] "viewdataGUI"            &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&gt; rattle()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&gt; ls()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[1] "Global_rattleGUI"        "crs"                    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[3] "crv"                     "on_aboutdialog_response"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[5] "rattleGUI"               "suppressRattleWelcome"  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;[7] "viewdataGUI"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;# Use Ctrl-Q to exit the Rattle GUI&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&gt; ls()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt; [1] "Global_rattleGUI"        "crs"                    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt; [3] "crv"                     "on_aboutdialog_response"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt; [5] "rattleGUI"               "suppressRattleWelcome"  &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt; [7] "viewdataGUI"&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Once the Rattle library has loaded, there is no way to tell from within R if the Rattle GUI is running or not. Modifying Rattle to create a variable when the GUI loads successfully, and to remove it when the GUI quites, would undoubtedly fix this.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-3232017972914951560?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/3232017972914951560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/12/standalone-rattle.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3232017972914951560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/3232017972914951560'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/12/standalone-rattle.html' title='&apos;standalone&apos; rattle'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4363533148787920442</id><published>2009-11-25T19:21:00.002-05:00</published><updated>2010-03-17T04:16:30.752-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='macports'/><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='kde'/><title type='text'>Too clever by half</title><content type='html'>It's always frustrating to come across a build error in allegedly-portable code, then to discover that the root of it is someone complicating things in a misguided attempt to improve portability -- usually by introducing complexity instead of removing it.&lt;br /&gt;&lt;br /&gt;The macports project in particular is rife with the problems, undoubtedly because most of the "cross-platform" open source projects it ports never considered OS X as a build target.&lt;br /&gt;&lt;br /&gt;KDE4 has refused to build on a particular OS X box (Quad G5, 10.5.8, XCode 3.1.4) via macports for quite some time. Currently, the build fails in kdebase4-runtime due to an error in the fishProtocol ctor in fish.cpp:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 78%;"&gt;&lt;span style="font-family: courier new;"&gt;/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.cpp: In constructor 'fishProtocol::fishProtocol(const QByteArray&amp;amp;, const QByteArray&amp;amp;)':&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.cpp:275: error: cannot convert 'const char* (*)()' to 'const char*' for argument '1' to 'size_t strlen(const char*)'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.cpp: In member function 'void fishProtocol::manageConnection(const QString&amp;amp;)':&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.cpp:1079: error: no matching function for call to 'fishProtocol::writeChild(const char* (&amp;amp;)(), int&amp;amp;)'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.cpp:561: note: candidates are: void fishProtocol::writeChild(const char*, KIO::fileoffset_t)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;make[2]: *** [kioslave/fish/CMakeFiles/kio_fish.dir/fish.o] Error 1&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;make[1]: *** [kioslave/fish/CMakeFiles/kio_fish.dir/all] Error 2&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;make: *** [all] Error 2&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Little did I know at the time that the actual cause for the error was further up:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 78%;"&gt;&lt;span style="font-family: courier new;"&gt;[ 57%] Generating fishcode.h&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;cd /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish &amp;amp;&amp;amp; /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/generate_fishcode.sh /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.pl /opt/local/bin/md5 /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/build/kioslave/fish/fishcode.h -f\ 4&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;sed: 1: "s/\\/\\\\/g;s/"/\\"/g;s ...": unescaped newline inside substitute pattern&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The line in question (fish.cpp:275) does a strlen on fishCode, which is not defined anywhere, though there is an #include for fishcode.h .&lt;br /&gt;&lt;br /&gt;I searched around, found fishcode.h, and it was close to empty:&lt;br /&gt;&lt;br /&gt;#&lt;span style="font-size: 78%;"&gt;&lt;span style="font-family: courier new;"&gt;define CHECKSUM "'fb18e850532f42b49f1034b4e17a4cdc /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.pl'"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;static const char&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;*fishCode('&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;');&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Something had gone horribly wrong, somewhere -- fishCode is empty, though that shouldn't cause the error being displayed. Still, builds get wacky, and it's a lead.&lt;br /&gt;&lt;br /&gt;Googling around for a copy of fishcode.h, turns up nothing, which is not a surprise as it is obviously autogenerated by the build process. What does turn up, however, are the commands used to generate it:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 85%;"&gt;&lt;span style="font-family: courier new;"&gt;SUM=`/usr/bin/md5sum&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/usr/src/packages/BUILD/kdebase-683581/kioslave/fish/fish.pl | cut -d ' ' -f 1`;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;echo '#define CHECKSUM "'$SUM'"' &amp;gt; fishcode.h; echo 'static const char&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;*fishCode(' &amp;gt;&amp;gt; fishcode.h; sed -e 's/\\/\\\\/g;s/"/\\"/g;s/^[  ]*/"/;/^"# /d;s/[&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;]*$/\\n"/;/^"\\n"$/d;s/{CHECKSUM}/'$SUM'/;'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;/usr/src/packages/BUILD/kdebase-683581/kioslave/fish/fish.pl &amp;gt;&amp;gt; fishcode.h; echo&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;');' &amp;gt;&amp;gt; fishcode.h;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Time to set SUM in the shell and started playing with the sed command, which is obviously what's failing:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 85%;"&gt;&lt;span style="font-family: courier new;"&gt;bash-3.2$ SUM='fb18e850532f42b49f1034b4e17a4cdc /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.pl'&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;bash-3.2$ sed -e 's/\\/\\\\/g;s/"/\\"/g;s/^[ ]*/"/;/^"# /d;s/[&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&amp;gt; ]*$/\\n"/;/^"\\n"$/d;s/{CHECKSUM}/'$SUM'/;'&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;sed: 1: "s/\\/\\\\/g;s/"/\\"/g;s ...": unbalanced brackets ([])&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Something is going wrong, set SUM to a more sane value:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 85%;"&gt;&lt;span style="font-family: courier new;"&gt;bash-3.2$ SUM='fb18e850532f42b49f1034b4e17a4cdc'&lt;br /&gt;&lt;br /&gt;bash-3.2$ sed -e 's/\\/\\\\/g;s/"/\\"/g;s/^[ ]*/"/;/^"# /d;s/[&amp;gt; ]*$/\\n"/;/^"\\n"$/d;s/{CHECKSUM}/'$SUM'/;'&lt;br /&gt;&lt;br /&gt;sed: 1: "s/\\/\\\\/g;s/"/\\"/g;s ...": unbalanced brackets ([])&lt;br /&gt;&lt;br /&gt;bash-3.2$ SUM='/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.pl'&lt;br /&gt;&lt;br /&gt;bash-3.2$ sed -e 's/\\/\\\\/g;s/"/\\"/g;s/^[ ]*/"/;/^"# /d;s/[&lt;br /&gt;&amp;gt; ]*$/\\n"/;/^"\\n"$/d;s/{CHECKSUM}/'$SUM'/;'&lt;br /&gt;&lt;br /&gt;sed: 1: "s/\\/\\\\/g;s/"/\\"/g;s ...": unbalanced brackets ([])&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Try replacing the embedded newline with a good old '\n':&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 78%;"&gt;&lt;span style="font-family: courier new;"&gt;bash-3.2$ SUM='/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.pl'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;bash-3.2$ sed -e 's/\\/\\\\/g;s/"/\\"/g;s/^[ ]*/"/;/^"# /d;s/[\n]*$/\\n"/;/^"\\n"$/d;s/{CHECKSUM}/'$SUM'/;'&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt; sed: 1: "s/\\/\\\\/g;s/"/\\"/g;s ...": bad flag in substitute command: 'o'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sed doesn't like the embedded newline; not a surprise. Now it seems like SUM is being interpreted as part of the expression. Let's see what's really going on:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 85%;"&gt;&lt;span style="font-family: courier new;"&gt;bash-3.2$ SUM='fb18e850532f42b49f1034b4e17a4cdc /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.pl'&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;bash-3.2$ echo sed -e 's/\\/\\\\/g;s/"/\\"/g;s/^[ ]*/"/;/^"# /d;s/[&amp;gt; ]*$/\\n"/;/^"\\n"$/d;s/{CHECKSUM}/'$SUM'/;' sed -e s/\\/\\\\/g;s/"/\\"/g;s/^[ ]*/"/;/^"# /d;s/[&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;]*$/\\n"/;/^"\\n"$/d;s/{CHECKSUM}/fb18e850532f42b49f1034b4e17a4cdc /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_kdebase4-runtime/work/kdebase-runtime-4.3.3/kioslave/fish/fish.pl/;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Argh! Someone's single quotes aren't properly escaped! Right there at '$SUM'!&lt;br /&gt;&lt;br /&gt;Simple enough to fix, though. Fortunately, using sed to generate fishcode.h manually allows the kdebase4-runtime build to continue, which saves the time of tracking down where in the build process that sed command is.&lt;br /&gt;&lt;br /&gt;It does raise the question, though: was any of this autogeneration &lt;span style="font-weight: bold;"&gt;really&lt;/span&gt; necessary? And if so, wouldn't a perl script (or even a one-liner) be more reliable?&lt;br /&gt;&lt;br /&gt;One can argue that perl does not ship on as many OSes as sed (debatable), but the fact that this build worked on a maintainer's machine and not on an end-user's machine (running an OS highly regulated by the manufacturer, no less) pretty much blows the portability argument out of the water: if that's what the maintainer is truly after, well, their methods aren't working either.&lt;br /&gt;&lt;br /&gt;OK, end angry-at-overly-convoluted-open-source-code-again rant. The moral: verify your goddamn shell commands!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4363533148787920442?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4363533148787920442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/11/too-clever-by-half.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4363533148787920442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4363533148787920442'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/11/too-clever-by-half.html' title='Too clever by half'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-7685865431129916559</id><published>2009-09-12T16:36:00.000-04:00</published><updated>2009-09-13T18:02:38.306-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='e70'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Ubuntu, bluetooth, and the E70</title><content type='html'>A few quick notes on connecting a phone (in this case the venerable Nokia E70, the last with the excellent gull-wing keyboard design) to ubuntu via bluetooth.&lt;br /&gt;&lt;br /&gt;Assuming the kernel modules and all necessary utilities (bluetooth, bluez, obexftp, obexfs, obecpushd, obex-data-server, irda-utils, cobex, gammi, kbluetooth) are installed, the first step is to create an rfcomm device:&lt;br /&gt;&lt;br /&gt;    mknod /dev/rfcomm0 c 216 0&lt;br /&gt;&lt;br /&gt;Next, bind the device to the phone's bluetooth MAC address:&lt;br /&gt;&lt;br /&gt;    sudo rfcomm bind 0 00:12:D1:AD:FD:5E&lt;br /&gt;&lt;br /&gt;The MAC address can be obtained using &lt;br /&gt;&lt;br /&gt;    hcitool scan&lt;br /&gt;&lt;br /&gt;Note that the link and the bind have to be performed at boot; the file /etc/bluetooth/rfcomm.conf can be modified to perform this (it contains a sufficiently clear example).&lt;br /&gt;&lt;br /&gt;Once these steps are performed, utilities like cobex and kbluetooth should just work.&lt;br /&gt;&lt;br /&gt;With the E70, though, they don't, so it's necessary to use Obex to get data to and from the device.&lt;br /&gt;&lt;br /&gt;The standard, longhand way to do this is with obex-ftp:&lt;br /&gt;&lt;br /&gt; obexftp -b $MAC_ADDR -B 11 -l&lt;br /&gt;&lt;br /&gt;The -b option is the bluetooth MAC address; the -B option is the channel (usually 10 or 11), and the -l option is the command to execute (ls). The man page lists commands.&lt;br /&gt;&lt;br /&gt;Better that obexftp is obexfs, which mounts the phone as a filesystem:&lt;br /&gt;&lt;br /&gt; mkdir ~/e70&lt;br /&gt; obexfs -b $MAC_ADDR -B 11 ~/e70&lt;br /&gt;&lt;br /&gt;This mounts the phone at the mount point ~/e70, where its phone and MMC memory can be accessed directly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-7685865431129916559?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/7685865431129916559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/09/ubuntu-bluetooth-and-e70.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7685865431129916559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7685865431129916559'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/09/ubuntu-bluetooth-and-e70.html' title='Ubuntu, bluetooth, and the E70'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-1243248845445595086</id><published>2009-09-08T23:26:00.001-04:00</published><updated>2010-08-16T03:00:35.383-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><category scheme='http://www.blogger.com/atom/ns#' term='doxygen'/><title type='text'>Doxygen gotchas</title><content type='html'>Finally took an afternoon to learn doxygen and have been commenting up the latest project for the past 24 or so hours.&lt;br /&gt;&lt;br /&gt;Doxygen is easy to get up to speed with, but there are a couple of gotchas that aren't made clear in the documentation. These are listed below in no particular order.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Standalone pages&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;This is the first thing one notices when mucking about with doxygen: how can the index.html (aka "Main Page") be modified?&lt;br /&gt;&lt;br /&gt;The answer is just as quick to find: create a document with a \mainpage tag. But what document? Surely adding a header file just to create doxygen tags is a bit silly?&lt;br /&gt;&lt;br /&gt;Indeed. Instead, create a directory (e.g. doc) and use it for standalone doxygen files. A standalone file is basically a text file consisting of only the C++ comment, e.g.:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;/*!&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt; \mainpage The Main Page&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt; \section sec_1 Section 1&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt; ...section 1 stuff...&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt; \section sec_2 Section 2&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt; ... section 2 stuff...&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;&amp;lt;HR&amp;gt;&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;&amp;lt;b&amp;gt;\ref todo%lt;/b&amp;gt;&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;*/&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Name this something like 'main.dox', and add the .dox extension to the FILE_PATTERNS definition in the project Doxyfile:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;FILE_PATTERNS += *.dox&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;These standalone doxygen files are incredibly useful for stuff like installation instructions, HowTos, and FAQs.&lt;br /&gt;&lt;br /&gt;They are also useful for defining groups. A file groups.dox can contain group definitions like the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family: courier new;"&gt;&lt;span style="font-size: 100%;"&gt;/*!                                                                         &lt;br /&gt;\defgroup outer_group "Outer Group"&lt;br /&gt;This module contains outer-group stuff.&lt;br /&gt;&lt;br /&gt;\defgroup inner_group "Inner Group"&lt;br /&gt;\ingroup outer_group&lt;br /&gt;These things are in a group in a group.&lt;br /&gt;*/&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;This provides a single, easy-to-maintain place for group definitions, so that header files and such just have to use "\ingroup". It seems like pretty good practice to put most files and classes in groups -- it makes for more expedient browsing.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Global namespace&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ok, there's a nice 'Namespaces' folder in the tree pane, and what does it contain? The project namespace. What about all those singletons ill-advisably implemented as globals (purely for narrative effect)?&lt;br /&gt;&lt;br /&gt;Turns out there is no way to list the global namespace. This seems like a huge oversight -- if there's one thing you want to know about a project, it's how many lame globals the developers used.&lt;br /&gt;&lt;br /&gt;Adding a 'globals' page seems like a good way to circumvent it, except for one slight problem -- if you create a standalone doc with "\page globals Global Namespace" in it, doxygen creates a page in the tree called "Global Namespace" ... with its own internal(?) version of the global namespace in it. This means, basically, that globals defined in your project are not there -- it only contains stuff like (for example) qRegisterMetaType invocations. It looks like 'globals' is an undocumented, and not particularly working, doxygen 'special command'.&lt;br /&gt;&lt;br /&gt;A workaround is to use xrefitems. Add a line like the following to doxygen (remember, 'globals" is not an allowed name):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family: courier new;"&gt;ALIASES                +=  "globalfn=\xrefitem global \"Functions\" \"Globals\""&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Now, use code like the following to document your global function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;/*! \fn void GlobalSingletonFactoryMess( bool suck_less )&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;       \globalfn void GlobalSingletonFactoryMess( bool suck_less )&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;       \param suck_less : Make singletons suck less&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;*/&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new; font-size: 100%;"&gt;void GlobalSingletonFactoryMess( bool );&lt;/span&gt;&lt;span style="font-size: 100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;A page called 'Globals' will appear under 'Related Pages', and will contain the function prototype, with a link to its documentation in the appropriate .h file. Of course, it's called a 'Member', but one can't have everything.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Xrefitems&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Speaking of xrefitems, see that second argument in the ALIASES line above? The one that's set to "Functions", and is supposedly the header for the section that contains the xref items?&lt;br /&gt;&lt;br /&gt;Yeh, that argument does nothing. Doxygen ignores it. Go on, try it. Set it to "Doxygen, please format my hard drive". Or have it make fun of your boss or users. It makes no difference. That text will never appear.&lt;br /&gt;&lt;br /&gt;Multiple namespaces in header files&lt;br /&gt;&lt;br /&gt;This one is just plain wacky. Or rather, it's just plain lazy. Of the doxygen parser.&lt;br /&gt;&lt;br /&gt;Let's say you have a header file where you declare an interface and an abstract base class implementing that interface (never mind why, it's an example):&lt;br /&gt;&lt;pre&gt;&lt;span style="font-family: courier new;"&gt;&lt;br /&gt;&lt;span style="font-size: 100%;"&gt;namespace Mine {&lt;br /&gt;class MyInterface {&lt;br /&gt;public:&lt;br /&gt;virtual ~MyInterface() {}&lt;br /&gt;virtual void doStuff() = 0;&lt;br /&gt;};&lt;br /&gt;} /* close namespace */&lt;br /&gt;&lt;br /&gt;/* interface must be registered in gobal namespace */&lt;br /&gt;Q_DECLARE_INTERFACE( Mine::MyInterface, "com.me.Mine.MyInterface/1.0");&lt;br /&gt;&lt;br /&gt;namespace Mine {&lt;br /&gt;class MyClass : public QObject, public MyInterface {&lt;br /&gt;Q_OBJECT&lt;br /&gt;Q_INTERFACES(Mine::MyInterface)&lt;br /&gt;public:&lt;br /&gt;MyClass( QObject *parent=0 );&lt;br /&gt;virtual void doStuff();&lt;br /&gt;};&lt;br /&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Guess what happens? MyClass never appears in the documentation. The second namespace block leaves the doxygen parsers as befuddled as ... your favorite befuddlement simile.&lt;br /&gt;&lt;br /&gt;The solution of course is to put the interface class in its own header file, which is no big deal ... but really, you shouldn't *have* to.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Random \example tag links&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;OK, there's an example in docs/examples/, that dir is safely added to the Doxyfile EXAMPLE_PATH, and the file shows up where it's supposed to in the doc tree under Examples.&lt;br /&gt;&lt;br /&gt;Looking at the class for which it is an example, though, you see nothing -- or maybe it gets linked to a few rather arbitrary methods, or to methods outside of the class altogether.&lt;br /&gt;&lt;br /&gt;What's going on?&lt;br /&gt;&lt;br /&gt;Well, it turns out that doxygen decides to out-clever you, and only put a link to the example file in elements that appear in the example. Thus, if your class never directly appears as a type in the file (e.g. &lt;span style="font-family: Courier;"&gt;'PluginManager::plugin("MyPlugin").status()'&lt;/span&gt; appears instead of &lt;span style="font-family: Courier;"&gt;'Plugin p(PluginManager::plugin("MyPlugin"); p.status()'&lt;/span&gt;), then the documentation for your class will not be linked to the example, no matter how close to the \class tag you put the \example tag. Doxy knows best, eh? Surely you have no idea where you want your examples linked from.&lt;br /&gt;&lt;br /&gt;The fix is to rewrite the example so that the class/function/whatever appears clearly and distinctly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-1243248845445595086?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/1243248845445595086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/09/doxygen-gotchas.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1243248845445595086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1243248845445595086'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/09/doxygen-gotchas.html' title='Doxygen gotchas'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-5672028319197955364</id><published>2009-08-11T18:35:00.000-04:00</published><updated>2009-11-25T22:39:58.851-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sip'/><category scheme='http://www.blogger.com/atom/ns#' term='qmake'/><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><title type='text'>Using qmake without a build target</title><content type='html'>One of the greatest weaknesses of Qt's qmake utility has been its over-reliance on templates. The available templates are standard stuff for Qt projects: &lt;span style="font-weight: bold;"&gt;app&lt;/span&gt;, &lt;span style="font-weight: bold;"&gt;lib&lt;/span&gt;, and &lt;span style="font-weight: bold;"&gt;subdirs&lt;/span&gt; (new with Qt4, I believe). These are all well and good when one is just slapping together a bunch of Qt code, but what about when interfacing with other build systems? A &lt;span style="font-weight: bold;"&gt;command&lt;/span&gt; template would be quite useful.&lt;br /&gt;&lt;br /&gt;The follow project file is a workaround. The &lt;span style="font-weight: bold;"&gt;lib&lt;/span&gt; template is used (although &lt;span style="font-weight: bold;"&gt;app&lt;/span&gt; could be used as well) with empty HEADERS and SOURCES variables. The qmake variables that specify which build tools to use are set to a NOP (no-operation) tool such as &lt;span style="font-weight: bold;"&gt;echo&lt;/span&gt; or &lt;span style="font-weight: bold;"&gt;true&lt;/span&gt;. An "extra" target is created for the command to be executed, and set to be a pre-dependency of the main target.&lt;br /&gt;&lt;br /&gt;Background: There is a config.py in . that has &lt;span style="font-weight: bold;"&gt;sip&lt;/span&gt; generate all C++ code, as well as the Makefile, in the directory 'sip'. The python module (sip_test.so) is output to the same directory. This project filed is invoked, along with many others, from a &lt;span style="font-weight: bold;"&gt;subdirs&lt;/span&gt; project file in the parent directory.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:100%;"  &gt;&lt;br /&gt;-----------------------------------------------------&lt;br /&gt;# sip_test.pro&lt;br /&gt;TEMPLATE    = lib&lt;br /&gt;TARGET      = sip_test&lt;br /&gt;&lt;br /&gt;# Check that sip exists&lt;br /&gt;unix {&lt;br /&gt;      !system(sip -V &gt; /dev/null) {&lt;br /&gt;              error( "SIP does not exist!" )&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# sip build command and makefile targets&lt;br /&gt;sip.commands          =    python ./config.py &amp;amp;&amp;amp; cd sip &amp;amp;&amp;amp; make&lt;br /&gt;QMAKE_EXTRA_TARGETS   +=   sip&lt;br /&gt;PRE_TARGETDEPS        =    sip&lt;br /&gt;&lt;br /&gt;# compiler toolchain override&lt;br /&gt;unix {&lt;br /&gt;     # 'true' should exist on Unix and OS X. Win32 folk can&lt;br /&gt;     # use TYPE.EXE or something.&lt;br /&gt;      NOP_TOOL    = true&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;QMAKE_CC        =    $$NOP_TOOL&lt;br /&gt;QMAKE_CXX       =    $$NOP_TOOL&lt;br /&gt;QMAKE_LINK      =    $$NOP_TOOL&lt;br /&gt;QMAKE_LN_SHLIB  =    $$NOP_TOOL&lt;br /&gt;&lt;br /&gt;# Copy the .so created by sip.commands to the current directory&lt;br /&gt;QMAKE_POST_LINK        =    cp sip/$${TARGET}.so .&lt;br /&gt;-----------------------------------------------------&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;With this project file, the standard "qmake &amp;amp;&amp;amp; make" command works as expected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-5672028319197955364?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/5672028319197955364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/08/using-qmake-without-build-target.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5672028319197955364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5672028319197955364'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/08/using-qmake-without-build-target.html' title='Using qmake without a build target'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-5975967750731413513</id><published>2009-05-30T17:50:00.000-04:00</published><updated>2009-05-30T18:09:54.049-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='e17'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='r500'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Toshiba R500 Backlight Button</title><content type='html'>Keep forgetting to u/l this script for toggling the r500 backlight using toshset:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;#!/bin/sh                                                                       &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;TOSHSET=/usr/local/bin/toshset&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;STATE=`sudo $TOSHSET -trmode 2&gt;/dev/null | grep -a transreflective | \&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;       cut -f 3 -d ' '`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;if [ $STATE = "on" ]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;then&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        NEW_STATE="off"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;else&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        NEW_STATE="on"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;fi&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;sudo $TOSHSET -trmode $NEW_STATE&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Xev shows the keycode for the backlight (and the 'i' internet/information/idiot key, so they'll both do the same thing) to be 180, which is already taken by XF86HomePage in Ubuntu. If for some reason this key is undefined (i.e. nothing shows up on &lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;`xmodmap -pke | grep '^keycode 180'`&lt;/span&gt;&lt;/span&gt;), add the following line to ~/.Xmodmap (which might need to be symlinked to ~/.xmodmaprc on non-Ubuntu Linuxes):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;    keycode 180 = XF86HomePage&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In E17, edit the keyboard bindings using Settings-&gt; Settings Panel -&gt; Input -&gt; Key Bindings. For XF86HomePage, select the &lt;span style="font-weight: bold;"&gt;Launch&lt;/span&gt; :&lt;span style="font-style: italic;"&gt; Defined Command&lt;/span&gt; option, and point it to the above shell script (e.g. in ~/bin).&lt;br /&gt;&lt;br /&gt;Note that the script uses sudo to launch toshset, so you will either need to change this to invoke an GUI sudo program like gtksu, or add a line like the following to /etc/sudoers:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;$USER     ALL=(root) NOPASSWD: /usr/local/bin/toshset&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;where $USER is your username. Note that this allows any toshset command to be run as root without a password from this user's session, which could be exploited if someone were sufficiently motivated.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-5975967750731413513?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/5975967750731413513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/05/toshiba-r500-backlight-button.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5975967750731413513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/5975967750731413513'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/05/toshiba-r500-backlight-button.html' title='Toshiba R500 Backlight Button'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-219898382736218931</id><published>2009-03-26T23:03:00.000-04:00</published><updated>2009-06-18T05:12:33.086-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gcc'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Linking static libraries into a shared library</title><content type='html'>The ability to link static libraries into a shared library -- e.g.&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;gcc -shared -o libstuff.so lib1.a lib2.a lib3.a&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;-- disappeared in one of the 3.x series. This broke my tendency to build applications as static libraries, linked into a shared library, linked to by an executable consisting of little more than a &lt;span style="font-family:courier new;"&gt;main().&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Turns out all that was missing was a GNU ld flag to prevent ld from optimizing the unused object code out of the shared library.&lt;br /&gt;&lt;br /&gt;The solution is to wrap the .a files in &lt;span style="font-family:courier new;"&gt;--whole-archive&lt;/span&gt; flags, e.g.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;gcc -shared -o libstuff.so -Wl,-whole-archive lib1.a lib2.a lib3.a -Wl,-no-whole-archive&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-219898382736218931?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/219898382736218931/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/03/linking-static-libraries-into-shared.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/219898382736218931'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/219898382736218931'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/03/linking-static-libraries-into-shared.html' title='Linking static libraries into a shared library'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-1534828782677214918</id><published>2009-03-06T15:19:00.001-05:00</published><updated>2011-01-04T15:20:50.629-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='e17'/><category scheme='http://www.blogger.com/atom/ns#' term='upgrade rage'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Insipid Ibex</title><content type='html'>Finally upgraded (K)Ubuntu to 8.10. Thanks for breaking my xorg config, guys, by assuming that HAL could actually do a better job that my hand-picked settings. This is easily fixed by uncommenting all of the lines commented out by HAL, but it's amazing that it had to be done at all, since the HAL change made X flat-out stop working. Also, for some reason, ~/.xsession no longer works -- one has to use ~/.xsessionrc instead. Those wacky Ubuntites.&lt;br /&gt;&lt;br /&gt;Upgraded E17 shortly afterwards, using the packages at&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 85%;"&gt;&lt;span style="font-family: courier new;"&gt;deb http://greenie.sk/ubuntu intrepid e17&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The new version of E has compiz support (which has to almost entirely be disabled for it to run at E17's proper speed) and looks nice. The modules all seem to work, for once.&lt;br /&gt;&lt;br /&gt;But it crashes!&lt;br /&gt;&lt;br /&gt;The first one came immediately, and was fixed by wiping ~/.e, which contained all of my old config settings. Apparently E17 can't reliably be upgraded with an existing config. Thanks for moving to a binary config file, guys, so that this is actually a far larger problem than it should be. Really, how hard is it to use the binary config only at runtime, and compile from text to binary whenever a config change is made?&lt;br /&gt;&lt;br /&gt;Some of the other crashes appear to be from the OpenGeu inclusion of compiz. After removing the calls to emerald and ecomorph from enlightenment_startup.sh, and stripping out the -evil command line option (which, along with -good and -psychotic, did not seem to make things stable), it's become pretty stable, if a little buggy. Dialog boxes occasionally do not disappear when closed, requiring a restart of E17.&lt;br /&gt;&lt;br /&gt;Also, some E17 idiocies remain -- things that seemed to be oversights before, but now (having been a pretty stable WM for the past three years) appear to be truly asinine design decisions.&lt;br /&gt;&lt;br /&gt;Specifically:&lt;br /&gt;&lt;br /&gt;* There is no way to modify the properties of an application (short of adding it to iBar, or modifying the desktop file directly). A dialog exists for this (it's used by ibar and when creating a new application), so it can't be much extra work.&lt;br /&gt;&lt;br /&gt;* One cannot cut/copy/paste between E17 widgets and X. This is nice to be able to do when, say, setting an icon file path (since the desktop files for KDE applications do not seem to be handled correctly by E17, and therefore have no icon files). Hope you like typing.&lt;br /&gt;&lt;br /&gt;* The binary only config, mentioned above. Really, there is no excuse for this -- the rest of the industry learned that lesson in the 90s. Even Windows allows its abhorred registry to be read from and written to text files.&lt;br /&gt;&lt;br /&gt;Aside from that, Ibex works well, and the upgrade was generally painless. The only sticky points were the Xorg conf file (their mistake), E17 (my fault for running it), and the patch I needed to add to get toshset supported by the latest kernel (the maintainer's fault, for not getting it merged after 2 years of reliable performance).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-1534828782677214918?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/1534828782677214918/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/03/e17-suckiness.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1534828782677214918'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1534828782677214918'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/03/e17-suckiness.html' title='Insipid Ibex'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-157615303013013221</id><published>2009-02-11T18:14:00.000-05:00</published><updated>2009-03-23T16:55:33.384-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><title type='text'>more ffox plugins</title><content type='html'>These are more for the UI folk:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/271"&gt;Colorzilla&lt;/a&gt;:Look up a pixel's color value&lt;br /&gt;&lt;br /&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/5235"&gt;Unicode lookup/converter&lt;/a&gt; : Character lookup utility&lt;br /&gt;&lt;br /&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/539"&gt;MeasureIt&lt;/a&gt; : Ruler widget for measuring distance in pixels&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-157615303013013221?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/157615303013013221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/02/more-ffox-plugins.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/157615303013013221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/157615303013013221'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/02/more-ffox-plugins.html' title='more ffox plugins'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-6289981903134942651</id><published>2009-02-03T22:52:00.000-05:00</published><updated>2009-02-03T23:58:48.566-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Vim links</title><content type='html'>People either love vi or hate it; if you hate it, sod off and take your parentheses with you.&lt;br /&gt;&lt;br /&gt;It's good to have a printout of the vi reference card handy, and a cheat sheet or two saved locally:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://tnerual.eriogerg.free.fr/vimqrc.pdf"&gt;Vi Reference Card&lt;/a&gt; (PDF)&lt;br /&gt;&lt;a href="http://www.viemu.com/vi-vim-cheat-sheet.gif"&gt;Graphical Vim Cheat Sheet&lt;/a&gt; (GIF)&lt;br /&gt;&lt;a href="http://www.bullium.com/support/vim.html"&gt;Vim Cheat Sheet&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In support of the "learn a new vim command every day" effort, both &lt;a href="http://vim.wikia.com/wiki/Best_Vim_Tips"&gt;Vim Tips&lt;/a&gt; and the &lt;a href="http://www.oualline.com/vim-cook.html"&gt;Vim Cookbook&lt;/a&gt; have proved interesting, as has the &lt;a href="http://www.softpanorama.org/Editors/Vimorama/vim_piping.shtml"&gt;piping&lt;/a&gt; feature.&lt;br /&gt;Speaking of pipes, &lt;a href="http://www.ivarch.com/programs/pv.shtml"&gt;pv&lt;/a&gt; is quite an interesting utility. So is &lt;a href="http://www.debian-administration.org/articles/464"&gt;histring&lt;/a&gt;, but it doesn't seem to be available (or maintained) except in &lt;a href="http://dir.filewatcher.com/d/Other/i386/Applications/Text/histring-1.0.0-1.i386.rpm.6393.html"&gt;rpm&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Vim has a ton of plugins worth checking out, including:&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1984"&gt;FuzzyFinder&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1520"&gt;OmniComplete&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=273"&gt;TagList&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2164"&gt;RenameWithCScope&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2368"&gt;C Call Tree Explorer&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1638"&gt;SourceCodeObedience&lt;/a&gt;&lt;br /&gt;&lt;a href="http://vim.sourceforge.net/scripts/script.php?script_id=663"&gt;VimDebug&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=159"&gt;MiniBufferExplorer&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1318"&gt;SnippetsEmu&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=578"&gt;AllFold&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2502"&gt;Fly&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2010"&gt;SessionManager&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=31"&gt;Alternate&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2511"&gt;RunView&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2524"&gt;Scratch*&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=90"&gt;VcsCommand&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=171"&gt;MultVals&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1934"&gt;GitCommit&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1846"&gt;GitDiff&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2185"&gt;GitFile&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=2258"&gt;Git Branch Info&lt;/a&gt;&lt;br /&gt;There is also &lt;a href="http://www.vim.org/scripts/script.php?script_id=1658"&gt;NERDTree&lt;/a&gt;, but its moderate utility does not justify its horrible name.&lt;br /&gt;&lt;br /&gt;Most languages have syntax and snippet plugins, such as:&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=213"&gt;C&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=790"&gt;Python&lt;/a&gt;&lt;br /&gt;&lt;a href="http://vim-ruby.rubyforge.org/"&gt;Ruby&lt;/a&gt;/&lt;a href="http://www.vim.org/scripts/script.php?script_id=1567"&gt;Rails&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=556"&gt;Perl&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=1572"&gt;SQL&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=966"&gt;x86&lt;/a&gt; and &lt;a href="http://www.vim.org/scripts/script.php?script_id=1650"&gt;x86-64&lt;/a&gt; asm&lt;br /&gt;&lt;a href="http://www.vim.org/scripts/script.php?script_id=301"&gt;XML/HTML&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Finally, to forestall the inevitable requests, here is a sample &lt;a href="http://dotfiles.org/%7Emkfs/.vimrc"&gt;.vimrc&lt;/a&gt; .&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-6289981903134942651?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/6289981903134942651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/02/vim-links.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6289981903134942651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6289981903134942651'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/02/vim-links.html' title='Vim links'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8373227629462868398</id><published>2009-02-03T22:30:00.000-05:00</published><updated>2009-02-04T19:13:15.478-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='tcp/ip'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>DNS</title><content type='html'>DNS timeouts have been pretty bad lately; decided to take action and make resolve.conf actually be usable. Of course, it gets overwritten every time DHCP is used to configure an interface, so the name servers have to be added to dhclient.conf .&lt;br /&gt;&lt;br /&gt;There are quite a lot of reliable servers to choose from:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://www.opendns.com/smb/start"&gt;OpenDNS&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.tech-faq.com/public-dns-servers.shtml"&gt;TechFAQ public dns server list&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.dnsserverlist.org/"&gt;dnsserverlist.org&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.dnsserverlist.org/index.php?oby=Q_RTT"&gt;dnsserverlist.org organized by round trip time&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The last two are particularly useful: dnsserverlist.org recommends three DNS servers based on your current IP, and provides an option for sorting their list by round-trip-time.&lt;br /&gt;&lt;br /&gt;"Permanent" DNS servers can be added by editing /etc/dhcp3/dhclient.conf and adding 'prepend domain-name-servers' lines:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 216.224.112.14;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 67.198.198.213;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 69.111.95.106 ;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 208.67.222.222;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 208.67.220.220;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 4.2.2.1;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 4.2.2.2;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 151.197.0.38;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 151.202.0.84;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;prepend domain-name-servers 151.203.0.84;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Remember to list these in reverse-order: the last server prepended will be the first line in resolv.conf.&lt;br /&gt;&lt;br /&gt;The ultimate solution, of course, is to &lt;a href="http://www.debianadmin.com/local-dns-cache-for-faster-browsing-on-ubuntu-system.html"&gt;set up a local caching DNS server&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8373227629462868398?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8373227629462868398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/02/dns.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8373227629462868398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8373227629462868398'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/02/dns.html' title='DNS'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-4545637006597439606</id><published>2009-01-30T03:57:00.000-05:00</published><updated>2009-02-03T23:56:02.115-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='words'/><title type='text'>Unfortunate words</title><content type='html'>&lt;span style="font-weight: bold;"&gt;leverage&lt;/span&gt;: To utilize or exploit in an unspecified, hand-wavy, and generally unplanned manner.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;We leverage the core compentency of our acquisitions in this sector to maximise ROI throughout the project lifecycle.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;pundit&lt;/span&gt;: A cheerleader in the sport of politics.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Pundits are claiming that the candidate's gaffe is an opportunity to appeal to blue-collar voters, in stark contrast to his opponent's overly intellectual demeanor.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;punditry&lt;/span&gt;: The presentation of half-considered opinion as research- or poll-backed fact. Generally considered the province of pundits and journalists.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(See above)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;blogosphere&lt;/span&gt;: Amateur hour in the world of publish-or-perish.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Lo, it is easier for a shark to stop swimming, than for a blogger to refrain from banal commentary.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-4545637006597439606?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/4545637006597439606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/01/unfortunate-words.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4545637006597439606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/4545637006597439606'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/01/unfortunate-words.html' title='Unfortunate words'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8631042347525367457</id><published>2009-01-28T02:15:00.000-05:00</published><updated>2009-01-28T02:51:11.008-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Productive Programmer</title><content type='html'>Just tore through Neal Ford's Productive Programmer (978-0-596-51978-0), ended up with mixed feelings about it.&lt;br /&gt;&lt;br /&gt;The first part of the book is pretty decent, with pointers to some good tools. This is also a weakness: the book is not going to be a timeless classic, and will likely be of half utility in two to five years.&lt;br /&gt;&lt;br /&gt;The second part has good advice, but is mostly a rehash of agile methodologies. Agile has (or rather, has appropriated) a lot of good ideas, but it is ultimately a methodology, and methodologies lead to two things: zealotry and incomplete projects.&lt;br /&gt;&lt;br /&gt;The advice given tends towards the obvious ("use the command line", "learn keyboard shortcuts", "make macros and scripts"), and UNIX is presented as something used by Other People.&lt;br /&gt;&lt;br /&gt;In fact the book seems to assume that the reader is encumbered by the two most enfeebling technologies out there: Windows and Java. Windows is inescapable as a platform, but one always has the choice not to code like a Windows programmer (excuse me, "developer"). Java seems to be the language of choice in... well, in companies that I have no interest in working for, so nothing lost there. Buying into the Windows Way or the Java Way means you've created a lot of your own problems, and if this book helps you solve some of them, more power to you.&lt;br /&gt;&lt;br /&gt;For the non-Windows-and/or-Java Way programmer, stick with the classics by Brooks, Hunt/Thomas, Kernighan, Plauger, Bentley, Fowler. Maybe some Beck, but you have to be careful with evangelicals.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8631042347525367457?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8631042347525367457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/01/productive-programmer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8631042347525367457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8631042347525367457'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/01/productive-programmer.html' title='Productive Programmer'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-6152758595963605379</id><published>2009-01-27T23:12:00.000-05:00</published><updated>2009-01-27T23:49:51.770-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='kernel'/><title type='text'>Free as in...</title><content type='html'>Finally got sick of building custom Linux kernels just to change CONFIG_HZ from the inane default of 250 to something that makes the 10ms timer in my (proprietary) device driver work reliably (instead of firing 83 times a second). Switched to hi-resolution timers, and immediate encountered this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-family:courier new;"&gt;FATAL: modpost: GPL-incompatible module my_module.ko uses GPL-only symbol 'hrtimer_cancel'&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Turns out, after reading stuff like &lt;a href="http://www.linuxdevices.com/articles/AT9161119242.html"&gt;http://www.linuxdevices.com/articles/AT9161119242.html&lt;/a&gt; and &lt;a href="http://www.linuxdevices.com/articles/AT5041108431.html"&gt;http://www.linuxdevices.com/articles/AT5041108431.html&lt;/a&gt;, that the lunatics have taken over the asylum: specific kernel subsystems can be restricted so that only GPL modules can call them.&lt;br /&gt;&lt;br /&gt;This is ludicrous: why should GPL code care who calls it? This has nothing to do with distribution; this is *usage*.&lt;br /&gt;&lt;br /&gt;Not to mention entirely unlike the "free as in speech" ethos that the FSF claims to support. "Free speech" generally doesn't imply "say anything you like, just don't say it in kernel mode!".&lt;br /&gt;&lt;br /&gt;Between this GPL nonsense and the changing of the CONFIG_HZ default value in a minor release, the Linux kernel is proving itself quite unreliable. I may have to move this entire product over to FreeBSD.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-6152758595963605379?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/6152758595963605379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2009/01/free-as-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6152758595963605379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/6152758595963605379'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2009/01/free-as-in.html' title='Free as in...'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-8024717219497614481</id><published>2008-06-18T17:39:00.000-04:00</published><updated>2008-06-18T18:21:10.415-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><title type='text'>Smart Search</title><content type='html'>By far the most useful plugin I've added to Firefox in the past half-year or so:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/188"&gt;https://addons.mozilla.org/en-US/firefox/addon/188&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;SmartSearch adds a context menu item that allows you to send selected text (or whatever word you right-clicked on) to any one of the links in the Bookmarks 'Quick Searches' folder (Bookmarks-&gt;Organize Bookmarks-&gt;Bookmarks Menu-&gt;Quick Searches). I had to create this folder myself, because at some point I short-sightedly deleted it as being useless.&lt;br /&gt;&lt;br /&gt;The SmartSearch &lt;a href="http://twofoos.org/content/quicksearches/"&gt;page on quicksearches&lt;/a&gt; has a number of handy links such as a dictionary, thesaurus, IMDB, amazon, netcraft, whois, wayback archive, and google maps. I added a few more for google images and some source code pages:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://images.google.com/images?hl=en&amp;amp;q=%s&amp;amp;gbv=2"&gt;Google Images Quicksearch&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.google.com/codesearch?q=%s&amp;amp;hl=en"&gt;Google Code Quicksearch&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.koders.com/default.aspx?s=%s&amp;amp;la=*&amp;amp;li=*"&gt;Koders Quicksearch&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.krugle.com/kse/files?query=%s"&gt;Krugle Code Quicksearch&lt;/a&gt; (requires JS)&lt;br /&gt;&lt;a href="http://www.codase.com/search/smart?join=%s&amp;amp;scope=join/join&amp;amp;lang=*&amp;amp;project="&gt;Codease SmartQuery&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.codase.com/search/text?join=%s&amp;amp;scope=join/join&amp;amp;lang=*&amp;amp;project="&gt;Codease FullText Query&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Obviously many more can come in handy, and the code search engines can be trimmed down to just one or two (probably Koders and Codease).&lt;br /&gt;&lt;br /&gt;The good news is that new Quicksearches are simple to create: navigate to the search page, right-click on the search form, select 'Add a keyword for this search', invent some useful keyword, and use the Quick Searches folder for the 'Create in' location.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-8024717219497614481?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/8024717219497614481/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2008/06/smart-search.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8024717219497614481'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/8024717219497614481'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2008/06/smart-search.html' title='Smart Search'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-7770285496656129324</id><published>2008-05-27T17:43:00.000-04:00</published><updated>2008-05-27T17:53:02.887-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Nightly Tester Tools</title><content type='html'>Got sick of the Intel driver crashing X11, and Ubuntu releasing their upgraded packages only on the recent version (Horny Heron or something). Bit the bullet, did the upgrade, and...&lt;br /&gt;&lt;br /&gt;...Firefox 3beta got installed! What the hell?!? Poof go all (well, most) of my extensions, you know those handy things I use for, oh, locking down Firefox, anonymous browsing, other small conveniences that make using such a poorly-performing browser worthwhile.&lt;br /&gt;&lt;br /&gt;Included in these was Zotero, in which I have quite a number of documents stored for offline viewing, and which *still* has no independent UI for accessing them (even the Open Office plugin requires a running instance of Firefox). Suckfest à la mode, just what I ordered!&lt;br /&gt;&lt;br /&gt;A search of the Zotero forums, though, turns up a link to the &lt;a href="http://www.oxymoronical.com/web/firefox/nightly"&gt;Nightly Tester Tools&lt;/a&gt; plugin, which can be used to run an extension for a previous version of Firefox. Zotero is now up and running on Firefox 3beta.&lt;br /&gt;&lt;br /&gt;Version 3 does seem to be a bit faster than 2, and hasn't been too crashy. The theme/extension writers should really get their act together and update/port their stuff before this puppy hits the street.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-7770285496656129324?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/7770285496656129324/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2008/05/nightly-tester-tools.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7770285496656129324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7770285496656129324'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2008/05/nightly-tester-tools.html' title='Nightly Tester Tools'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-1087349577175488700</id><published>2008-05-07T03:27:00.000-04:00</published><updated>2008-05-07T03:37:45.747-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Pulseaudio, Firefox, Flash 9</title><content type='html'>Upgrading to the latest flash plugin (9) kills audio in Firefox. Awesome!&lt;br /&gt;&lt;br /&gt;Based on the PulseAudio information on &lt;a href="http://www.pulseaudio.org/wiki/PerfectSetup"&gt;http://www.pulseaudio.org/wiki/PerfectSetup&lt;/a&gt;&lt;br /&gt;and the library provided by &lt;a href="http://project.revolutionlinux.com/PulseAudio"&gt;revolutionlinux&lt;/a&gt;, it took only a couple of steps to get things working:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;cd /usr/src&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;git-clone http://git.0pointer.de/repos/libflashsupport.git&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;cd libflashsupport&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;sudo ./bootstrap.sh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;sudo ./configure --prefix=/usr&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;sudo make&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;sudo make install&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Then restart firefox.&lt;br /&gt;&lt;br /&gt;It probably helps to have /etc/firefox/firefoxrc contain the line&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;FIREFOX_DSP="padsp"&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...and to have /etc/asound.conf contain the lines&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;pcm.!default {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        type pulse&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;ctl.!default {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        type pulse&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;pcm.pulse {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        type pulse&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;ctl.pulse {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        type pulse&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...but libflashsupport is the magic bit.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-1087349577175488700?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/1087349577175488700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2008/05/pulseaudio-firefox-flash-9.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1087349577175488700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/1087349577175488700'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2008/05/pulseaudio-firefox-flash-9.html' title='Pulseaudio, Firefox, Flash 9'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-7203664794166673056</id><published>2008-05-05T18:52:00.000-04:00</published><updated>2008-05-05T18:56:05.084-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>VimMate</title><content type='html'>Yet &lt;a href="http://vim.sourceforge.net/scripts/script.php?script_id=1318"&gt;another&lt;/a&gt; Textmate-style extension to Vim:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://vimmate.rubyforge.org/"&gt;http://vimmate.rubyforge.org/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This one integrates a project manager with Vim. Jury is still out on whether it will prove useful, but coupled with the &lt;a href="http://vim.sourceforge.net/scripts/script.php?script_id=663"&gt;Vim Debugger plugin&lt;/a&gt; it could prove quite useful for the dynamic languages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6088262555941383716-7203664794166673056?l=entrenchant.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://entrenchant.blogspot.com/feeds/7203664794166673056/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://entrenchant.blogspot.com/2008/05/vimmate.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7203664794166673056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6088262555941383716/posts/default/7203664794166673056'/><link rel='alternate' type='text/html' href='http://entrenchant.blogspot.com/2008/05/vimmate.html' title='VimMate'/><author><name>mkfs</name><uri>http://www.blogger.com/profile/10211910723613676910</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/-PFycZuCIRjo/TgvVbyULQWI/AAAAAAAAABU/dVRt4Jephmo/s220/Mitchum_Post.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6088262555941383716.post-5469931126656617859</id><published>2008-05-05T17:01:00.000-04:00</published><updated>2008-05-05T17:16:00.324-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Git as a backup tool</title><content type='html'>Recently migrated from CVS to GIT at work, and started to look at other uses of local GIT repositories. Backing up config files in home directories seemed a natural application, and as usual someone else has thought of it, implemented it, and abandoned it:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://jean-francois.richard.name/ghh/"&gt;git-home-history&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Getting this built is pretty straightforward:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;   cd /usr/src&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   git clone http://jean-francois.richard.name/ghh.git&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   cd ghh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   ./autogen.sh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   make&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   make install&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   cd ~&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   git-home-history init&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   vi .gitignore&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   git commit -a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   crontab -e&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  * */4 * * *  /usr/local/bin/git-home-history commit &gt;/dev/null 2&gt;&amp;amp;1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The last line is what should be added to crontab to run this every 4 hours.&lt;br /&gt;&lt;br /&gt;There are similar projects for /etc and for general backup:&lt;br /&gt; &lt;a href="http://kitenet.net/%7Ejoey/code/etckeeper/"&gt;etckeeper&lt;/a&gt;&lt;br /&gt; &lt;a href="http://e
