<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2titles.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemtitles.css"?><rss xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>TechKnack</title><link>http://techknack.net</link><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/TechKnack" /><description>The rantings of a techie</description><language>en</language><lastBuildDate>Thu, 24 Dec 2009 11:53:49 PST</lastBuildDate><generator>http://wordpress.org/?v=2.8.4</generator><sy:updatePeriod xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">hourly</sy:updatePeriod><sy:updateFrequency xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">1</sy:updateFrequency><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/TechKnack" /><feedburner:info uri="techknack" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>TechKnack</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.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>Python: urllib2 handlers</title><link>http://feedproxy.google.com/~r/TechKnack/~3/X8VMNilB2TY/</link><category>python</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Tue, 03 Nov 2009 12:23:57 PST</pubDate><guid isPermaLink="false">http://techknack.net/?p=381</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Python is a great language, with a good community (see Freenode irc channel #python) &#8212; but its modules can have lousy documentation at times.</p>
<p>One place where I find the documentation lacking is in the description of <a href="http://docs.python.org/library/urllib2.html">urllib2</a>&#8217;s <a href="http://docs.python.org/library/urllib2.html#basehandler-objects">BaseHandler</a> class.  Subclasses of this class can be passed to urllib2&#8217;s build_opener function to add functionality to your url-opening activity.  Some examples of useful modifications are modifying HTTP requests (and responses) or creating support for an unsupported network protocol.  Unfortunately, while the methods of interest are described, examples are sparse.  Here I&#8217;ll give a couple of examples of handlers that I&#8217;ve come up with.  They may not be &#8220;pythonic,&#8221; and I&#8217;d be grateful for pythonic suggestions, but they get their jobs done and demonstrate some of the features of BaseHandler.</p>
<p>The first example is fairly simple: a User-Agent string injector.</p>
<pre class="brush: python;">
import urllib2

class UserAgentProcessor(urllib2.BaseHandler):
    &quot;&quot;&quot;A handler to add a custom UA string to urllib2 requests
    &quot;&quot;&quot;
    def __init__(self, uastring):
        self.handler_order = 100
        self.ua = uastring

    def http_request(self, request):
        request.add_header(&quot;User-Agent&quot;, self.ua)
        return request
    https_request = http_request
