<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2titles.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemtitles.css" type="text/css" media="screen"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>TechKnack</title><link>http://techknack.net</link><description>The rantings of a techie</description><language>en</language><generator>http://wordpress.org/?v=2.6.2</generator><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/TechKnack" type="application/rss+xml" /><feedburner:emailServiceId>1138445</feedburner:emailServiceId><feedburner:feedburnerHostname>http://www.feedburner.com</feedburner:feedburnerHostname><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2FTechKnack" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FTechKnack" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2FTechKnack" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/TechKnack" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FTechKnack" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FTechKnack" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FTechKnack" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.addtoany.com/?linkname=TechKnack&amp;linkurl=http%3A%2F%2Ffeeds.feedburner.com%2FTechKnack&amp;type=feed" src="http://www.addtoany.com/addfr-b.gif">Add to Any Feed Reader</feedburner:feedFlare><item><title>Using a 1D array like a 2D (or 3D) array</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/452129613/</link><category>linux</category><category>windows</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Thu, 13 Nov 2008 13:44:50 -0600</pubDate><guid isPermaLink="false">http://techknack.net/?p=191</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>So I&#8217;ve been working on a 3D graphics project for school, and I&#8217;ve come across an inconvenience: while g++, the <b>linux</b> compiler, will happily let you declare a new multi-dimensional array with a variable as the size, Visual Studio (the <b>MicroSoft</b> compiler) complains that such a daunting task is too difficult &#8212; &#8220;I expect a FIXED, STATIC size for these arrays!&#8221; it says.</p>
<p>For example:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw4">void</span> blah<span class="br0">&#40;</span><span class="kw4">int</span> width, <span class="kw4">int</span> height<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp;<span class="kw4">unsigned</span> <span class="kw4">char</span> Array<span class="br0">&#91;</span>height<span class="br0">&#93;</span><span class="br0">&#91;</span>width<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span><br />
&nbsp;</div>
<p>Seems logical, right?  Create a new array, with whatever width and height were passed to the function.  And, as I said, g++ will happily let you do this (well, maybe not <i>this</i> particular example, but you get the idea), but VS complains.</p>
<p>As such, and since I have to have a Windows binary to submit to the professor, I&#8217;ve ventured into the world of <b>malloc</b>.  Sounds like a villain&#8217;s name, doesn&#8217;t it?  For those who don&#8217;t know, <a href="http://www.elook.org/programming/c/malloc.html">malloc</a> is (one of?) C&#8217;s <b>m</b>emory <b>alloc</b>ation function(s) &#8212; give it the amount of memory you need, it&#8217;ll reserve that much memory and return the base address (in the form of a &#8220;void *&#8221;.  pfft.).  Of course, it&#8217;s not that simple, but that&#8217;s the basic idea.</p>
<p>To get our two-dimensional array with malloc, you need to get fancy with the looping.  For example:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw4">void</span> blah<span class="br0">&#40;</span><span class="kw4">int</span> width, <span class="kw4">int</span> height<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp;<span class="kw4">unsigned</span> <span class="kw4">char</span>** Array = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>**<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span>height*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>*<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp;<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i=<span class="nu0">0</span>; i&lt;height ; i++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Array<span class="br0">&#91;</span>i<span class="br0">&#93;</span> = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>*<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span>width*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp;<span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
&nbsp;</div>
<p>What a freakin&#8217; mess!  Instead of reading &#8220;create a height-by-width array&#8221;, this reads more like &#8220;eat up as much memory as you can&#8221;.  Yes, you have to semi-manually create each level of the &#8220;dynamic&#8221; array, all so you can conveniently use &#8220;Array[n][m]&#8221; later in your program.  Want a three-dimensional array (used to hold RGB images &#8212; Array[height][width][3])?</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="kw4">void</span> blah<span class="br0">&#40;</span><span class="kw4">int</span> width, <span class="kw4">int</span> height<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp;<span class="kw4">unsigned</span> <span class="kw4">char</span>*** Array = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>***<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span>height*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>**<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp;<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i=<span class="nu0">0</span>; i&lt;height ; i++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; Array<span class="br0">&#91;</span>i<span class="br0">&#93;</span> = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>**<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span>width*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>*<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> j=<span class="nu0">0</span>; j&lt;width; j++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Array<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="br0">&#91;</span>j<span class="br0">&#93;</span> = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>*<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span><span class="nu0">3</span>*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp;<span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
&nbsp;</div>
<p>And so on for each dimension you wish to add.  Truly hideous.  So many levels of pointers to keep track of, so many <i>stinking</i> loops!  There is such a better way <img src='http://techknack.net/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<p>Instead of using unsightly code to allow the convenience of multiple subscripts, you could just as well create a width*height(*3), single-dimensional array, and use some simple math to get the proper offset.  Here are examples of up to three dimensions (I&#8217;ve actually used the three-dimension one in my program to load an image file, works perfectly):</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="co1">// One dimension &#8212; easy enough:</span><br />
<span class="kw4">unsigned</span> <span class="kw4">char</span>* OneDim = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>*<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span>width*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i=<span class="nu0">0</span>; i&lt;width ; i++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp;OneDim<span class="br0">&#91;</span>i<span class="br0">&#93;</span>;<br />
<span class="br0">&#125;</span></p>
<p><span class="co1">// Two dimensions &#8212; a little trickier:</span><br />
<span class="kw4">unsigned</span> <span class="kw4">char</span>* TwoDim = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>*<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span>width*height*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i=<span class="nu0">0</span>; i&lt;height; i++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp;<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> j=<span class="nu0">0</span>; j&lt;width; j++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="co1">// TwoDim[i][j] = </span><br />
&nbsp; &nbsp; &nbsp; TwoDim<span class="br0">&#91;</span>i*width+j<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp;<span class="br0">&#125;</span><br />
<span class="br0">&#125;</span></p>
<p><span class="co1">// Three dimensions &#8212; w00t, crazy!</span><br />
<span class="kw4">unsigned</span> <span class="kw4">char</span>* ThreeDim = <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span>*<span class="br0">&#41;</span>malloc<span class="br0">&#40;</span>width*height*<span class="nu0">3</span>*<span class="kw4">sizeof</span><span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">char</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br />
<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> i=<span class="nu0">0</span>; i&lt;height; i++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp;<span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> j=<span class="nu0">0</span>; j&lt;width; j++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">int</span> k=<span class="nu0">0</span>; k&lt;<span class="nu0">3</span>; k++<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">// ThreeDim[i][j][k] = </span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ThreeDim<span class="br0">&#91;</span><span class="br0">&#40;</span>i*width+j<span class="br0">&#41;</span>*<span class="nu0">3</span>+k<span class="br0">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp;<span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
&nbsp;</div>
<p>If I&#8217;m not mistaken, the recursive pattern for the subscript is &#8220;(other-loops)*max+counter&#8221;, starting from the innermost loop.  So three dimensions goes like this:</p>
<pre>
(other-loops)*3+k
((other-loops)*width+j)*3+k
(((other-loops)*height+i)*width+j)*3+k