</pre>
<p>The __init__ method takes a string to be used as the user agent string for HTTP requests.  Init also sets the handler_order attribute to 100; the default for BaseHandler subclasses is 500, but we have to set a smaller number, otherwise our UA will be overwritten with the urllib2 default UA.  I&#8217;m not entirely sure why (the docs barely mention this attribute), but my experimentation as shown that it&#8217;s necessary.</p>
<p>The http_request (and https_request) methods are used to pre-process HTTP (and HTTPS) requests before the requests are actually made. The &#8220;request&#8221; parameter is a urllib2.Request object, which contains a wealth of editable information, including HTTP headers.  Here I&#8217;ve simply aliased https_request to http_request, since https requests don&#8217;t need special handling to add a UA string.</p>
<p>To use this handler, we need to build an opener with it:</p>
<pre class="brush: python;">
opener = urllib2.build_opener(UserAgentProcessor(&quot;My Useragent&quot;))
</pre>
<p>Then we can either &#8220;install&#8221; our new opener or use it straight up:</p>
<pre class="brush: python;">
# install and use the opener
urllib2.install_opener(opener)
urllib2.urlopen(&quot;http://whatismyuseragent.com/&quot;)

# Just use it
opener.open(&quot;http://whatismyuseragent.com/&quot;)
</pre>
<p>Another, more complex example, is to add gzip compression to urllib2.</p>
<pre class="brush: python;">
import urllib2
from gzip import GzipFile
from StringIO import StringIO

class GZipProcessor(urllib2.BaseHandler):
    &quot;&quot;&quot;A handler to add gzip capabilities to urllib2 requests
    &quot;&quot;&quot;
    def http_request(self, req):
        req.add_header(&quot;Accept-Encoding&quot;, &quot;gzip&quot;)
        return req
    https_request = http_request

    def http_response(self, req, resp):
        if resp.headers.get(&quot;content-encoding&quot;) == &quot;gzip&quot;:
            gz = GzipFile(
                        fileobj=StringIO(resp.read()),
                        mode=&quot;r&quot;
                      )
#            resp.read = gz.read
#            resp.readlines = gz.readlines
#            resp.readline = gz.readline
#            resp.next = gz.next
            old_resp = resp
            resp = urllib2.addinfourl(gz, old_resp.headers, old_resp.url, old_resp.code)
            resp.msg = old_resp.msg
        return resp
    https_response = http_response
</pre>
<p>You can see that the GZipProcessor class uses the http_request method, like UserAgentProcessor, to add an HTTP header to the request.  However, GZProcessor also uses the http_response method to pre-process the response before it is returned to whatever called urlopen().</p>
<p>In http_response, the &#8220;req&#8221; and &#8220;resp&#8221; parameters are urllib2.Request and urllib2.Response objects, respectively.  Since the Response contains the page contents, that&#8217;s what we&#8217;re interested in. If the server responded with gzipped content, it should have set the &#8220;Content-Encoding&#8221; response HTTP header to &#8220;gzip&#8221;.  If we have received gzipped content, we need to make it so that read attempts on the Response object return valid strings of text, not chunks of gzip data.</p>
<p>The method I&#8217;ve used here is somewhat convoluted and certainly not &#8220;pythonic&#8221;, but it gets the job done.  First, the gzipped content is read from the response and stuck into a StringIO object, which offers a file-like interface to string objects.  This StringIO object is then passed to GzipFile&#8217;s fileobj argument.  <del datetime="2009-11-28T07:33:48+00:00">Now, since the Response object contains info other than the page contents, we can&#8217;t simply replace it; instead, we set the read, readlines, readline, and next methods of the response to the corresponding methods of the GzipFile object.</del>  <ins datetime="2009-11-28T07:33:48+00:00">Next, we replace the incoming Response object with a new one, this one with its fp (file pointer, originally the HTTP connection socket) attribute pointing to our GZipFile object.  By instantiating a new addinfourl object, we don&#8217;t have to worry about missing a relevant read method; the constructor will take care of that for us.</ins>  Now, when we request a page and get gzipped contents, we can read(), readline(), etc, seamlessly, as if there were no gzipping involved.  The downside is that it will take slightly longer to receive our response because the GZProcessor has to read the entire contents of the page before decoding it.</p>
<p>Now, granted, there are other ways to do these things.  A UA string can be inserted into a manually-created Request object, which can then be passed to a raw OpenerDirector (created with urllib2.build_opener()) just fine.  Same with gzip acceptance, you&#8217;ll just have to remember to un-zip the contents you get back.  But the nice thing about handlers is that you can build your opener once with your custom handlers and not have to worry about the results anywhere else in your code.  Also, if you install your opener with urllib2.install_opener(), you can simply use urllib2.urlopen() for every page you need to open, and your handlers will automatically be called as needed.  Just plug-and-chug.</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=381&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=X8VMNilB2TY:LUna-kuLtVw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=X8VMNilB2TY:LUna-kuLtVw:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=X8VMNilB2TY:LUna-kuLtVw:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=X8VMNilB2TY:LUna-kuLtVw:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=X8VMNilB2TY:LUna-kuLtVw:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=X8VMNilB2TY:LUna-kuLtVw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=X8VMNilB2TY:LUna-kuLtVw:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/X8VMNilB2TY" height="1" width="1"/>]]></content:encoded><description>Python is a great language, with a good community (see Freenode irc channel #python) &amp;#8212; but its modules can have lousy documentation at times.
One place where I find the documentation lacking is in the description of urllib2&amp;#8217;s BaseHandler class.  Subclasses of this class can be passed to urllib2&amp;#8217;s build_opener function to add functionality to [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/python-urllib2-handlers/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">0</slash:comments><feedburner:origLink>http://techknack.net/python-urllib2-handlers/</feedburner:origLink></item><item><title>SVN setup</title><link>http://feedproxy.google.com/~r/TechKnack/~3/M_pAPgtzRA4/</link><category>linux</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Sat, 29 Aug 2009 18:32:24 PDT</pubDate><guid isPermaLink="false">http://techknack.net/?p=376</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Recently I set up an SVN repository in the hopes that maybe I can show off some of my programming work to potential clients (or maybe just other people who are interested).  I ran into sufficient trouble during setup that I thought it would be a good idea to give a run-down of my setup for others.</p>
<p>My setup runs on Apache/Linux, on a subdomain of one of my sites.  I have a root SVN directory that can hold multiple repositories.  While the repos are accessible via SVN, you can also browse them through a web interface (though it&#8217;s fairly simple, basically just lists of &#8220;directory&#8221; contents).  The repos give anonymous access for checkouts and other &#8220;read&#8221; functions, but are login-protected for commits and other &#8220;write&#8221; functions.</p>
<p>The guides I came across for setting up SVN mostly want you to put your settings in the file /etc/apache2/mods-available/dav_svn.conf , but I found that it works just fine if you set it up like a regular site/subdomain.  That is, as a VirtualHost entry in a file in /etc/apache2/sites-available.  So here is my VHost entry for SVN:</p>
<pre class="brush: xml;">
&lt;VirtualHost *&gt;
        ServerName svn.eternicode.com
        &lt;Location /&gt;
                Order allow,deny
                Allow from all
                DAV svn
                SVNParentPath /var/www/svn
                SVNListParentPath on
                AuthType Basic
                AuthName &quot;Eternicode SVN Repo&quot;
                AuthUserFile /etc/apache2/mods-available/dav_svn.passwd
                &lt;LimitExcept GET PROPFIND OPTIONS REPORT&gt;
                        Require valid-user
                &lt;/LimitExcept&gt;
        &lt;/Location&gt;
        ErrorLog /var/log/apache2/error_svn.log
        CustomLog /var/log/apache2/access_svn.log combined
&lt;/VirtualHost&gt;
</pre>
<p>Most of the special, SVN-specific stuff comes inside the &lt;Location&gt; tags; the ServerName, ErrorLog, and CustomLog are standard Apache directives.</p>
<p>SVNParentPath is the &#8220;root&#8221; repo directory; your various repos will be below this directory.  If you only want to host a single repo here, change the directive to just &#8220;SVNPath&#8221;.  The SVNListParentPath allows web access to a listing of all available repos; if you use SVNPath, remove SVNListParentPath.  If SVNListParentPath is off or omitted, users will get a 403 Forbidden error when browsing the parent directory.</p>
<p>AuthType, AuthName, and AuthUserFile define the login settings; the AuthUserFile is created with the htpasswd command.</p>
<p>The LimitExcept section is where we differentiate between anonymous reads and protected writes.  For everything other than GET, PROPFIND, OPTIONS, and REPORT methods, we want to Require a valid-user; if someone wants to commit, they have to authenticate according to the AuthUserFile defined above.</p>
<p>One issue I ran into was that, after everything was seemingly set up properly, any svn commands would result in a &#8220;Moved Permanently&#8221; error.  It turned out that I had defined the DocumentRoot to also be /var/www/svn; this would conflict with the svn settings, confuse apache, and result in the Moved error.  Don&#8217;t include a DocumentRoot; the svn setup will take care of everything once you give it an SVNParentPath.</p>
<p>Credits: Sematopia.com has a <a href="http://www.sematopia.com/?p=66">nice little guide</a> that takes you through steps outside of the apache configuration (though their 4-line apache config is scarily sparse).  Sellersrank.com has a guide for <a href="http://www.sellersrank.com/ubuntu/setup-apache-subversion-ssl-https-with-virtual-hosts-on-ubuntu/">setting up Apache-run SVN over https</a>; though I&#8217;m not using https, this guide helped me get my settings right.  </p>
<p>A few notes: Don&#8217;t use a DocumentRoot, only use the SVNParentPath.  Make sure the parent path is owned by user and group www-data (or whatever user your apache instance uses).  You can ensure this by:</p>
<pre class="brush: bash;">
$ sudo chown -R www-data.www-data /var/www/svn
</pre>
<p>While it would be extremely nice to be able to create new repositories with a simple &#8220;svn import&#8221;, it seems svn&#8217;s capabilities are restricted in that area.  It wouldn&#8217;t be too difficult to set up a cron job to monitor the SVN logs and create new repos as required, but that&#8217;d be hacky and inadequate. To create a new repository, you first have to run</p>
<pre class="brush: bash;">
$ sudo svnadmin create /var/www/svn/new_repo
</pre>
<p>on the server.  However, in order to not have issues with the fact that the web server owns all the svn files, I&#8217;d recommend creating new repos as the www-data user:</p>
<pre class="brush: bash;">
$ sudo su www-data -c &quot;svnadmin create /var/ww/svn/new_repo&quot;
</pre>
<p>Once the initial repo structure is created, you can then use &#8220;svn import&#8221; on a remote machine to import the first revision.</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=376&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=M_pAPgtzRA4:si0EXziUgW4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=M_pAPgtzRA4:si0EXziUgW4:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=M_pAPgtzRA4:si0EXziUgW4:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=M_pAPgtzRA4:si0EXziUgW4:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=M_pAPgtzRA4:si0EXziUgW4:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=M_pAPgtzRA4:si0EXziUgW4:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=M_pAPgtzRA4:si0EXziUgW4:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/M_pAPgtzRA4" height="1" width="1"/>]]></content:encoded><description>Recently I set up an SVN repository in the hopes that maybe I can show off some of my programming work to potential clients (or maybe just other people who are interested).  I ran into sufficient trouble during setup that I thought it would be a good idea to give a run-down of my [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/svn-setup/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">0</slash:comments><feedburner:origLink>http://techknack.net/svn-setup/</feedburner:origLink></item><item><title>Bash rpad, lpad, and “center-pad”</title><link>http://feedproxy.google.com/~r/TechKnack/~3/a5CD4gsOa7g/</link><category>linux</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Mon, 03 Aug 2009 14:09:57 PDT</pubDate><guid isPermaLink="false">http://techknack.net/?p=297</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>One thing it seems bash has no tool for is padding strings.  If you want to left-pad a number (say, 3) with zeros to a certain &#8220;string width&#8221; (say, 5 characters wide), you could do:</p>
<pre class="brush: bash;">
$ echo `printf &quot;%05d&quot; 3`
00003
</pre>
<p>However, this only works with numbers.  Actually, it only works with integers (bash can&#8217;t handle floats, apparently).  Worse, it only pads on the left with zeros.  No right-padding, no &#8220;other character&#8221; padding.</p>
<pre class="brush: bash;">
$ echo `printf &quot;%05d&quot; -`
bash: printf: -: invalid number
00000
$ echo `printf &quot;%05s&quot; -`
-
</pre>
<p>Breaking out of numbers and into string concatenation, however, makes it a bit easier:</p>
<pre class="brush: bash;">
function lpad {
    word=&quot;$1&quot;
    while [ ${#word} -lt $2 ]; do
        word=&quot;$3$word&quot;;
    done;
    echo &quot;$word&quot;;
}
$ lpad 3 5 0
00003
</pre>
<p>&#8220;But it&#8217;s the same output!&#8221; you say?  Well, for this input it happens to be, yes.</p>
<pre class="brush: bash;">
$ lpad 4 5 -
----4
$ lpad hello 11 -
------hello
$ lpad hello 11 !
!!!!!!hello
</pre>
<p>We can even pad with whitespace:</p>
<pre class="brush: bash;">
$ echo &quot;'`lpad hello 11 \ `'&quot;
'      hello'
</pre>
<p>Here I&#8217;ve printed single quotes around the results to show the space padding, and used the backslash to escape the space in the command, so it would be used as $3 rather than be seen as CLI whitespace (and ignored).</p>
<p>Of course, this function can be switched around to make an rpad function, and extended to make a cpad function:</p>
<pre class="brush: bash;">
function rpad {
    word=&quot;$1&quot;
    while [ ${#word} -lt $2 ]; do
        word=&quot;$word$3&quot;;
    done;
    echo &quot;$word&quot;;
}
function cpad {
    word=&quot;$1&quot;
    while [ ${#word} -lt $2 ]; do
        word=&quot;$word$3&quot;;
        if [ ${#word} -lt $2 ]; then
            word=&quot;$3$word&quot;
        fi;
    done;
    echo &quot;$word&quot;;
}
$ rpad &quot;w00t&quot; 15 ^
w00t^^^^^^^^^^^
$ cpad &quot;hello, world&quot; 50 -
-------------------hello, world-------------------
</pre>
<p>Of course, these functions aren&#8217;t perfect, as they make (at least) the following assumptions:</p>
<ol>
<li>Inputs $1 (word to pad), $2 (length to pad to), and $3 (padding characters) are given.</li>
<li>Input $2 is a number.</li>
<li>Input $3 is one character, and no more.</li>
</ol>
<p>Assumption #1 means that if, for example, only $1 and $2 are given, the function will go into an infinite loop (or actually exit with an error, &#8220;bash: [: 8: unary operator expected").  This can be fixed by checking for those variables' existence, and initializing some internal variables to default values if they are not:</p>
<pre class="brush: bash;">
function lpad {
    if [ &quot;$1&quot; ]; then
        word=&quot;$1&quot;;
    else
        word=&quot;&quot;;
    fi;

    if [ &quot;$2&quot; ]; then
        len=$2;
    else
        len=${#word};
    fi;

    if [ &quot;$3&quot; ]; then
        padding=&quot;$3&quot;;
    else
        padding=&quot; &quot;;
    fi;

    while [ ${#word} -lt $len ]; do
        word=&quot;$padding$word&quot;;
    done;
    echo &quot;$word&quot;;
}
</pre>
<p>Here, the "word" defaults to a blank string, the "length" defaults to the length of the word, and the "padding" defaults to a space.  If only the word is given, the word will be returned.  If word and length are given, word will be padded with spaces.  If all three are given, it works fine.</p>
<p>Assumption #2 means that if $2 is a string containing anything other than digits 0-9, the function will exit with an error ("bash: [: &lt;length&gt;: integer expression expected").  This can be fixed by "filtering" any non-digit characters out of the length string:</p>
<pre class="brush: bash;">
    if [ &quot;$2&quot; ]; then
        len=$((`echo $2 | sed 's/[^0-9]//g'`));
    else
        len=${#word};
    fi;
</pre>
<p>This uses the sed tool, which should be available on most if not all *nix systems, to replace all non-digits ([^0-9], in regex notation) with nothing, then converts the result from a string of numbers to an actual number with $(()).  This means that "5uh0oh0" will be turned into 500.  We'll get a scary big string in that case, but at least we won't get errors!</p>
<p>Assumption #3 means that if the pad string is more than one character, the result *could* be longer than the length we wanted.  For example:</p>
<pre class="brush: bash;">
$ string=`cpad hey 50 -`
$ echo ${#string}
50
$ string=`cpad hey 50 -=`
$ echo ${#string}
51
</pre>
<p>In the second example there, the length of the output string actually has one extra character.  This is because the function only adds the padding while the length of the string is less than our specified length; it doesn't actually do any length-checking once we've gone beyond the "length less than" test.  This is fixed slightly less simply, and requires different logic for each function:</p>
<pre class="brush: bash;">
function rpad {
    ...
    while [ ${#word} -lt $len ]; do
        word=&quot;$word$padding&quot;;
    done;
    while [ ${#word} -gt $len ]; do
        word=${word:0:$((${#word}-1))}
    done;
    echo &quot;$word&quot;;
}
function lpad {
    ...
    while [ ${#word} -lt $len ]; do
        word=&quot;$padding$word&quot;;
    done;
    while [ ${#word} -gt $len ]; do
        word=${word:1:$((${#word}-1))}
    done;
    echo &quot;$word&quot;;
}
function cpad {
    ...
    while [ ${#word} -lt $len ]; do
        word=&quot;$word$padding&quot;;
        if [ ${#word} -lt $len ]; then
            word=&quot;$padding$word&quot;
        fi;
    done;
    while [ ${#word} -gt $len ]; do
        word=${word:0:$((${#word}-1))}
        if [ ${#word} -gt $len ]; then
            word=${word:1:$((${#word}-1))}
        fi;
    done;
    echo &quot;$word&quot;;
}
</pre>
<p>The solution uses the bash substring notation, ${variable:start:length}, to strip off extra characters at the end/beginning when the resulting string is too long.  Of course, this isn't a perfect fix; if you specify a shorter length than your input string, the string will simply be truncated to that length.  Also, if you use a long string for padding with cpad, you'll get unbalanced output.</p>
<pre class="brush: bash;">
$ lpad &quot;hello, world&quot; 5
world
$ rpad &quot;hello, world&quot; 5
hello
$ cpad &quot;hello, world&quot; 5
lo, w
$ cpad hello 50 ---=--=---
-=--=------=--=---hello---=--=------=--=------=--=
</pre>
<p>You could easily continue to refine this function to make it obey all sorts of preconceptions, but I'm going to stop here.  If you use the functions as they're meant to be used -- numerical lengths and single-character paddings, etc -- it'll all be fine.</p>
<p>Though if you do find a nice, concise way to make these behave "properly", feel free to share in the comments!</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=297&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=a5CD4gsOa7g:0_r-YbOFg08:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=a5CD4gsOa7g:0_r-YbOFg08:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=a5CD4gsOa7g:0_r-YbOFg08:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=a5CD4gsOa7g:0_r-YbOFg08:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=a5CD4gsOa7g:0_r-YbOFg08:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=a5CD4gsOa7g:0_r-YbOFg08:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=a5CD4gsOa7g:0_r-YbOFg08:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/a5CD4gsOa7g" height="1" width="1"/>]]></content:encoded><description>One thing it seems bash has no tool for is padding strings.  If you want to left-pad a number (say, 3) with zeros to a certain &amp;#8220;string width&amp;#8221; (say, 5 characters wide), you could do:

$ echo `printf &amp;#34;%05d&amp;#34; 3`
00003

However, this only works with numbers.  Actually, it only works with integers (bash can&amp;#8217;t handle [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/bash-rpad-lpad-and-center-pad/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">1</slash:comments><feedburner:origLink>http://techknack.net/bash-rpad-lpad-and-center-pad/</feedburner:origLink></item><item><title>PHP Socket Implementation and Webpage Downloader</title><link>http://feedproxy.google.com/~r/TechKnack/~3/epCtim0OfUg/</link><category>php</category><category>web</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Thu, 18 Jun 2009 21:09:12 PDT</pubDate><guid isPermaLink="false">http://techknack.net/?p=294</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>This post reveals two handy PHP constructs I&#8217;ve been using for a while now: a Socket class and a webpage-downloading function.</p>
<p>The files:</p>
<ul>
<li>Socket.php (<a href="/examples/source_highlighted.php?file=phpwebpage/Socket.php">highlighted</a>; <a href="/examples/source_plaintext.php?file=phpwebpage/Socket.php">plaintext</a>)</li>
<li>webpage.php (<a href="/examples/source_highlighted.php?file=phpwebpage/webpage.php">highlighted</a>; <a href="/examples/source_plaintext.php?file=phpwebpage/webpage.php">plaintext</a>)</li>
</ul>
<p>What good is a full-on Socket class and function, you may ask, when you can simply run file_get_contents with allow_url_fopen turned on?  Well, consider that you want to send specific headers to the server, to POST data for example.  Also, there are other uses for a Socket class than webpage downloading; I use this Socket implementation in a PHP-based IRC bot that I run (by the name of ecode on the Freenode network).  You can use the socket to interact with many different servers.</p>
<h3>Socket</h3>
<p>The Socket class essentially encapsulates PHP&#8217;s socket_* functions, making it easy and straightforward to make a TCP connection with a server.  The socket is given a timeout period in microseconds; while I&#8217;m not 100% sure what this means, I do know that it emulates a sort of &#8220;busy wait&#8221; while waiting for content, allowing applications to do processing while waiting for data to come in through the pipe.</p>
<p>To use the socket, include the Socket.php file and create a variable to hold a new Socket.</p>
<pre class="brush: php;">
require_once(&quot;Socket.php&quot;);

$s = new Socket(1000);
</pre>
<p>The Socket will automatically set up the outgoing TCP socket for usage.  The parameter passed to the new socket (1000 in this case) is the microsecond timeout &#8212; this can be left blank if you wish.</p>
<p>Once created, the socket can connect to any service through the connect() function.  The function takes two arguments: host and port.  Both parameters are optional to allow reuse of the connection; if called without parameters, the Socket will establish a new connection using a host and port from the last connection.  The host is the address you would ping to see if the servers are up &#8212; for example, either google.com or 74.125.67.100, the Socket doesn&#8217;t care which as long as it&#8217;s a server it can connect to.  The port is optional: if you specify a port number, that will be used; if no port number is specified, it attempts to extract a port number from the host (for example, if you used google.com:80); failing that, it will use the last port number specified (default of 80).</p>
<p>Once connected, you can use the send() and get() functions to &#8212; you guessed it &#8212; send() and get() data.  The send function takes a string parameter, the message to be sent, sends the message through socket_write, and returns the value returned by socket_write (the number of bytes successfully sent through the socket).  The get function takes an array parameter to specify fetching options: line (boolean) and length (integer).  If line is set to true, then get will fetch one character at a time until a newline (\n) is encountered and return the line along with any trailing carriage returns (\r) and newlines (\n).  If length is specified as greater than 0, get will fetch one character at a time until the specified number of characters have been read.  If neither is specified, the function will read and return the next 512 characters.  Of course, all cases will terminate if the end of data has been reached.</p>
<p>As a quick example, this portion uses the previously created socket to download the HTTP response headers from google.com&#8217;s homepage:</p>
<pre class="brush: php;">
if ($s-&gt;connect(&quot;www.google.com&quot;, 80)) {
    $get;
    $s-&gt;setVerbose(false);
    $s-&gt;send(&quot;GET / HTTP/1.1&quot;);
    $s-&gt;send(&quot;Host: www.google.com&quot;);
    $s-&gt;send(&quot;&quot;);

    $response = &quot;&quot;;
    while (strpos($response, &quot;\r\n\r\n&quot;)===false) {
        $response .= $s-&gt;get(array(&quot;line&quot;=&gt;true));
    }
}
</pre>
<p>The Socket supports logging and verbosity functions.  The setLog function takes a boolean (true/false for logging) and a string (filename to log to).  The setVerbose function takes only a boolean (true/false &#8212; default false &#8212; for printing out extra information to the console).</p>
<p>Finally, when you&#8217;re finished with the connection, you can call the disconnect() function to cleanly sever the connection.</p>
<h3>webpage()</h3>
<p>The webpage function takes three parameters: a url, a headers array, and a &#8220;headers only&#8221; flag.  The url is simple enough; this is the full url that you want to fetch, including both the host name and the file path (and, optionally, a port number after the hostname).  The headers array goes two ways; you can pass in headers to send and you&#8217;ll get back any headers received.  The headers only flag specifies whether or not to fetch the content of the page; if not, it saves the time of downloading that content, and leaves you with only the response headers.</p>
<p>The headers parameter is two-way, as I mentioned.  You can pass in an associative array of header-name => header-value pairs, which will be formatted properly and sent to the server along with the HTTP request.</p>
<pre class="brush: php;">
$h = array(
    &quot;User-Agent&quot; =&gt; &quot;PHP-webscraper/1.0 php/5&quot;,
    &quot;Authorization&quot; =&gt; &quot;Basic &quot;.base64_encode(&quot;username:password&quot;)
);
$page = webpage(&quot;http://twitter.com/statuses/mentions.json&quot;, $h);
</pre>
<p>The above will send the User-Agent, for identification, and Authorization, to login, headers to the Twitter API, fetching any tweets that mention the user name &#8220;username&#8221; (using the password &#8220;password&#8221;).  The response will be in JSON, and will require extra processing, but this will get you the data.  For more information on HTTP headers, check out the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">HTTP 1.1 RFC, section 14</a>.</p>
<p>Once the page is received from the server, the headers array you passed in will be overwritten by the server&#8217;s response headers, which you can then examine with external code.  With this method, you can also pass in a simple empty array, which will then be populated with the response headers.</p>
<p>The third parameter to the webpage function is simple enough.  Pass true if you want to receive only the server&#8217;s response headers, cutting off the response once those have been received; a blank string will be returned from the function.  Pass false (or no third parameter) if you wish to receive the entire page, the contents of which will be returned as a string.</p>
<p>With these two files (and, of course, the PHP constructs they define), you can download any webpage you would have access to with your browser, provided you are able to use sockets on your server.  Need to scrape some data from a page?  Download that page and apply some regexes to the content.  HTTP Auth required?  No problem, send an extra header with your creds.  The function could also be modified to POST data to a page, or even download and save images and other media; however, those functions will be left as an exercise for the reader.</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=294&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=epCtim0OfUg:dMXz3OOn4HA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=epCtim0OfUg:dMXz3OOn4HA:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=epCtim0OfUg:dMXz3OOn4HA:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=epCtim0OfUg:dMXz3OOn4HA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=epCtim0OfUg:dMXz3OOn4HA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=epCtim0OfUg:dMXz3OOn4HA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=epCtim0OfUg:dMXz3OOn4HA:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/epCtim0OfUg" height="1" width="1"/>]]></content:encoded><description>This post reveals two handy PHP constructs I&amp;#8217;ve been using for a while now: a Socket class and a webpage-downloading function.
The files:

Socket.php (highlighted; plaintext)
webpage.php (highlighted; plaintext)

What good is a full-on Socket class and function, you may ask, when you can simply run file_get_contents with allow_url_fopen turned on?  Well, consider that you want to send [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/php-socket-implementation-and-webpage-downloader/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">0</slash:comments><feedburner:origLink>http://techknack.net/php-socket-implementation-and-webpage-downloader/</feedburner:origLink></item><item><title>“Selective Top” Bash Script</title><link>http://feedproxy.google.com/~r/TechKnack/~3/dGLIpNTMzfg/</link><category>linux</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Fri, 29 May 2009 15:05:18 PDT</pubDate><guid isPermaLink="false">http://techknack.net/?p=292</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>In my daily work on linux, I constantly have several tabs open in my console app for various CLI activites.  Normally, when I log in to a session, Yakuake auto-starts and I immediately open four tabs in it.  Tab four becomes a system monitor through the use of the &#8220;top&#8221; command, tab one becomes an SSH window to my home server, and the other two are open for whatever.</p>
<p>Occasionally, I&#8217;ll want to keep an eye on specific processes and their resource usage, but they won&#8217;t show up in my &#8220;top&#8221; screen, no matter how I sort the fields.  At this point, I have to use &#8220;ps&#8221; to find the PID (process ID) of each process I want to watch and add each PID to the end of the &#8220;top&#8221; command (using the -p switch for each PID), just to watch a few specific processes.</p>
<p>This little process is well-defined and repeating.  Just the kind of task computers were designed to handle.  I wrote a bash script which takes any number of arguments &#8212; all of them names of processes you want to watch &#8212; finds all related PIDs, and tacks them on to a top command.</p>
<pre class="brush: bash;">
#!/bin/bash

ps=&quot;&quot;

for p in $*; do
    ps=&quot;$ps `ps -e | grep $p | awk '{print $1}' | sort -n | sed 's/^/-p /' | tr '\n' ' '`&quot;
done

while [ `echo &quot;$ps&quot; | grep -c &quot;  &quot;` -ne 0 ]; do
    ps=`echo &quot;$ps&quot; | sed 's/  / /'`
done
ps=`echo &quot;$ps&quot; | sed 's/\(^\s*\)\|\(\s*$\)//'`

if [ -z &quot;$ps&quot; ]; then
    echo &quot;No processes found.&quot;
else
    top $ps
fi
</pre>
<p><em>Please only use the above as a reference!</em>  The script uses a lot of single quotes, which WordPress likes to convert to &#8220;smart&#8221; quotes.  To see the script as it should be, you can <a href="/examples/bash/btm.txt">view the script as a text file</a>.</p>
<p>The first chunk (&#8221;for p in $*&#8230;&#8221;) iterates over all the arguments, pulling out all the associated PIDs.  The second block (&#8221;while&#8230;&#8221;) replaces multiple spaces with single spaces within the extracted process list, and trims any whitespace off of the beginning and end, to prepare the string for the test at the end.  The last part (&#8221;if [ -z&#8230;&#8221;) checks if the process list is empty or not (if it&#8217;s all spaces, it&#8217;s &#8220;not&#8221;).  If it is empty, the script alerts you and exits &#8212; you won&#8217;t get an empty top list (or full, since there are no arguments passed).  If it&#8217;s not empty, we can assume we found some PIDs, so we tack that on to the top command and take you straight there.</p>
<p>If you want to use this, just stick it in an executable file in your path (see &#8220;echo $PATH&#8221; at the command line); I simply named mine &#8220;btm&#8221;, but it doesn&#8217;t matter what you call it.  If you have any command line arguments you like to pass on to top, you can stick them between &#8220;top&#8221; and &#8220;$ps&#8221; in the &#8220;else&#8221; part of the script.  Once it&#8217;s all set up, just call the script with the process names you want to watch:</p>
<pre class="brush: bash;">$ btm rsync firefox thunderbird</pre>
<p>Keep in mind that this will only work with processes that are currently running &#8212; once that rsync job is done, it&#8217;ll disappear from the list, but no new rsync jobs will show up unless you re-run the script.</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=292&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=dGLIpNTMzfg:nzDaYGIlNOs:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=dGLIpNTMzfg:nzDaYGIlNOs:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=dGLIpNTMzfg:nzDaYGIlNOs:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=dGLIpNTMzfg:nzDaYGIlNOs:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=dGLIpNTMzfg:nzDaYGIlNOs:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=dGLIpNTMzfg:nzDaYGIlNOs:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=dGLIpNTMzfg:nzDaYGIlNOs:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/dGLIpNTMzfg" height="1" width="1"/>]]></content:encoded><description>In my daily work on linux, I constantly have several tabs open in my console app for various CLI activites.  Normally, when I log in to a session, Yakuake auto-starts and I immediately open four tabs in it.  Tab four becomes a system monitor through the use of the &amp;#8220;top&amp;#8221; command, tab one [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/selective-top-bash-script/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">1</slash:comments><feedburner:origLink>http://techknack.net/selective-top-bash-script/</feedburner:origLink></item><item><title>Conky Transparency Fix for KDE4</title><link>http://feedproxy.google.com/~r/TechKnack/~3/v1nbSwUFRkU/</link><category>linux</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Thu, 07 May 2009 18:31:42 PDT</pubDate><guid isPermaLink="false">http://techknack.net/?p=290</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I love the feeling of power that comes from doing something that google couldn&#8217;t help with <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>In this case, I&#8217;ve spent the past couple days messing with Conky (yes, it is as addicting as they say).  A google search will tell you what it is, and another will show you all the cool things it can do.  One thing that absolutely irked me, though, was the lack of transparency under KDE4.  Basically, my options were:</p>
<ol>
<li>Use &#8220;own_window_type override&#8221;, which seemed to allow transparency, but conky would crash unless I set use_xft to no.  So I got true transparency, but no sexy fonts.</li>
<li>Use &#8220;own_window_type normal&#8221;, and use sexy fonts with use_xft set to true, but using the &#8220;pseudo-transparency&#8221; that conky uses would use the wrong wallpaper.</li>
<li>Use #2 above, but set own_window_transparency to no, and use a solid color background.</li>
</ol>
<p>While I was tweaking conky, I was using #3.  Not the best I could hope for, but the best I could get without any help.  And good enough for development.</p>
<p>When I asked google for help, I found out several things:</p>
<ol>
<li>Conky uses the wallpaper set on the &#8220;root window&#8221; for its pseudo-transparency.</li>
<li>KDE4/Plasma uses its own desktop-drawing to draw the wallpaper.</li>
<li>KDE4/Plasma does not update the root window&#8217;s wallpaper when it updates the desktop&#8217;s wallpaper.</li>
<li>The latest and greatest KDE4 versions are phasing out DCOP support in favor of DBUS.  DCOP, coupled with a program called &#8220;feh&#8221;, is what many workarounds to this issue have used.</li>
<li>The DBUS interface to KDE4 is not yet fully developed, and, as such, has no way to get the current desktop wallpaper.</li>
<li>A method has been pieced together that <a href="http://bbs.archlinux.org/viewtopic.php?pid=549405">greps the user&#8217;s appletsrc file</a> to get the user-set wallpaper:
<pre class="brush: bash;">
feh --bg-scale &quot;`grep 'wallpaper=' ~/.kde4/share/config/plasma-appletsrc | tail --bytes=+11`&quot;
</pre>
</li>
<li>Nobody seems to have hacked together anything that works for those of us who use the slideshow feature of KDE4.</li>
</ol>
<p>Figures. Anyway, after some more digging around, I&#8217;ve created a script which fixes the problem for slideshow-users:</p>
<pre class="brush: bash;">
#!/bin/bash
# kde4bg - by Andrew Rowls &lt;andrew@techknack.net&gt;

SLIDEPATH=`grep slidepaths ~/.kde/share/config/plasma-appletsrc | tail --lines=1 | tail --bytes=+12`
WALL=`ls --sort=time --time=atime &quot;$SLIDEPATH&quot; | head --lines=1`
feh --bg-scale &quot;$SLIDEPATH/$WALL&quot;
</pre>
<p>Part one (SLIDEPATH) greps your appletsrc file for the directory(ies) set as your slidepath.  In the appletsrc file, this is probably a comma-separated list of directories, but I only wrote the script to handle one directory.  Also, you may have noticed that I used the directory ~/.kde rather than ~/.kde4.  I looked at the ctimes (modification times) of those files (basically, the times KDE4 last modified them).  The ctime for ~/.kde4/share/config/plasma-appletsrc was about 5 days earlier than ~/.kde/share/config/plasma-appletsrc .  A little further digging, and I found that the &#8220;kde4 version&#8221; had an old path to my slideshow directory, while the &#8220;kde version&#8221; had the current (ie, correct) path.  If your KDE4 installation still uses ~/.kde4, change the directory appropriately.</p>
<p>Part two (WALL) gets the last-accessed file in that slidepath.  Basically, every time Plasma changes the wallpaper, it has to *access* the image file, read it into memory, and display it.  As long as the access times (read: wallpaper switches) are at least a minute apart, the current wallpaper will bubble to the top so we can grab its name.  This, of course, assumes that 1) only your slideshow images are in your slidepath directory and 2) you don&#8217;t go messing with those image files very often.</p>
<p>Finally, part three uses feh to set the root window&#8217;s wallpaper, giving it the full path to the image file.</p>
<p>Now, every time you run this script, it will find the last-accessed (presumably by Plasma itself) file and set it as the background, at which point Conky can use that image for its pseudo-transparency.  Since the script has to be manually run, you could put it on cron or as a part of Conky&#8217;s updating, like this:</p>
<pre class="brush: bash;">
${texeci 3 /path/to/kde4bg}
</pre>
<p>That&#8217;ll execute the script every 3 seconds from conky.  You could put it as every 1 second, but I noticed that, when I tried it, the cpu usage would get a little carried away.  3 seconds is enough to keep cpu usage down while keeping the &#8220;transparency&#8221; sufficiently up-to-date.</p>
<p>Quick disclaimer: This was just a quick throw-together script, written based on my needs.  The main issue with it right now is it only supports one slide directory.  In KDE4, you can set multiple directories, but I really don&#8217;t know what that would do to the script.  Probably just confuse it, but then it wouldn&#8217;t work.</p>
<p>Enjoy.</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=290&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=v1nbSwUFRkU:YaRda1Frw-8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=v1nbSwUFRkU:YaRda1Frw-8:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=v1nbSwUFRkU:YaRda1Frw-8:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=v1nbSwUFRkU:YaRda1Frw-8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=v1nbSwUFRkU:YaRda1Frw-8:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=v1nbSwUFRkU:YaRda1Frw-8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=v1nbSwUFRkU:YaRda1Frw-8:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/v1nbSwUFRkU" height="1" width="1"/>]]></content:encoded><description>I love the feeling of power that comes from doing something that google couldn&amp;#8217;t help with  
In this case, I&amp;#8217;ve spent the past couple days messing with Conky (yes, it is as addicting as they say).  A google search will tell you what it is, and another will show you all the cool [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/conky-transparency-fix-for-kde4/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">0</slash:comments><feedburner:origLink>http://techknack.net/conky-transparency-fix-for-kde4/</feedburner:origLink></item><item><title>PHP Templating: Variable Date</title><link>http://feedproxy.google.com/~r/TechKnack/~3/2Oaj-Aiuv2I/</link><category>blogs</category><category>design</category><category>php</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Sat, 28 Mar 2009 18:38:55 PDT</pubDate><guid isPermaLink="false">http://techknack.net/?p=282</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>In building a template system, you may want some way to inject date info using a timestamp (perhaps generated through mktime() ) and date().  But you don&#8217;t want the format to be dependent on the template parser, but rather specified through the variable in the template itself.</p>
<pre class="brush: php;">
$template = preg_replace(
    &quot;#\%date(\:\s*(.*?))?\%#ei&quot;,
    &quot;date(('\\2'!=\&quot;\&quot;?'\\2':\$date_format), \$timestamp)&quot;,
    $template
);
</pre>
<p>This falls on the &#8220;advanced&#8221; side of intermediate, I would think, so here&#8217;s an explanation:</p>
<p>This is a standard preg_replace($pattern, $replacement, $source_string).  What we want to do is replace the variable %date% in $template with a date string generated from $timestamp.  But %date% is too limiting; within the template, we&#8217;d like to write something like %date: F j, Y% to get a date like &#8220;March 12, 2009,&#8221; or any other format.</p>
<p>The regular expression is like so: <strong>#\%date(\:\s*(.*?))?\%#ei</strong>.  The &#8220;hashes&#8221; (#) here act as delimiters for the regex; the <em>e</em> and <em>i</em> after the second hash turn on &#8220;eval&#8217;d replacement&#8221; and &#8220;case-insensitivty&#8221;, respectively (<a href="http://us.php.net/manual/en/reference.pcre.pattern.modifiers.php">learn more about PCRE regex modifiers at php.net</a>).  So the regex to deal with is <strong>\%date(\:\s*(.*?))?\%</strong>.  The <strong>\%</strong> are the markers for our template variables; <strong>date</strong> is the name of the variable to look for.  <strong>(\:\s*(.*?))?</strong> matches zero or one instances of our &#8220;variable modifier&#8221;: a colon, followed by any amount of whitespace (including none), followed by any number of any characters (where the extra <strong>?</strong> makes the <strong>.*</strong> &#8220;non-greedy&#8221;; it&#8217;ll stop &#8220;eating&#8221; characters when it reaches the next percent sign).  For preg_replace, this will place our date() format in &#8220;\\2&#8243; if it is present; if it is not present, &#8220;\\2&#8243; will not be set.</p>
<p>The replacement is as follows: <strong>&#8220;date((&#8217;\\2&#8242;!=\&#8221;\&#8221;?&#8217;\\2&#8242;:\$date_format), \$timestamp)&#8221;</strong>.  preg_replace will insert the matched date format from the regex into every occurrence of &#8220;\\2&#8243; in the replacement string, and then run eval() on the resulting string to get the replacement value (all thanks to the <em>e</em> modifier in the regex).  Assuming our regex matched &#8220;F j, Y&#8221; as the match, we&#8217;ll get <strong>&#8220;date((&#8217;F j, Y&#8217;!=\&#8221;\&#8221;?&#8217;F j, Y&#8217;:\$date_format), \$timestamp)&#8221;</strong>.  The <strong>(&#8217;F j, Y&#8217;!=\&#8221;\&#8221;?&#8217;F j, Y&#8217;:\$date_format)</strong> part chooses the date format; if the match is not set (ie, an empty string), use whatever is in the variable $date_format; else, if the date format was matched, use it.  This allows us to use %date% in the template, to default to a server-side specified format.</p>
<p>And there it is.  You can now put &#8220;%date: F j, Y%&#8221; in your template, and it will be expanded to &#8220;March 12, 2009&#8243; (depending on the actual date, of course <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ).  That is, if your templating system works in the first place&#8230;</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=282&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=2Oaj-Aiuv2I:1N2BLh-IWAE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=2Oaj-Aiuv2I:1N2BLh-IWAE:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=2Oaj-Aiuv2I:1N2BLh-IWAE:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=2Oaj-Aiuv2I:1N2BLh-IWAE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=2Oaj-Aiuv2I:1N2BLh-IWAE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=2Oaj-Aiuv2I:1N2BLh-IWAE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=2Oaj-Aiuv2I:1N2BLh-IWAE:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/2Oaj-Aiuv2I" height="1" width="1"/>]]></content:encoded><description>In building a template system, you may want some way to inject date info using a timestamp (perhaps generated through mktime() ) and date().  But you don&amp;#8217;t want the format to be dependent on the template parser, but rather specified through the variable in the template itself.

$template = preg_replace(
    &amp;#34;#\%date(\:\s*(.*?))?\%#ei&amp;#34;,
  [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/php-templating-variable-date/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">0</slash:comments><feedburner:origLink>http://techknack.net/php-templating-variable-date/</feedburner:origLink></item><item><title>A Method for CSS Variables</title><link>http://feedproxy.google.com/~r/TechKnack/~3/7faDE9aVREA/</link><category>css</category><category>php</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Thu, 19 Mar 2009 14:33:57 PDT</pubDate><guid isPermaLink="false">http://techknack.net/?p=279</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Jay Salvat at <a href="http://net.tutsplus.com">nettuts</a> has written an article on <a href="http://net.tutsplus.com/tutorials/html-css-techniques/how-to-add-variables-to-your-css-files/">adding variables to your css files</a>.  I like the approach, which includes caching the results, though the code presented is not 100% secure, as <a href="http://net.tutsplus.com/tutorials/html-css-techniques/how-to-add-variables-to-your-css-files/#comment-45724">commenter Pelle</a> has noted.</p>
<p>As for the most popular response, having apache run css files as php code, there are a few issues that come to mind that the article&#8217;s approach takes care of: caching, cleanliness, and portability.</p>
<p>Caching, as the author notes, reduces the number of CPU cycles the server must devote to php processing of the css files.  The parser also implements HTTP features for determining if the file has been modified and needs reloading.  Simply setting the server to parse CSS files, you may or may not lose these features, depending on your server settings.</p>
<p>The method is also clean; using a parsing class lets you put simple variable assignments directly in the CSS file.  Associating CSS with the PHP parser requires you to use the PHP tags and all the appropriate PHP markers ( &lt;?php $var = &#8220;whatever&#8221;; ?&gt; ); if you miss a semicolon, a quote, a dollar sign, or any other part of the required &#8220;PHP syntax&#8221;, your css file at best will contain a php error or warning message, making the code invalid, or at worst will not display at all, giving a blank css file.  The parser Jay gives, while requiring a syntax of its own, is much simpler to use.</p>
<p>Jay&#8217;s solution is portable; the only server configuration necessary is enabling apache&#8217;s mod_rewrite module, which should be enabled anyway.  If that is done, you can move the .htaccess and enhanced_css.php files to any server, and it will work without further server config.</p>
<p>As commenter Pelle notes, however, if someone knows you&#8217;re using this method and they know the path to your enhanced_css.php file, they could use it maliciously.  Jay responds with a quick fix that makes sure the class only parses files ending with &#8220;css&#8221;, but I believe that further security could be provided, though I&#8217;ve not taken the time to analyze the code enough to make suggestions.</p>
<p><a href="http://net.tutsplus.com/tutorials/html-css-techniques/how-to-add-variables-to-your-css-files/#comment-45795">Chris Pratt</a> made a suggestion of changing the parser to hunt out <a href="http://disruptive-innovations.com/zoo/cssvariables/#mozTocId992035">CSS variables as proposed by Daniel Glazman and David Hyatt</a>.  I thought this would be a good idea; the syntax proposed in that doument is much more like the CSS3 stuff we&#8217;re slowly getting used to, such as :not() and attr().</p>
<p>For those interested, you could read <a href="http://www.w3.org/People/Bos/CSS-variables">why some think CSS variables are harmful</a>.</p>
<img src="http://techknack.net/?ak_action=api_record_view&id=279&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=7faDE9aVREA:u2Q_FcVGxa0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=7faDE9aVREA:u2Q_FcVGxa0:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=7faDE9aVREA:u2Q_FcVGxa0:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=7faDE9aVREA:u2Q_FcVGxa0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=7faDE9aVREA:u2Q_FcVGxa0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=7faDE9aVREA:u2Q_FcVGxa0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=7faDE9aVREA:u2Q_FcVGxa0:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/7faDE9aVREA" height="1" width="1"/>]]></content:encoded><description>Jay Salvat at nettuts has written an article on adding variables to your css files.  I like the approach, which includes caching the results, though the code presented is not 100% secure, as commenter Pelle has noted.
As for the most popular response, having apache run css files as php code, there are a few [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/a-method-for-css-variables/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">0</slash:comments><feedburner:origLink>http://techknack.net/a-method-for-css-variables/</feedburner:origLink></item><item><title>Make Windows Act Like Linux</title><link>http://feedproxy.google.com/~r/TechKnack/~3/mi4Wv3YnhIY/</link><category>linux</category><category>windows</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Thu, 26 Feb 2009 12:23:11 PST</pubDate><guid isPermaLink="false">http://techknack.net/?p=262</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>As I use Win7 more and more, there are things that I notice it doesn&#8217;t do that linux did do.  Virtual desktops, certain programs minimizing to the systray, clipboard history.  Small things, things that you normally would take for granted if you had them, but things that become very noticeable when they&#8217;re suddenly missing.  Fortunately, there are lots of little, independently developed programs that provide some of the little pieces of functionality that linux enthusiasts grow used to.</p>
<p>Before I begin listing off links, I feel the need to put a disclaimer.  I realize that the title for this post will make many laugh.  It makes me laugh.  Why, after all, would you want to make Windows act like Linux when you can have the real deal for free?  The answer is just that: Linux is not free, not in the modern sense.  The switch from windows to linux is a shift in the way your world works.  It&#8217;s often a shift for the better, but the shift itself almost always takes time &#8212; time to get used to a new ideology, time to figure things out, time to make things work just the way you want them to.  Time that many people don&#8217;t have.</p>
<p>Truth is, there is simply no way to make Windows akin to Linux in any way, shape, or form.  They are two different environments, two different philosophies.  However, thanks to third-party developers and a fairly open distribution platform (I&#8217;m looking at you, Apple), it&#8217;s possible to bring some functionality to the Windows desktop.  You can&#8217;t change its personality, but you can change its behavior.  This list is meant to bring some linux-y goodness to those without the time to jump in head first.</p>
<p>Now, with that out of the way, the list:</p>
<ul>
<li><b>Clipboard history</b>: something so small, so unnoticeable until you need it, and so nice to have when you do need it.  <a href="http://clipx.org/">ClipX</a>; 131K download; 252K footprint (no plugins, 25 items, text only).</li>
<li><b>Virtual desktops</b>: Most linux distros come with virtual desktops or workspaces.  Once you learn to work with them, you can&#8217;t work without them.  <a href="http://virtuawin.sourceforge.net/">VirtuaWin</a>; 385K download; 1.3M footprint (four desktops).  Won&#8217;t give you the Compiz cube, but you shouldn&#8217;t be expecting that anyways <img src='http://techknack.net/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  .</li>
<li><b>Window snapping</b>: a nice feature for those who like to work with non-maximized windows in the corners of their desktops.  <a href="http://ivanheckman.com/allsnap/">AllSnap</a>; 69.4K download; just over 1M footprint (portable version via startup folder, desktop snapping only)</li>
<li><b>Systray minimization</b>: I got used to Amarok minimizing to the systray, and I wanted Windows Media Player to do the same.  <a href="http://www.teamcti.com/trayit/trayit.htm">TrayIt!</a>; 327K download; just over 1M footprint (one window rule for WMP).</li>
<li><b>Launchy/Gnome-Do/Program Launcher</b>: I never had much luck with launchy under linux, so I ended up using Gnome-Do for app launching.  Win7, however, seems to do fine with its Start Menu searchbar.  No download, native footprint.</li>
<li><b>Wallpaper rotater</b>: while not strictly functional, it is nice to be able to choose a number of images to auto-rotate through as a wallpaper.  Win7 has this built in.  No download, native footprint.</li>
</ul>
<img src="http://techknack.net/?ak_action=api_record_view&id=262&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=mi4Wv3YnhIY:kE-gntpZAWw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=mi4Wv3YnhIY:kE-gntpZAWw:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=mi4Wv3YnhIY:kE-gntpZAWw:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=mi4Wv3YnhIY:kE-gntpZAWw:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=mi4Wv3YnhIY:kE-gntpZAWw:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=mi4Wv3YnhIY:kE-gntpZAWw:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=mi4Wv3YnhIY:kE-gntpZAWw:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/mi4Wv3YnhIY" height="1" width="1"/>]]></content:encoded><description>As I use Win7 more and more, there are things that I notice it doesn&amp;#8217;t do that linux did do.  Virtual desktops, certain programs minimizing to the systray, clipboard history.  Small things, things that you normally would take for granted if you had them, but things that become very noticeable when they&amp;#8217;re suddenly [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/make-windows-act-like-linux/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">2</slash:comments><feedburner:origLink>http://techknack.net/make-windows-act-like-linux/</feedburner:origLink></item><item><title>Linux-to-Windows Program Replacements</title><link>http://feedproxy.google.com/~r/TechKnack/~3/-m4CoGAvIu0/</link><category>linux</category><category>windows</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">eternicode</dc:creator><pubDate>Mon, 23 Feb 2009 12:21:57 PST</pubDate><guid isPermaLink="false">http://techknack.net/?p=226</guid><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>For the past couple of weeks, I&#8217;ve been using the Windows 7 beta.  A shocker, I know, but I&#8217;ve been able to find replacements for most of the software I use everyday in Linux.  The biggest hangups I&#8217;ve found so far are 1) lack of package manager (PLEASE, Microsoft, implement something to this effect!), and 2) lack of some SSHfs capabilities.  I used SSHfs extensively under linux, and WinSCP just doesn&#8217;t cut it.</p>
<p>Moving from a well-established Linux environment to a squeaky-clean Windows environment is bound to be a bumpy experience without first determining suitable replacements for all the programs that one uses on a daily basis.  After my initial move-in, I was rather ad-hoc in finding replacements.  Now that I&#8217;ve found suitable replacements for my most-used programs, perhaps this list can help those of you looking to make n experimental switch.</p>
<ul>
<li><b>BitTorrent</b>: I do a lot of torrenting, so a good replacement for KTorrent is necessary.  uTorrent has so far served my purposes for the odd torrent, while I continue to use my <a href="http://techknack.net/torrent-management-system-with-rtorrent-and-bash/">automated torrent system</a> on my home server for TV shows and such.</li>
<li><b>IM client with AIM and IRC</b>: I sit on IRC channels all day long these days, occasionally throwing in a word or two, but mainly gleaning whatever knowledge I can from the chatter.  Also, I have an AIM account that is my main real-time online point-of-access.  I&#8217;ve gotten used to Kopete, but it&#8217;s lacking in just the right areas so as to make me dissatisfied with it.  Recently I&#8217;ve installed Pidgin (under Kubuntu), and I&#8217;ve found that it has its own quirks that I&#8217;m dissatisfied with, perhaps more so than with Kopete.  The release of kDE4-only Kubuntu 8.10 has further disappointed me: Kopete lost its IRC capabilities, and I was forced to use Konversation for IRC, but it has been an exceptional IRC program.  Here on Windows, the options are rather scarce &#8212; Pidgin is available, but suffers from the aforementioned shortcomings.  I&#8217;ve heard great things about Digsby, but it doesn&#8217;t seem to support IRC at the moment.  However, as I was forced to split my chatting between two programs on linux, doing the same on windows isn&#8217;t too bad a compromise.  I use Digsby for AIM, and <a href="http://www.silverex.org/">YChat</a>, which seems to be a branch of XChat, for IRC.  I am not completely satisfied with YChat as a Konversation replacement, because it has no auto-login, auto-identification, or auto-join features that work on Win7 (seems to be a dll issue here), but it works well enough.</li>
<li><b>Desktop Feed Reader</b>: My primary feed reader for feeds of interest to me is Google Reader, and that isn&#8217;t going to change.  However, I also use a desktop feed reader to toss temporary feeds like ebay and craigslist search results into.  On linux I&#8217;ve grown accustomed to using Akregator, simply because it&#8217;s included with Kubuntu.  Here on Windows, however, FeedDemon has taken the role quite nicely.</li>
<li><b>Music/Video playback</b>, a la Amarok/Kaffeine: I love my music.  And I love my video, whether it be downloaded TV shows or a DVD.  And I have to have some way to play them.  Regarding music, my needs are not outrageous &#8212; I just need some way to organize my music, fetch album info from the net, and perhaps display album art for me.  I don&#8217;t care for song ratings or anything fancy like that.  Windows Media Player works well in regards to both audio and video, and even works with my Inspiron&#8217;s front-side music player controls.</li>
<li><b>Gimp or PhotoShop</b>: Being in web development, I do occasionally do graphics editing.  For this, I need Gimp at the least, though I would like to try PhotoShop.  I&#8217;m going to attempt to install PS CS4 at some point, but I&#8217;m afraid I won&#8217;t have the free disk space for it.  Bummer.</li>
<li><b>Advanced text editor</b>: On Kubuntu, I used Kate for text editing (HTML, CSS, PHP, etc files).  My requirements here are syntax highlighting, tabs, and sessions.  Code collapsing is a nice feature, but not required.  I will likely use Notepad++ on Windows, though I haven&#8217;t done much development lately to really test its workflow yet.</li>
<li><b>Firefox, Thunderbird, and Sunbird</b>: I&#8217;m sorry, there is no replacement for these three <img src='http://techknack.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  .</li>
<li><b>Office software</b>: On Linux, I got by fine using OpenOffice.  However, since I have free access to Office 2007, I am using that.  Sorry, OSS radicals.</li>
<li><b>Filezilla</b>: Again, no replacement.</li>
<li><b>PDF Viewer/Printer</b>: Another thing that comes stock with Kubuntu.  Foxit is good for PDF viewing, and <a href="http://www.acrosoftware.com/Products/CutePDF/writer.asp">CutePDF</a> works for a printer.</li>
<li><b>File manager</b>: Requirements in this area include all the things I&#8217;ve come to expect from Konqueror: tabbed interface (with spring-loaded tabs, no less), session management, and easy switching amongst various folder views (icons, details, etc).  CubicExplorer fits the bill quite nicely, though I still find myself opening Windows Explorer for short filemanaging stints.</li>
<li><b>SSHfs</b>: This was rather high on my priority list, as I have my web development folders, which reside on my server, mounted locally as SSHfs shares.  This means that, whenever I have internet access, I can simply open, edit, and save the files at home as if they were on my local machine.  Unfortunately, no SSHfs for Windows seems to exist.  I had heard things about Novell&#8217;s netdrive.exe, but it doesn&#8217;t support sftp.  Dokan&#8217;s SSHfs looked promising, but, alas, would not install on a Windows 7 environment (even in Vista compatibility mode).  For now, I have to resort to WinSCP for SSH-based editing, but it doesn&#8217;t come near the convenience of SSHfs.</li>
</ul>
<img src="http://techknack.net/?ak_action=api_record_view&id=226&type=feed" alt="" /><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/TechKnack?a=-m4CoGAvIu0:hG16KYya0XM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/TechKnack?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=-m4CoGAvIu0:hG16KYya0XM:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=-m4CoGAvIu0:hG16KYya0XM:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=-m4CoGAvIu0:hG16KYya0XM:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=-m4CoGAvIu0:hG16KYya0XM:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/TechKnack?a=-m4CoGAvIu0:hG16KYya0XM:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/TechKnack?i=-m4CoGAvIu0:hG16KYya0XM:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/TechKnack/~4/-m4CoGAvIu0" height="1" width="1"/>]]></content:encoded><description>For the past couple of weeks, I&amp;#8217;ve been using the Windows 7 beta.  A shocker, I know, but I&amp;#8217;ve been able to find replacements for most of the software I use everyday in Linux.  The biggest hangups I&amp;#8217;ve found so far are 1) lack of package manager (PLEASE, Microsoft, implement something to this [...]</description><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://techknack.net/linux-to-windows-program-replacements/feed/</wfw:commentRss><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">0</slash:comments><feedburner:origLink>http://techknack.net/linux-to-windows-program-replacements/</feedburner:origLink></item></channel></rss>