no more loops, use "0":

(((0)*height+i)*width+j)*3+k
((i)*width+j)*3+k
(i*width+j)*3+k
</pre>
<p>So, for a fourth loop &#8220;m&#8221; going from 0 to, say, 40, we would probably have:</p>
<pre>
((i*width+j)*3+k)*40+m
</pre>
<p>And so on, but who uses 4D arrays, anyways?</p>
<p><i>Disclaimers:<br />
&#8220;for (int i=0; &#8230;&#8221; syntax is, as far as I know, a C++ feature; in true C, you would have to declare &#8220;i&#8221; beforehand, then use it in the loop.<br />
When creating objects with malloc, be sure to <a href="http://www.elook.org/programming/c/free.html">free</a> the memory when you&#8217;re done with it; I refuse to be held responsible for memory leaks in your program.<br />
C library &#8220;stdlib.h&#8221; must be included to use these functions.<br />
Yes, you have to typecast malloc&#8217;s return value to whatever your data type is, unless your variable itself is a &#8220;void *&#8221;, but why would you need a pointer to nothing?<br />
For each level of the array in the first malloc example, you need as many &#8220;*&#8221; as you have levels left to create &#8212; three for the first level, two for the next, one for the last, etc.<br />
Strictly speaking, &#8220;sizeof(unsigned char)&#8221; translates to &#8220;1&#8243;, so it&#8217;s not necessary here; however, it is required to get the right size for other data types, in which case the hideous factor is no more reduced.<br />
I have not tested, nor plan on testing, the four-dimensional subscript example given above.  If it doesn&#8217;t work and you figure out how to make it work, feel free to post your solution <img src='http://techknack.net/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </i></width></pre>
<p></height></pre>
<p></height></pre>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=mLMvN"><img src="http://feeds.feedburner.com/~f/TechKnack?i=mLMvN" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=1URmn"><img src="http://feeds.feedburner.com/~f/TechKnack?i=1URmn" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=Q1IGn"><img src="http://feeds.feedburner.com/~f/TechKnack?i=Q1IGn" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=1Ub2n"><img src="http://feeds.feedburner.com/~f/TechKnack?i=1Ub2n" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/452129613" height="1" width="1"/>]]></content:encoded><description>So I&amp;#8217;ve been working on a 3D graphics project for school, and I&amp;#8217;ve come across an inconvenience: while g++, the linux compiler, will happily let you declare a new multi-dimensional array with a variable as the size, Visual Studio (the MicroSoft compiler) complains that such a daunting task is too difficult &amp;#8212; &amp;#8220;I expect a [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/using-a-1d-array-like-a-2d-or-3d-array/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/using-a-1d-array-like-a-2d-or-3d-array/</feedburner:origLink></item><item><title>I Love Me Some APIs</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/424936171/</link><category>web</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Sat, 18 Oct 2008 16:54:14 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=182</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I love <a href="http://en.wikipedia.org/wiki/API"><acronym title="Application Programming Interfaces">APIs</acronym></a>.  They make using web services so much easier.</p>
<p>I&#8217;ve been developing an IRC bot as a side project, and three of its functions require the use of web services.  It can shorten urls using <a href="http://is.gd/">http://is.gd/</a>, search google, and validate web pages using the W3C&#8217;s <a href="http://validator.w3.org/">HTML</a> and <a href="http://jigsaw.w3.org/css-validator/">CSS</a> validators.  Fortunately, and to my great elation, all four services have APIs available.</p>
<p>Is.gd&#8217;s API is straightforward and easy to use: call the page http://is.gd/api.php?longurl=http://really-long-url-goes-here.com , and is.gd will return the shortened url (or an error message) as the only text.  You&#8217;ll have to parse out the HTTP headers, of course, but that&#8217;s the easy part.</p>
<p>The W3C&#8217;s APIs are just as easy to use.  For HTML validation, fetch the page http://validator.w3.org/check?uri=http://webpage-to-check.com .  This is the same page you would navigate to in a browser, and returns a full-on HTML page with the results.  However, it also puts custom headers in the HTTP response.  To get the results, you have to parse out the values of the X-W3C-Validator-Status, X-W3C-Validator-Errors, and X-W3C-Validator-Warnings headers.  For CSS, the same info comes in the headers of a call to http://jigsaw.w3.org/css-validator/validator?uri=http://webpage-to-check.com .  In the case of the CSS validator, though, the X-W3C-Validator-Warnings header is not returned.</p>
<p>Google&#8217;s search API is a little more involved.  Not only because it uses JSON, but because <a href="http://googlesystem.blogspot.com/2006/11/secret-google-json-api.html">it&#8217;s a secret</a>.  Google&#8217;s <a href="http://code.google.com/apis/ajaxsearch/">AJAX Search</a> package is no secret of course, but the API calls that it makes are hidden away.  Fortunately, Google set up an experimental search site called <a href="http://www.searchmash.com/">SearchMash.com</a>, along with an API of its own.  Simply fetch the page http://www.searchmash.com/results/search+terms+go+here , and searchmash will return a JSON string which can then be parsed out to obtain the google search results, alternate spelling suggestions, estimated number of results, the works.  Even better, you can append &#8220;?n=X&#8221; to the end to limit the search to X results.  Using ?n=1 returns the top result only.</p>
<p>These APIs all save processing power that would otherwise be spent parsing out possibly badly-formed HTML from HTTP responses.</p>
<p><i>Aside: If you would like to check the bot out, it currently resides on #webdezvous and #ecode on the freenode network.  It goes by the name of &#8220;ecode&#8221;.</i></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=hMQUM"><img src="http://feeds.feedburner.com/~f/TechKnack?i=hMQUM" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=4dkMm"><img src="http://feeds.feedburner.com/~f/TechKnack?i=4dkMm" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=oNA6m"><img src="http://feeds.feedburner.com/~f/TechKnack?i=oNA6m" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=tAMkm"><img src="http://feeds.feedburner.com/~f/TechKnack?i=tAMkm" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/424936171" height="1" width="1"/>]]></content:encoded><description>I love APIs.  They make using web services so much easier.
I&amp;#8217;ve been developing an IRC bot as a side project, and three of its functions require the use of web services.  It can shorten urls using http://is.gd/, search google, and validate web pages using the W3C&amp;#8217;s HTML and CSS validators.  Fortunately, and [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/i-love-me-some-apis/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/i-love-me-some-apis/</feedburner:origLink></item><item><title>New Layout</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/414529536/</link><category>blogs</category><category>design</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Wed, 08 Oct 2008 01:04:40 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=180</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I know, I updated the site design <a href="http://techknack.net/time-for-a-redesign/">less than a year ago</a>.  I decided I could do better than a fixed-width design built for 800 by 600 resolution displays.</p>
<p>The new layout, also my first WordPress theme (yay), is a bit more flexible.  By default, it is full-browser-width, with reasonable space allotted to the right sidebar and the main content areas.  For modern browsers that support max-width CSS, the design will stop &#8220;growing&#8221; at 1,000 pixels, just short of full-width on a 1024&#215;768 resolution.  The content and sidebar columns have, in addition to their own (possibly redundant) max-widths, reasonable min-widths.  This is to prevent the content from being squashed beyond recognition should someone with an abnormally small browser come along.  In this case, the sidebar will drop below the content area when the point-of-no-more-shrinkage is reached.</p>
<p>There&#8217;s a whopping 6.3KB worth of images to load per page.  Unfortunately, they&#8217;re almost all translucent gradients, so IE6 users will receive *html hacks in the CSS to clear most of them, to avoid unsightly grey blocks that would otherwise obscure text.  The color scheme was heavily based on <i>Cool Grays Lavender</i> by julievonderropp on <a href="http://kuler.adobe.com/">Kuler</a>.</p>
<p>In addition to the right-hand sidebar, I put two separate &#8220;widget areas&#8221; in the footer, one above the other.  The bottom section is primarily for displaying the archives links &#8212; there are a lot of them (one for each month the site&#8217;s been up), and they don&#8217;t fit very nicely in vertical sidebars.  The top section is simply another sidebar, designed to fit up to three widgets.</p>
<p>A decent amount of work went into the comments section, along with some help and ideas from a comments template posted at <a href="http://www.christianmontoya.com/2006/10/15/full-featured-commentsphp-template-for-wordpress/">ChristianMontoya.com</a>.  Even/odd comments are striped for easy differentiation.  The post&#8217;s author&#8217;s name is retrieved via an extra database query, and comments made by that author are given an additional class name, used to attach another gradient image to the background.  Also, comments and pingbacks are listed separately, comments first, along with their respective counts.  This change is extended to the front and search pages, where another extra query is executed for each post to obtain the number of actual comments for that post.  A small amount of extra overhead, but it might be worth it for the extra accuracy.  Unfortunately, I haven&#8217;t yet thought of a succinct way of displaying the total number of responses (comments plus pingbacks).</p>
<p>Concerning the actual theme construction, at the outset, I really had no clue about making WordPress themes, other than that it involved PHP, HTML, CSS, and some MySQL.  The design itself started as an HTML file on my local harddrive, just an idea I started tweaking around.  Fortunately, in my research I ran across a <a href="http://www.wpdesigner.com/2007/02/19/so-you-want-to-create-wordpress-themes-huh/">full-fledged WordPress theme tutorial</a> on WPDesigner.com; it&#8217;s really a great tutorial for beginners to the whole WordPress skinning idea.  For what the tutorial lacked, the <a href="http://codex.wordpress.org/">WordPress Codex</a> was a great reference, along with a couple of google searches for some unclear topics.  I did have to consult <a href="http://php.net/">PHP.net</a> a couple of times, but that&#8217;s normal for any PHP project.  Overall, though, it wasn&#8217;t too difficult, just a matter of putting the right pieces in the right places <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>So, there you have it.  My first post in almost a month (sorry <img src='http://techknack.net/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> ) is a self-centered look-at-me post.  Criticisms, comments, and congratulations on the design are welcome.  As are lectures about what coding, design, or WordPress standards I may have broken <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .  In other words, I look forward to your feedback.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=rbtAM"><img src="http://feeds.feedburner.com/~f/TechKnack?i=rbtAM" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=X58am"><img src="http://feeds.feedburner.com/~f/TechKnack?i=X58am" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=RjAWm"><img src="http://feeds.feedburner.com/~f/TechKnack?i=RjAWm" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=0fpfm"><img src="http://feeds.feedburner.com/~f/TechKnack?i=0fpfm" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/414529536" height="1" width="1"/>]]></content:encoded><description>I know, I updated the site design less than a year ago.  I decided I could do better than a fixed-width design built for 800 by 600 resolution displays.
The new layout, also my first WordPress theme (yay), is a bit more flexible.  By default, it is full-browser-width, with reasonable space allotted to the [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/new-layout/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/new-layout/</feedburner:origLink></item><item><title>Torrent Management System with rTorrent and Bash</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/395003349/</link><category>linux</category><category>rtorrent</category><category>torrent</category><category>torrent management system</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Wed, 17 Sep 2008 03:36:16 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=176</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I do a fair amount of torrenting, mostly for TV shows.  A college student&#8217;s schedule doesn&#8217;t always allow for sitting in front of the TV on a certain night during a certain timeslot <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .  Nor does it allow for micromanaging my torrents &#8212; making sure I have enough, but not too many, torrents running at a given time; making sure torrents are properly seeded once downloaded; making sure the files get moved to the right place on my external HDD (so XBMC can pull them over the network).  Just too much to make sure it all happens.</p>
<p>There are a lot of <acronym title="Graphical User interface">GUI</acronym> clients that claim to do a lot of this stuff, but I prefer the KDE-native KTorrent as my desktop client.  For a management system, though, the GUI is almost irrelevant &#8212; a set-it-and-forget-it system needs to sit in the background until called to the foreground, and rTorrent does this very nicely.  Coupled with an always-on-and-connected home server, some bash scripting, and the linux &#8220;screen&#8221; program, rTorrent is fully capable of automating torrent downloads.</p>
<p>In researching others&#8217; systems, I came across &#8220;<a href="http://www.stabellini.net/rtorrent-howto.txt">rTorrent as a download manager</a>&#8220;, a text file detailing what the author expected from his management system, and how he went about obtaining the desired results.  I also came across an <a href="http://libtorrent.rakshasa.no/wiki/RTorrentUserGuide">rTorrent and libtorrent user guide</a> on the <a href="http://libtorrent.rakshasa.no/">official rTorrent site</a>.  Unfortunately, there is little up-to-date, <i>detailed</i> info on all the options offered for rTorrent customization.</p>
<p>According to the first resource, the (initial) main goals of the <acronym title="Torrent Management System">TMS</acronym> are:</p>
<ol>
<li>Limit the number of concurrent active torrents</li>
<li>Queue and start inactive torrents</li>
<li>Move downloaded files to another directory and remove the associated .torrent file</li>
<li>Manage bandwidth usage to keep the DSL connection useful</li>
</ol>
<p>These are all well and good, and relatively simple to achieve with little bash scripting, but rTorrent is capable of more.  When a torrent is finished, I want to move it to another directory before I continue seeding.  When it&#8217;s done seeding, I want to move it to another directory, so I can know, simply by looking at directory contents, which files are completely done.  And, of course, I want to move the downloaded files to the appropriate directories.  So the complete list of TMS requirements is:</p>
<ol>
<li>Limit the number of concurrent active torrents</li>
<li>Queue and start inactive torrents</li>
<li>Move downloaded files to a second directory and continue to seed the torrent to a specific ratio</li>
<li>Move seeded files to a third directory and remove associated .torrent files</li>
<li>Manage bandwidth usage to keep the DSL connection useful</li>
<li>Move files from the final directory to the appropriate external locations</li>
</ol>
<p>The first two can be accomplished with rTorrent&#8217;s directory watching and a simple bash script.  The next three can be implemented solely within rTorrent.  And the final point can be implemented with a more complicated bash script that takes advantage of community torrent naming conventions.</p>
<p><a href='http://techknack.net/wp-content/uploads/rtorrent_dirs.png'><img src="http://techknack.net/wp-content/uploads/rtorrent_dirs.png" alt="" title="rtorrent_dirs" width="278" height="261" class="alignright size-full wp-image-177" /></a> First, the directory structure.  Let us assume that the base directory for torrent activity is <i>/torrents</i>.  Limiting torrents is a simple matter of making sure that rTorrent&#8217;s watched directory never contains more than N .torrent files, where N is our maximum.  This gives us two directories: <i>/torrents/watch</i> and <i>/torrents/loading</i>.  The <i>watch</i> directory is the one rTorrent will watch for torrents, and the <i>loading</i> directory will be a holding cell for inactive torrents.  Points 3 and 4 give us three more directories, <i>/torrents/active</i>, <i>/torrents/seeding</i>, and <i>/torrents/finished</i>.  These are for active, downloaded (seeding), and completely finished torrents, respectively.  Another directory to use is <i>/torrents/session</i>, for use with rTorrent&#8217;s session feature.  Finally, I have a hidden directory <i>/torrents/.archive</i> where I send copies of activated .torrent files.</p>
<p>Once the directory structure is in place, rTorrent needs to be told how to manage things.  This is done in the .rtorrent.rc file, which, by default, is in the initiating user&#8217;s home directory.  Here is the section of my .rtorrent.rc file that handles torrent management (make sure each command is on one line):</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="co1">#************** Automagic Torrent Management &nbsp;****************#</span></p>
<p><span class="co1"># Maintain session info in /torrents/session</span><br />
session=/torrents/session</p>
<p><span class="co1"># Drop .torrent files in /torrents/loading (or /torrents, if starttor can handle &quot;misguided&quot; files)</span></p>
<p><span class="co1"># Use &quot;starttor&quot; script on cronjob to keep /torrents/watch occupied with three or fewer .torrents</span></p>
<p><span class="co1"># Load torrents in &quot;watch&quot; directory (check every 5sec)</span><br />
schedule = watch_directory,<span class="nu0">5</span>,<span class="nu0">5</span>,load_start=/torrents/watch/*.torrent</p>
<p><span class="co1"># Download torrents to &quot;active&quot; directory</span><br />
directory = /torrents/active</p>
<p><span class="co1"># Move to &quot;seeding&quot; directory and seed when downloaded</span><br />
on_finished = seed,<span class="st0">&quot;execute=mv,-u,$d.get_base_path=,/torrents/seeding/ ;d.set_directory=/torrents/seeding/&quot;</span></p>
<p><span class="co1"># Close when seed ratio reached (check every 60sec)</span><br />
schedule = ratio,<span class="nu0">60</span>,<span class="nu0">60</span>,<span class="st0">&quot;close_on_ratio=110&quot;</span></p>
<p><span class="co1"># Move to &quot;finished&quot; on close (when both the file is downloaded and the ratio is reached)</span><br />
on_close = move_fin,<span class="st0">&quot;execute=mv,-u,$d.get_base_path=,/torrents/finished/ ;d.set_directory=/torrents/finished/&quot;</span></p>
<p><span class="co1"># Delete .torrent files in /torrents/watch when torrent is closed</span><br />
on_close = remove_fin,<span class="st0">&quot;execute=rm,$d.get_tied_to_file=&quot;</span></p>
<p><span class="co1"># Torrent is downloaded, seeded, and done with. &nbsp;Use custom script to move to appropriate directory.</span></p>
<p><span class="co1">#*************************************************************#</span><br />
&nbsp;</div>
<p>All other &#8220;schedule&#8221; and &#8220;on_whatever&#8221; commands that were in the <a href="http://libtorrent.rakshasa.no/browser/trunk/rtorrent/doc/rtorrent.rc?rev=latest">example .rtorrent.rc file</a> are commented out.  This defines the session, watch, and active download directories; specifies that downloaded files should be moved to the seeding directory and seeded; specifies what ratio to stop a seeding torrent at; and specifies that completely finished torrents should be moved to the finished directory.  Getting finished files to be moved before seeding was fairly straightforward; in fact, there were already examples floating around the internet on how to do just that.  Getting the files moved when the torrent was finished was tricky.  All the examples I could find only showed &#8220;stop_on_ratio=&#8221; for the ratio schedule.  Unfortunately, this leaves the torrent sitting in rTorrent as &#8220;inactive&#8221;, and, were I to use the &#8220;on_stop&#8221; command, the torrent would be moved when Ctrl-D was pushed (which temporarily stops the torrent).  So I decided to try &#8220;close_on_ratio&#8221; instead, and that particular command happened to exist.  Thanks to this, the torrent is closed (and moved according to the following on_close command) when my 1.10 ratio is reached, freeing up space for the next torrent.  The only downside is if you are the original seed for a torrent (or are trying to seed a torrent beyond 1.10) and try to temporarily stop that torrent, it will be moved if the ratio is above 1.10.  Other commands in .rtorrent.rc (upload_rate and download_rate) allow bandwidth control.</p>
<p>Once rTorrent knows how to manage the torrents (and you can test this by starting rTorrent and dropping a torrent in the watch directory), you need to implement the &#8220;queuing system&#8221;.  Here is the &#8220;starttor&#8221; bash script that I use:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="co2">#!/bin/bash</span></p>
<p><span class="co2"># Root dir (for finding &quot;misguided&quot; torrents)</span><br />
root=<span class="st0">&quot;/torrents&quot;</span><br />
<span class="co2"># Loading dir (for inactives)</span><br />
loading=<span class="st0">&quot;/torrents/loading&quot;</span><br />
<span class="co2"># Watching dir (for actives)</span><br />
watch=<span class="st0">&quot;/torrents/watch&quot;</span><br />
<span class="co2"># Archive dir - leave blank (archive=&quot;&quot;) if you don&#8217;t use one</span><br />
archive=<span class="st0">&quot;/torrents/.archive&quot;</span><br />
<span class="co2"># max # torrents at once</span><br />
N=<span class="nu0">3</span></p>
<p><span class="co2"># max-1 for comparisons</span><br />
N=$<span class="br0">&#40;</span><span class="br0">&#40;</span><span class="re0">$N</span><span class="nu0">-1</span><span class="br0">&#41;</span><span class="br0">&#41;</span></p>
<p><span class="co2"># Move all &quot;misguided&quot; torrents (thrown into the root dir) to the &quot;loading&quot; dir</span><br />
misguided=`ls <span class="st0">&quot;$root&quot;</span> | grep .*\.torrent | replace <span class="st0">&quot; &quot;</span> <span class="st0">&quot;~&quot;</span>`<br />
<span class="kw1">for</span> torrent in <span class="re0">$misguided</span>; <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; torrent=`<a href="http://www.php.net/echo"><span class="kw3">echo</span></a> <span class="re0">$torrent</span> | replace <span class="st0">&quot;~&quot;</span> <span class="st0">&quot; &quot;</span>`<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2"># Make all .torrent files rw/r/r</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/chmod"><span class="kw3">chmod</span></a> <span class="nu0">644</span> <span class="st0">&quot;$root/$torrent&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; mv <span class="st0">&quot;$root/$torrent&quot;</span> <span class="st0">&quot;$loading/&quot;</span><br />
done</p>
<p><span class="co2"># If N or more active torrents, don&#8217;t do anything</span><br />
active=`ls <span class="st0">&quot;$watch&quot;</span> | grep .*\.torrent | grep <span class="st0">&quot;.*&quot;</span> -c`<br />
<span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re0">$active</span> -gt <span class="re0">$N</span> <span class="br0">&#93;</span>; then<br />
&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/exit"><span class="kw3">exit</span></a> <span class="nu0">0</span>;<br />
fi</p>
<p><span class="co2"># If no inactive torrents, do nothing</span><br />
inactive=`ls <span class="st0">&quot;$loading&quot;</span> | grep .*\.torrent | grep <span class="st0">&quot;.*&quot;</span> -c`<br />
<span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re0">$inactive</span> -eq <span class="nu0">0</span> <span class="br0">&#93;</span>; then<br />
&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/exit"><span class="kw3">exit</span></a> <span class="nu0">0</span>;<br />
fi</p>
<p><span class="co2"># While we have inactive torrents and less than 3 active, copy over inactives</span><br />
tostart=`ls <span class="st0">&quot;$loading&quot;</span> | grep .*\.torrent | replace <span class="st0">&quot; &quot;</span> <span class="st0">&quot;~&quot;</span>`<br />
<span class="kw1">for</span> torrent in <span class="re0">$tostart</span>; <span class="kw1">do</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; torrent=`<a href="http://www.php.net/echo"><span class="kw3">echo</span></a> <span class="st0">&quot;$loading/$torrent&quot;</span> | replace <span class="st0">&quot;~&quot;</span> <span class="st0">&quot; &quot;</span>`<br />
&nbsp; &nbsp; &nbsp; &nbsp; active=`ls <span class="st0">&quot;$watch&quot;</span> | grep .*\.torrent | grep <span class="st0">&quot;.*&quot;</span> -c`<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> <span class="re0">$active</span> -gt <span class="re0">$N</span> <span class="br0">&#93;</span>; then<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/exit"><span class="kw3">exit</span></a> <span class="nu0">0</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; fi<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#91;</span> -n <span class="re0">$archive</span> <span class="br0">&#93;</span>; then<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cp <span class="st0">&quot;$torrent&quot;</span> <span class="st0">&quot;$archive/&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; fi<br />
&nbsp; &nbsp; &nbsp; &nbsp; mv <span class="st0">&quot;$torrent&quot;</span> <span class="st0">&quot;$watch/&quot;</span><br />
done<br />
&nbsp;</div>
<p>This script moves any stray torrents from the root directory (the easiest place to drop them) to the loading directory; I refer to these as &#8220;misguided&#8221; torrents, since they are not in the main loading directory.  It then checks for the number of active torrents; if the number is greater than or equal to the max, the script exits, doing nothing.  If there&#8217;s room for more, it checks for any torrents in the loading directory.  If there are torrents there, it will move them, one at a time, into the watch directory, until the max is reached.  If the archive variable is set, it will also save a copy to the specified directory.  You can use this script with cron; I use this line in root&#8217;s crontab to run it every ten minutes:</p>
<div class="dean_ch" style="white-space: wrap;">
*/10 * * * * /home/andrew/bin/starttor<br />
&nbsp;</div>
<p>Moving the finished files to their appropriate places is more complicated.  Basically, you have to write a script that takes into account the common layout of filenames as well as your specific organizational directory tree.  For example, I like to download TV shows.  My directory structure is TV &gt; Show Name &gt; Season #.  Most TV torrent files follow the conventional naming scheme of TV.Show.Name.S01E03.Episode.Name.Release.Group.avi.  My script recursively goes through all the .avi files in the <i>finished</i> directory, for each file pulling out everything up to the season number (in this case, &#8220;TV.Show.Name.S01&#8243;).  It then splits it at the S##, extracting the show name and season number.  It then replaces all periods, underscores, and dashes in the show name with spaces, and reduces multiple consecutive spaces to one space each.  Once the show name is prepped, the script checks if the directory &#8220;TV Show Name&#8221; exists in the TV directory.  If it does, it checks for &#8220;Season #&#8221; (if that doesn&#8217;t exist, it checks for &#8220;Season#&#8221;, without the space, just in case).  If that exists, the file is moved to that Season directory.  If not, the file is left alone, for me to move manually if I need to.  This leaves other, non-TV torrents in the finished directory for me to work with later.  I also have this script on cron, but it is set to run every hour.</p>
<p>RTorrent makes building a TMS easy, as long as you know what settings to use.  To really automate the whole thing, I could add a script which daily (or weekly) checks various RSS feeds for new torrents of shows I like, but I haven&#8217;t gotten that far yet &#8212; I&#8217;m still manually hunting down my shows.  Since copying and pasting the file contents I&#8217;ve given is not ideal, copies of the files I use are <a href="/examples/rtorrent/">available for download</a>, just be sure to rename and chmod them as appropriate.</p>
<p>If you feel I left out any details, feel free to ask questions in the comments, and I&#8217;ll answer to the best of my ability.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=KYlrL"><img src="http://feeds.feedburner.com/~f/TechKnack?i=KYlrL" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=i1FCl"><img src="http://feeds.feedburner.com/~f/TechKnack?i=i1FCl" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=5cJ7l"><img src="http://feeds.feedburner.com/~f/TechKnack?i=5cJ7l" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=jP1Ml"><img src="http://feeds.feedburner.com/~f/TechKnack?i=jP1Ml" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/395003349" height="1" width="1"/>]]></content:encoded><description>I do a fair amount of torrenting, mostly for TV shows.  A college student&amp;#8217;s schedule doesn&amp;#8217;t always allow for sitting in front of the TV on a certain night during a certain timeslot  .  Nor does it allow for micromanaging my torrents &amp;#8212; making sure I have enough, but not too many, [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/torrent-management-system-with-rtorrent-and-bash/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/torrent-management-system-with-rtorrent-and-bash/</feedburner:origLink></item><item><title>Down and Dirty Un-spam WordPress Comments</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/386001994/</link><category>blogs</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Sun, 07 Sep 2008 14:21:38 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=175</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>One thing I&#8217;ve learned in running my own blog is that spam-bots really, <i>really</i> like open forms.  I&#8217;m sure experienced bloggers will smile (and say &#8220;that&#8217;s all?&#8221;) when I say that I get probably no less than 5 spam comments per day.  With WordPress, it&#8217;s simple to be rid of spam &#8212; just click the comment&#8217;s &#8220;spam&#8221; link in the moderation section.  Unfortunately, with so many spam and so few real comments, the spam link becomes second nature to click.  And today I accidentally spammed a legit comment.  Oops.</p>
<p>There doesn&#8217;t seem to be much info on Google regarding un-spamming comments, so I thought I&#8217;d look into the database to see what I could do.  Comments are held in the MySQL table &#8220;[wp-prefix]comments&#8221; (the default is &#8220;wp_comments&#8221;).  One of the columns in that table is &#8220;comment_approved&#8221;.  Unmoderated comments have a 0 in this column.  Approved comments have a 1.  And marked-as-spam comments have &#8220;spam&#8221;.  To un-spam a comment, use your MySQL client (be it phpMyAdmin or the MySQL command line program) to edit the comment_approved field back to 0 (or 1) in the appropriate comment&#8217;s row.  Unfortunately, &#8220;delete&#8221; really means &#8220;delete&#8221; &#8212; a deleted comment is removed from the database altogether, so you&#8217;ll have to scrounge legit comments back together from the emails that WP sends.</p>
<p>I really don&#8217;t see why the WP devs didn&#8217;t build an un-spam function into the WP admin.  It seems like an obvious addition, given the structure of the database.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=VLutL"><img src="http://feeds.feedburner.com/~f/TechKnack?i=VLutL" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=aUvml"><img src="http://feeds.feedburner.com/~f/TechKnack?i=aUvml" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=urNol"><img src="http://feeds.feedburner.com/~f/TechKnack?i=urNol" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=pPqxl"><img src="http://feeds.feedburner.com/~f/TechKnack?i=pPqxl" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/386001994" height="1" width="1"/>]]></content:encoded><description>One thing I&amp;#8217;ve learned in running my own blog is that spam-bots really, really like open forms.  I&amp;#8217;m sure experienced bloggers will smile (and say &amp;#8220;that&amp;#8217;s all?&amp;#8221;) when I say that I get probably no less than 5 spam comments per day.  With WordPress, it&amp;#8217;s simple to be rid of spam &amp;#8212; just [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/down-and-dirty-un-spam-wordpress-comments/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/down-and-dirty-un-spam-wordpress-comments/</feedburner:origLink></item><item><title>Google Chrome Not Yet For Linux</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/381952724/</link><category>web</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Tue, 02 Sep 2008 22:23:37 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=174</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Google recently announced and released their browser, <a href="http://google.com/chrome">Google Chrome</a>.  And, as should be expected, <i>everyone</i> is talking about it (<a href="http://www.quirksmode.org/blog/archives/2008/09/google_chrome.html">QuirksMode</a>, <a href="http://www.dave-woods.co.uk/index.php/google-chrome-web-browser/">Dave Woods</a>, and <a href="http://lifehacker.com/5044484/google-chrome-first-look">LifeHacker</a>, amongst <a href="http://digg.com/search?section=all&#038;s=google+chrome">others</a>).  Unfortunately, it&#8217;s <a href="http://www.google.com/chrome/intl/en/linux.html">not yet available for linux</a>.  There&#8217;s promise that the developers are working on a linux port, and there&#8217;s a nice little textbox you can drop your email into for updates on the status.</p>
<p>The features touted on Google&#8217;s promotional website include searchable history / smart address bar, a &#8220;portal&#8221; page as the default &#8220;new tab&#8221; view, individual tab processes, dynamic tabs (ala <a href="http://www.youtube.com/watch?v=lWyT2MfMUKo&#038;fmt=18">Safari 3.1</a>), and &#8220;incognito mode&#8221; (is that the <a href="http://www.redhat.com/">Red Hat</a> mascot I see in the corner?).  Underneath the hood, I&#8217;ve heard it uses a combination of opensource code from Safari&#8217;s Webkit and Mozilla&#8217;s Gecko engines, so it should be interesting from a web development perspective.</p>
<p>All in all, from what I&#8217;ve read others saying, it looks like Google took some opensource browser code, implemented a few nifty FireFox addons as default features, and slapped a shiny Google interface on it.  My test run for Chrome won&#8217;t happen until it&#8217;s available for linux, but if you&#8217;re itching to see it, check out <a href="http://lifehacker.com/5044484/google-chrome-first-look">LifeHacker&#8217;s first look at Chrome</a> (using the Windows release).</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=izAwyL"><img src="http://feeds.feedburner.com/~f/TechKnack?i=izAwyL" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=5TNerl"><img src="http://feeds.feedburner.com/~f/TechKnack?i=5TNerl" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=1Utl8l"><img src="http://feeds.feedburner.com/~f/TechKnack?i=1Utl8l" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=DGTadl"><img src="http://feeds.feedburner.com/~f/TechKnack?i=DGTadl" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/381952724" height="1" width="1"/>]]></content:encoded><description>Google recently announced and released their browser, Google Chrome.  And, as should be expected, everyone is talking about it (QuirksMode, Dave Woods, and LifeHacker, amongst others).  Unfortunately, it&amp;#8217;s not yet available for linux.  There&amp;#8217;s promise that the developers are working on a linux port, and there&amp;#8217;s a nice little textbox you can [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/google-chrome-not-yet-for-linux/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/google-chrome-not-yet-for-linux/</feedburner:origLink></item><item><title>Message to the Readers: School’s Started</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/381609442/</link><category>blogs</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Tue, 02 Sep 2008 13:54:56 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=172</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>As of approximately two weeks ago, the Fall &#8216;08 school semester has officially started.  This semester, I&#8217;m taking 18 credit hours over 6 classes.  To give you an idea of how busy that is, this school considers 12 credit hours to be &#8220;full time&#8221;, and I normally take around 15 credit hours.  As such, my posting schedule for the next few months will be sparse at best.  Most likely, I will only post shorter articles as I come across ideas.</p>
<p>If any of you have a question you think I could answer (that other readers would be interested in, of course <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ), by all means, <a href="http://techknack.net/about/">shoot me an email</a>.  Tips on possible subjects would be appreciated, too.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=wI8apL"><img src="http://feeds.feedburner.com/~f/TechKnack?i=wI8apL" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=CYVRll"><img src="http://feeds.feedburner.com/~f/TechKnack?i=CYVRll" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=8rgAEl"><img src="http://feeds.feedburner.com/~f/TechKnack?i=8rgAEl" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=5nNRbl"><img src="http://feeds.feedburner.com/~f/TechKnack?i=5nNRbl" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/381609442" height="1" width="1"/>]]></content:encoded><description>As of approximately two weeks ago, the Fall &amp;#8216;08 school semester has officially started.  This semester, I&amp;#8217;m taking 18 credit hours over 6 classes.  To give you an idea of how busy that is, this school considers 12 credit hours to be &amp;#8220;full time&amp;#8221;, and I normally take around 15 credit hours.  [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/message-to-the-readers-schools-started/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/message-to-the-readers-schools-started/</feedburner:origLink></item><item><title>Stop the OpenDNS Address Bar Hijack</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/373671578/</link><category>firefox</category><category>web</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Sun, 24 Aug 2008 15:15:45 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=171</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I started using <a href="http://opendns.com">OpenDNS</a> a few weeks ago.  One thing I immediately noticed was that OpenDNS was hijacking my address-bar-initiated google searches.  You know that feature in FireFox that, if the words entered in the address bar don&#8217;t match a website, it takes you to the google search for those keywords?  Seems OpenDNS redirects that particular page of google to their own custom search.  Yuck.  Fortunately, there&#8217;s a way to fix it &#8212; by changing the default url.</p>
<p>I found <a href="http://www.labnol.org/software/browsers/prevent-opendns-google-redirects-firefox-address-bar-ie/2662/">this trick</a> over at <a href="http://labnol.org">labnol.org</a>.  Simply change the about:config key <i>keyword.url</i> to <i>http://www.google.com/search?q=</i> and make sure <i>keyword.enabled</i> is set to <i>true</i>.</p>
<p>The default value of <i>keyword.url</i> is <i>http://www.google.com/search?ie=UTF-8&#038;oe=UTF-8&#038;sourceid=navclient&#038;gfns=1&#038;q=</i>.  Most likely, OpenDNS has this particular string programmed into their system, to catch navbar searches from FireFox (and IE) with default settings.  Change the setting, and you can skirt the redirect <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=3MnQVK"><img src="http://feeds.feedburner.com/~f/TechKnack?i=3MnQVK" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=Qb0nyk"><img src="http://feeds.feedburner.com/~f/TechKnack?i=Qb0nyk" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=BPD7tk"><img src="http://feeds.feedburner.com/~f/TechKnack?i=BPD7tk" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=DYAihk"><img src="http://feeds.feedburner.com/~f/TechKnack?i=DYAihk" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/373671578" height="1" width="1"/>]]></content:encoded><description>I started using OpenDNS a few weeks ago.  One thing I immediately noticed was that OpenDNS was hijacking my address-bar-initiated google searches.  You know that feature in FireFox that, if the words entered in the address bar don&amp;#8217;t match a website, it takes you to the google search for those keywords?  Seems [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/stop-the-opendns-address-bar-hijack/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/stop-the-opendns-address-bar-hijack/</feedburner:origLink></item><item><title>Auto-Update Sun Map Wallpaper in KDE4</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/368344451/</link><category>desktop</category><category>linux</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Mon, 18 Aug 2008 14:04:40 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=169</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><a href='http://techknack.net/wp-content/uploads/worldmap.png'><img src="http://techknack.net/wp-content/uploads/worldmap.png" alt="" title="" width="300" height="166" class="size-full wp-image-170" /></a>Yesterday, LifeHacker posted an article on <a href="http://lifehacker.com/400505/rotate-desktop-backgrounds-in-ubuntu">setting up an auto-rotate wallpaper in Gnome</a>.  Now, KDE4 comes with this functionality out of the box - just point the desktop configuration to an image folder, and it will rotate the images in user-specified intervals (with a minimum of one hour).  However, <a href="http://lifehacker.com/400505/rotate-desktop-backgrounds-in-ubuntu#c7274862">one comment by joelena</a> caught my interest.  Joelena mentioned having found a way to display a &#8220;<a href="http://www.opentopia.com/images/cams/world_sunlight_map_rectangular.jpg">sun map</a>&#8221; as a wallpaper using a scheduled wget command.</p>
<p>Apparently, Gnome will automatically detect changes in the wallpaper image file, and update the desktop accordingly.  No joy with KDE4 - apparently the devs assume we&#8217;ll never change the image <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .  Fortunately, there&#8217;s a way to &#8220;trick&#8221; the KDE4 wallpaper rotator into updating the wallpaper with the latest sun map image.</p>
<p>First, create a folder to contain the sun map images, I created /usr/share/wallpapers/worldmap .  Next, setup a cron job to automatically pull the updated image.  Run &#8220;sudo crontab -e&#8221; in a terminal.  This will start you editing root&#8217;s cron file.  Paste the following as it&#8217;s own line in the cron file (Shift+Insert usually acts as paste for CLI editors):</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="nu0">10</span> * * * * <span class="kw2">wget</span> http://www.opentopia.com/images/cams/world_sunlight_map_rectangular.jpg -O- | convert - -scale 1440&#215;900 /usr/share/wallpapers/worldmap/worldmap.png &amp;&amp; <span class="kw2">cp</span> /usr/share/wallpapers/worldmap/worldmap.png /usr/share/wallpapers/worldmap/worldmap1.png<br />
&nbsp;</div>
<p>Be sure that it&#8217;s all on one line, and be sure to edit the -scale option for the convert command to match your own screen resolution, and the directory paths to match your directory.  Save and exit.  This command will pull the sun map image from the server that maintains it, and run it through the convert command to rescale it and convert it to a PNG image.  Then, it will copy the output file to a file in the same directory with a different name.  The cron options (the &#8220;10 * * * *&#8221;) specify that the command will be run ten minutes after every hour (apparently the map image is updated shortly after every hour).</p>
<p>Alternatively, you can copy and paste the following into a plain text file, add the proper permissions to make the file executable (chmod +x file), and use whatever scheduling software you like to run the script shortly after every hour:</p>
<div class="dean_ch" style="white-space: wrap;">
<span class="re3">#!/bin/bash</span></p>
<p><span class="re2">dir=</span><span class="st0">&quot;/usr/share/wallpapers/worldmap&quot;</span><br />
<span class="re2">file=</span><span class="st0">&quot;worldmap&quot;</span><br />
<span class="re2">file2=</span><span class="st0">&quot;worldmap1&quot;</span><br />
<span class="re2">format=</span><span class="st0">&quot;png&quot;</span><br />
<span class="re2">scale=</span><span class="st0">&quot;1440&#215;900&quot;</span></p>
<p><span class="kw2">wget</span> http://www.opentopia.com/images/cams/world_sunlight_map_rectangular.jpg -O- | convert - -scale <span class="re0">$<span class="br0">&#123;</span>scale<span class="br0">&#125;</span></span> <span class="re0">$<span class="br0">&#123;</span><span class="kw2">dir</span><span class="br0">&#125;</span></span>/<span class="re0">$<span class="br0">&#123;</span><span class="kw2">file</span><span class="br0">&#125;</span></span>.<span class="re0">$<span class="br0">&#123;</span>format<span class="br0">&#125;</span></span><br />
<span class="kw2">cp</span> <span class="re0">$<span class="br0">&#123;</span><span class="kw2">dir</span><span class="br0">&#125;</span></span>/<span class="re0">$<span class="br0">&#123;</span><span class="kw2">file</span><span class="br0">&#125;</span></span>.<span class="re0">$<span class="br0">&#123;</span>format<span class="br0">&#125;</span></span> <span class="re0">$<span class="br0">&#123;</span><span class="kw2">dir</span><span class="br0">&#125;</span></span>/<span class="re0">$<span class="br0">&#123;</span>file2<span class="br0">&#125;</span></span>.<span class="re0">$<span class="br0">&#123;</span>format<span class="br0">&#125;</span></span><br />
&nbsp;</div>
<p>This script does the same thing, but in a more easily customizable form.</p>
<p>Once you have the script setup, run it once to get the initial files, and point KDE4&#8217;s wallpaper slideshow to the directory containing the two files.  It will loop through the two files (both of which are updated every hour), constantly updating your wallpaper with the changes.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=M5gZ5K"><img src="http://feeds.feedburner.com/~f/TechKnack?i=M5gZ5K" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=QVxQgk"><img src="http://feeds.feedburner.com/~f/TechKnack?i=QVxQgk" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=5E2Sek"><img src="http://feeds.feedburner.com/~f/TechKnack?i=5E2Sek" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=hxvq9k"><img src="http://feeds.feedburner.com/~f/TechKnack?i=hxvq9k" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/368344451" height="1" width="1"/>]]></content:encoded><description>Yesterday, LifeHacker posted an article on setting up an auto-rotate wallpaper in Gnome.  Now, KDE4 comes with this functionality out of the box - just point the desktop configuration to an image folder, and it will rotate the images in user-specified intervals (with a minimum of one hour).  However, one comment by joelena [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/auto-update-sun-map-wallpaper-in-kde4/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/auto-update-sun-map-wallpaper-in-kde4/</feedburner:origLink></item><item><title>Hosting a Website: The Email</title><link>http://feeds.feedburner.com/~r/TechKnack/~3/365750086/</link><category>thunderbird</category><category>web</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Fri, 15 Aug 2008 10:16:08 -0500</pubDate><guid isPermaLink="false">http://techknack.net/?p=168</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Hosting a website on your own server gives you ultimate control over all the details of how that site works.  Running your domain&#8217;s mail on your own server gives you the same amount of control.  Unfortunately, setting up postfix (Ubuntu server edition&#8217;s default mail server) is not nearly as simple as setting up Apache.  Ultimately, I decided to go with <a href="http://www.google.com/a/help/intl/en/index.html">Google Apps for Your Domain</a> to handle my email.</p>
<p>In addition to gmail-style email hosting for your domain (and ThunderBird and Outlook hookup instructions to boot), GAppsFYD, as <a href="http ://lifehacker.com/software/ask-lifehacker/what-does-google-apps-for-your-domain-actually-do-330318.php">LifeHacker</a> calls it, sports Docs, Chat, and Calendar, all private to specified users of your domain.  It also includes Web Pages, giving you space to host static web pages (but you have <a href="http://techknack.net/hosting-a-website-the-server/">your own server</a> for that &#8212; right?); Sites, a collaborative intranet-style hosting space; and a Start Page, a central page from which all domain users can access the activated services.  Best of all, you can activate/deactivate whichever services you choose, allowing only what you need.</p>
<p>Each domain you set up with Google Apps can accomodate up to 100 users under the free version.  If the email service is activated, each user gets their own inbox with up to 6GB of storage.  You can also setup multiple addresses per user (with one shared inbox amongst them), and a &#8220;catchall&#8221; email address, where mail sent to a nonexistent user will end up.  And, of course, you get all the standard <a href="http://lifehacker.com/software/gmail/">advantages of gmail</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~f/TechKnack?a=7IcAJK"><img src="http://feeds.feedburner.com/~f/TechKnack?i=7IcAJK" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=4mkQXk"><img src="http://feeds.feedburner.com/~f/TechKnack?i=4mkQXk" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=9rmqPk"><img src="http://feeds.feedburner.com/~f/TechKnack?i=9rmqPk" border="0"></img></a> <a href="http://feeds.feedburner.com/~f/TechKnack?a=Rt4COk"><img src="http://feeds.feedburner.com/~f/TechKnack?i=Rt4COk" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/365750086" height="1" width="1"/>]]></content:encoded><description>Hosting a website on your own server gives you ultimate control over all the details of how that site works.  Running your domain&amp;#8217;s mail on your own server gives you the same amount of control.  Unfortunately, setting up postfix (Ubuntu server edition&amp;#8217;s default mail server) is not nearly as simple as setting up [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/hosting-a-website-the-email/feed/</wfw:commentRss><feedburner:origLink>http://techknack.net/hosting-a-website-the-email/</feedburner:origLink></item></channel></rss>
