<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8567731656382992063</id><updated>2012-01-19T23:31:26.580Z</updated><category term='ruby'/><category term='mailcap'/><category term='bin'/><category term='Imagemagick'/><category term='perl'/><category term='xsel'/><category term='psbook'/><category term='xargs'/><category term='irb'/><category term='lpoptions'/><category term='uniq'/><category term='sprunge'/><category term='html2text'/><category term='curl'/><category term='i3lock'/><category term='w3m'/><category term='sed'/><category term='vlock'/><category term='grammar'/><category term='mutt'/><category term='awk'/><category term='find'/><category term='git'/><category term='sensible-browser'/><category term='purple-remote'/><category term='FTP'/><category term='expr'/><category term='grep'/><category term='aptitude'/><category term='dlocate'/><category term='vim'/><category term='chmod'/><category term='guitar'/><category term='umask'/><category term='unison'/><category term='pandoc'/><category term='wget'/><category term='urxvt'/><category term='sort'/><category term='tab'/><category term='xml'/><category term='screen'/><category term='commandline'/><category term='MySQL'/><category term='SSH'/><category term='gdb'/><category term='xclip'/><category term='imgur'/><category term='mount'/><category term='Filezilla'/><category term='tr'/><category term='lynx'/><category term='bash'/><category term='chgrp'/><category term='tar'/><category term='which'/><category term='pdftops'/><category term='PHP'/><category term='vimperator'/><category term='xrandr'/><category term='lpr'/><category term='newgrp'/><category term='Textures'/><category term='bchunk'/><category term='zsh'/><category term='CD'/><category term='cdparanoia'/><category term='ncurses'/><category term='cat'/><category term='dpkg-query'/><category term='gmail'/><category term='zip'/><category term='svn'/><category term='xte'/><category term='xmousepos'/><title type='text'>trembits</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>39</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-6390983871109953853</id><published>2012-01-19T06:35:00.005Z</published><updated>2012-01-19T06:48:53.712Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='cdparanoia'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='bin'/><category scheme='http://www.blogger.com/atom/ns#' term='CD'/><category scheme='http://www.blogger.com/atom/ns#' term='mount'/><category scheme='http://www.blogger.com/atom/ns#' term='bchunk'/><title type='text'>Extracting audio from CD images</title><content type='html'>&lt;p&gt;I always wondered what the point was in cue/bin CD images compared to the ISO format, but never bothered to look it up.&lt;/p&gt;
&lt;p&gt;A couple of days ago I was looking for music from an old Playstation game (which I own but couldn't be bothered to track down). I knew the music was on the disc as CD audio since I'd copied it to tape a dozen years or more ago, so I found and downloaded an image of the game from the web.&lt;/p&gt;
&lt;p&gt;The thought then occurred -- how do I rip the music from this image? I usually use cdparanoia to get music off CDs, but could I point cdparanoia to a mounted CD image? The answer is no -- I would only be able to mount the data partition. You don't mount an audio CD before ripping music from it, cdparanoia just accesses the drive directly. Would I have to burn the image to a CD, then rip it back onto the machine? That's ridiculous.&lt;/p&gt;
&lt;p&gt;Meanwhile the download finished and I saw that it was bin/cue rather than ISO. This is when I looked it up. It seems obvious now, but an ISO is just the ISO-9660 filesystem -- the data part. As such, an ISO file can't include CD audio. The bin/cue format is needed in order to include the data on the separate tracks of the CD. Converting the bin/cue to ISO would have lost me all the music.&lt;/p&gt;
&lt;p&gt;So for future reference, extracting the audio (and the ISO at the same time) from bin/cue is as easy as this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bchunk -w wild9.bin wild9.cue wild9&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-w&lt;/code&gt; there makes it write any audio as wave files so I can then convert to Flac. I assume it'd write them as raw PCM otherwise, but I've no desire to check.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-6390983871109953853?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/6390983871109953853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2012/01/cd-images.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6390983871109953853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6390983871109953853'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2012/01/cd-images.html' title='Extracting audio from CD images'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-7620039897496330626</id><published>2012-01-11T00:05:00.004Z</published><updated>2012-01-11T00:42:50.265Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='gmail'/><title type='text'>Archiving mailing list messages but not replies to my own posts</title><content type='html'>&lt;p&gt;Sometimes I sign up to a mailinglist and want to see replies to my own threads but not have my inbox overtaken by everything else.&lt;/p&gt;
&lt;p&gt;I can set up a filter in Gmail to label the mailing list posts and archive them, obviously, but then replies to my own threads (or threads I've participated in) are not obvious.&lt;/p&gt;
&lt;p&gt;So let's make a more sophisticated filter, to match all messages with the relevant &lt;code&gt;List-ID&lt;/code&gt; header except those which mention my messages in their &lt;code&gt;References&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;After a few test queries it seems that search queries of the form &lt;code&gt;References:*@segnus&lt;/code&gt; matches messages replying to things I sent from my laptop. The negative version &lt;code&gt;-References:*@segnus&lt;/code&gt; appears to match the opposite. So it's not hard to add more negatives to avoid matching messages from my other machines and to build up a final query:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;List:django-users.googlegroups.com -References:*@segnus -References:*@t900 -References:*@perihelion&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This then goes in the "includes the words" box of the filter, and is set to apply a label and skip the inbox.&lt;/p&gt;
&lt;p&gt;It's possible that other people send messages with &lt;code&gt;Message-ID&lt;/code&gt; headers like mine and so I'd get some extra messages in my inbox, but I can live with that.&lt;/p&gt;
&lt;p&gt;This doesn't seem to work quite correctly on existing messages. I'm guessing this is because Gmail sees the messages in the thread before any of them referenced me -- my original message or the existing conversation before I turned up -- and those messages match the filter, leading Gmail to lump the rest of the conversation in with that positive match and archive the lot. Fingers crossed it'll work with fresh incoming messages, though -- I'll update this post when I confirm.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-7620039897496330626?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/7620039897496330626/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2012/01/archiving-mailing-list-messages-but-not.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7620039897496330626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7620039897496330626'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2012/01/archiving-mailing-list-messages-but-not.html' title='Archiving mailing list messages but not replies to my own posts'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-410131064601179133</id><published>2011-12-27T21:21:00.006Z</published><updated>2012-01-10T20:35:34.885Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='mutt'/><category scheme='http://www.blogger.com/atom/ns#' term='lynx'/><category scheme='http://www.blogger.com/atom/ns#' term='w3m'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='mailcap'/><category scheme='http://www.blogger.com/atom/ns#' term='html2text'/><category scheme='http://www.blogger.com/atom/ns#' term='pandoc'/><title type='text'>Viewing HTML in mutt</title><content type='html'>&lt;p&gt;I use mutt to read my email, and every now and then I get sent a message in HTML format with no plain text alternative. I don't like to load these files in a browser, since it'd go ahead and fetch any messages, run scripts and so on with potential privacy risks. In other words, a message from a dubious source might phone home and confirm my email address or track me or whatever, just from opening their HTML in my browser.&lt;/p&gt;
&lt;p&gt;So generally I just mind-parse the HTML. In more obfuscated cases (like the garbage output as newsletters by various websites) I manually pipe the message through lynx or similar.&lt;/p&gt;
&lt;p&gt;Then when I reply to the message I have to pipe it through again if I want to quote something other than the HTML code.&lt;/p&gt;
&lt;p&gt;I got fed up of this and looked for a solution. It consists of two parts -- changing up the entries in my mailcap file so that filtering the HTML to plain text is preferred to opening up a browser; and telling mutt to automatically filter text/html files using the rules it finds in the mailcap file. I've added some redundancy in to the mailcap entries so that it works both on my main machines (where I prefer pandoc since Markdown is nice to read, then I prefer lynx to either w3m or html2text, since lynx displays the links as references at the bottom) and on my phone (where only lynx is available).&lt;/p&gt;
&lt;p&gt;In ~/.mailcap:&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;text/html; pandoc -f html -t markdown; copiousoutput; description=HTML Text; test=type pandoc &amp;gt;/dev/null
text/html; lynx -stdin -dump -force_html -width 70; copiousoutput; description=HTML Text; test=type lynx &amp;gt;/dev/null
text/html; w3m -dump -T text/html -cols 70; copiousoutput; description=HTML Text; test=type w3m &amp;gt;/dev/null
text/html; html2text -width 70; copiousoutput; description=HTML Text; test=type html2text &amp;gt;/dev/null&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;In ~/.mutt/muttrc:&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;auto_view text/html&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now HTML is automatically piped through one of those programs to turn it into plain text, and when I reply the quoted text is the plain text version rather than the raw HTML.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-410131064601179133?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/410131064601179133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2011/12/viewing-html-in-mutt.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/410131064601179133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/410131064601179133'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2011/12/viewing-html-in-mutt.html' title='Viewing HTML in mutt'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-7297387656763397350</id><published>2011-12-07T09:01:00.002Z</published><updated>2011-12-07T09:02:19.279Z</updated><title type='text'>Different keys to push and pull to git repository</title><content type='html'>&lt;p&gt;I was getting fed up of typing in the password for my home server when pulling changes from a git repository up to the live server. But I'm not comfortable with putting my private key on the remote since other people have root. What to do?&lt;/p&gt;

&lt;p&gt;I made two new SSH key pairs. One with a passphrase and one without. I told gitosis, which handles permissions for the git repositories on my home server, to accept my main passphraseless key with read/write access, the passphrased one also with read/write access, and the new passphraseless one with read-only access. I then uploaded the private keys for the passphrased key and the new passphraseless key to the remote host.&lt;/p&gt;

&lt;p&gt;So even though they have one of my private keys, it's only good for read access to the repositories -- data which they already have anyway.&lt;/p&gt;

&lt;p&gt;To tell SSH it has multiple keys you edit the config file and add an &lt;code&gt;IdentityFile&lt;/code&gt; line for each key. But when connecting to the remote SSH server only the first acceptable key is tried. So if the passphraseless key is first everything will be fine when doing a read operation but gitosis will give a no permission error message when doing a write operation, and the other key won't be tried. If the key with the passphrase is first, the passphrase is asked for no matter whether it's a read or write operation.&lt;/p&gt;

&lt;p&gt;So here's the solution: pretend to git using the &lt;code&gt;pushurl&lt;/code&gt; option that we're pulling from and pushing to different hosts, then set up SSH to use different keys for these different hosts, but in fact then point them to the same host. Here's the configuration to illustrate.&lt;/p&gt;

&lt;p&gt;Configuration in repository/.git/config:&lt;/p&gt;

&lt;p&gt;&lt;pre&gt;[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    url = gitosis@example.com:repository.git
    pushurl = gitosis@rw.example.com:repository.git&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Configuration in ~/.ssh/config:&lt;/p&gt;

&lt;p&gt;&lt;pre&gt;Host example.com
IdentityFile ~/.ssh/id_rsa.ro

Host rw.example.com
IdentityFile ~/.ssh/id_rsa.rw
HostName example.com&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;So the dummy hostname &lt;code&gt;rw.example.com&lt;/code&gt; triggers SSH to use the passphrased private key at the correct hostname. A passphrase prompt appears when pushing but not when pulling.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-7297387656763397350?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/7297387656763397350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2011/12/different-keys-to-push-and-pull-to-git.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7297387656763397350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7297387656763397350'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2011/12/different-keys-to-push-and-pull-to-git.html' title='Different keys to push and pull to git repository'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-5780066088810940843</id><published>2011-04-06T18:05:00.006+01:00</published><updated>2011-04-06T18:15:19.970+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='zsh'/><category scheme='http://www.blogger.com/atom/ns#' term='chmod'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='newgrp'/><category scheme='http://www.blogger.com/atom/ns#' term='chgrp'/><category scheme='http://www.blogger.com/atom/ns#' term='umask'/><title type='text'>Automatically switch main group when logging in to a host</title><content type='html'>&lt;p&gt;I have accounts on couple of hosts where I don't have root powers, and on some of them I am working alongside others. We're in the same group, but our primary groups are named after ourselves. Sometimes we need to edit files the other person has made and it can be a pain if one of us forgets to set the permissions such that the other user can edit the file.&lt;/p&gt;
&lt;p&gt;Instead of having to remember to run &lt;code&gt;chgrp semsorgrid newfile&lt;/code&gt; and then &lt;code&gt;chmod g+w newfile&lt;/code&gt; every time we make a new file, we can change our primary group after logging in with &lt;code&gt;newgrp semsorgrid&lt;/code&gt; and set the file creation mask to give the group maximum privileges with &lt;code&gt;umask 002&lt;/code&gt;. But we still have to remember to do that when logging in.&lt;/p&gt;
&lt;p&gt;I tried putting those commands in our shell configuration files (my .zshrc and his .bashrc) but then when logging in new shells are spawned recursively. The solution is to check which group we're in and conditionally run &lt;code&gt;newgrp&lt;/code&gt; like this:&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;umask 007
if [ $(groups | awk '{print $1}') != "semsorgrid" ]; then
    exec newgrp semsorgrid
fi&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;This replaces the shell which was originally spawned with a new one with the primary group properly set, then when that shell initializes it skips the newgrp command since the primary group is already "semsorgrid".&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-5780066088810940843?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/5780066088810940843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2011/04/automatically-switch-main-group-when.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5780066088810940843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5780066088810940843'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2011/04/automatically-switch-main-group-when.html' title='Automatically switch main group when logging in to a host'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-3449684104813424193</id><published>2011-03-25T14:41:00.008Z</published><updated>2011-03-28T18:25:29.633+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lynx'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><title type='text'>Open online PHP reference from vim in a new split vim window</title><content type='html'>&lt;p&gt;Following &lt;a href="http://trembits.blogspot.com/2011/03/open-online-php-reference-from-vim.html"&gt;the last post&lt;/a&gt; I went a bit further. I haven't decided which solution out of that and the following I like best yet.&lt;/p&gt;

&lt;p&gt;This time I wanted the documentation to open in a new window within vim. That means grabbing the documentation HTML, cutting out what I don't want to see, rendering to plain text and putting the result in a new window.&lt;/p&gt;

&lt;p&gt;Since the documentation is given as XHTML, as long as it's valid the safest way to chop out the bits I don't want is by parsing it as XML. So since PHP is my quick hacking language of choice I cooked up the following script and saved it as ~/bin/phpman-text.&lt;/p&gt;

&lt;p&gt;&lt;pre&gt;#!/usr/bin/env php
&amp;lt;?php

if (!isset($_SERVER["argv"][1])) {
 fwrite(STDERR, "No keyword given\n");
 exit(1);
}

$xmlstring = @file_get_contents("http://php.net/" . urlencode($_SERVER["argv"][1]));
if ($xmlstring === false) {
 fwrite(STDERR, "Failed to fetch doc page\n");
 exit(2);
}

// remove default namespace
$xmlstring = preg_replace('%\bxmlns="[^"]*"%', "", $xmlstring);

$xml = @simplexml_load_string($xmlstring);
if ($xml === false) {
 fwrite(STDERR, "Failed to parse doc page\n");
 exit(3);
}

// get content div
$content = array_pop($xml-&amp;gt;xpath("//div[@id='content']"));

if (is_null($content)) {
 fwrite(STDERR, "Couldn't find div with ID 'content'\n");
 exit(4);
}

// remove nav bars
foreach ($content-&amp;gt;xpath("./div") as $nav)
 if (in_array("manualnavbar", explode(" ", $nav["class"])))
  simplexml_remove_node($nav);

// get new XML
$newxml = $content-&amp;gt;asXML();

// run lynx
$lynx = proc_open("lynx -dump -stdin", array(array("pipe", "r"), array("pipe", "w"), array("pipe", "w")), $pipes);
if (!is_resource($lynx)) {
 fwrite(STDERR, "Couldn't run lynx\n");
 exit(5);
}

// poke new XML to lynx's stdin
fwrite($pipes[0], $newxml);
fclose($pipes[0]);

// get lynx's stdout
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
$erroroutput = stream_get_contents($pipes[2]);
fclose($pipes[2]);

// close lynx
$returnval = proc_close($lynx);

// check return value
if ($returnval != 0) {
 fwrite(STDERR, "lynx exited with code $returnval -- error output follows.\n$erroroutput");
 exit(6);
}

exit(0);

function simplexml_remove_node($node) {
 $domnode = dom_import_simplexml($node);
 $domnode-&amp;gt;parentNode-&amp;gt;removeChild($domnode);
}

?&amp;gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Then, with help from the vim wiki, I came up with this, to be added to my .vimrc:&lt;/p&gt;

&lt;p&gt;&lt;pre&gt;function! OpenPhpFunction (keyword)
 exe "12new"
 exe "silent read !phpman-text ".substitute(a:keyword, "_", "-", "g")
 exe "set buftype=nofile bufhidden=delete filetype=php readonly"
 exe "1"
endfunction
autocmd FileType php map &amp;lt;buffer&amp;gt; K :call OpenPhpFunction('&amp;lt;C-r&amp;gt;&amp;lt;C-w&amp;gt;')&amp;lt;CR&amp;gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;This mostly works. It loads the PHP documentation into a temporary new buffer and sets the filetype to PHP so that bits of text in &lt;code&gt;&amp;lt;?php ?&amp;gt;&lt;/code&gt; tags are highlighted as PHP code. There are a few bad things -- some of the blocks of PHP code in the manual don't have the end tag and so highlighting continues beyond the end of the code, some of the manual pages (probably due to dodgy comments) aren't valid XML and so won't go through the script, and lynx's output isn't completely ideal.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-3449684104813424193?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/3449684104813424193/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2011/03/open-online-php-reference-from-vim-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/3449684104813424193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/3449684104813424193'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2011/03/open-online-php-reference-from-vim-in.html' title='Open online PHP reference from vim in a new split vim window'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-1511526899285288782</id><published>2011-03-09T17:18:00.005Z</published><updated>2011-03-09T17:23:17.568Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='sensible-browser'/><title type='text'>Open online PHP reference from vim</title><content type='html'>&lt;p&gt;Pressing K when over a keyword in vim usually opens the keyword's manpage. In Python it invokes &lt;code&gt;pydoc&lt;/code&gt; instead. I wanted it to open PHP's online documentation when I press it over a function name in PHP. Easy.&lt;/p&gt;
&lt;p&gt;I wrote a tiny shell script in my ~/bin directory first, phpman:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;#!/bin/sh&lt;br&gt;sensible-browser http://php.net/"$*"&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I then added to my .vimrc the line&lt;/p&gt;
&lt;p&gt;&lt;code&gt;autocmd FileType php set keywordprg=phpman&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Done. And using &lt;code&gt;sensible-browser&lt;/code&gt; has the added bonus that it'll run whatever my preferred graphical browser is when I am in X or a textmode browser when I'm not.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-1511526899285288782?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/1511526899285288782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2011/03/open-online-php-reference-from-vim.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1511526899285288782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1511526899285288782'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2011/03/open-online-php-reference-from-vim.html' title='Open online PHP reference from vim'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-5867885927240497753</id><published>2010-10-10T01:31:00.004+01:00</published><updated>2010-10-12T13:25:15.599+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='screen'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='unison'/><title type='text'>Merging files with unison and vimdiff -- update</title><content type='html'>&lt;p&gt;I've just updated my post from February about merging files with unison and vimdiff -- I figured out how to do so without launching a new terminal emulator. The solution uses screen. See &lt;a href="http://trembits.blogspot.com/2010/02/merging-unison-conflict-with-vim.html"&gt;the updated post&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-5867885927240497753?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/5867885927240497753/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/10/merging-files-with-unison-and-vimdiff.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5867885927240497753'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5867885927240497753'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/10/merging-files-with-unison-and-vimdiff.html' title='Merging files with unison and vimdiff -- update'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-576778534433190866</id><published>2010-08-30T15:28:00.003+01:00</published><updated>2010-08-30T15:31:27.617+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>Committing part of a file</title><content type='html'>&lt;p&gt;A fantastic feature of git I only found a few days ago is committing just part of a file. Usually I have the foresight to use &lt;code&gt;git stash&lt;/code&gt; but sometimes that doesn't occur or it's not practical for whatever reason, such as only realizing after the edits that the changes in a particular file should be two commits rather than one.&lt;/p&gt;
&lt;p&gt;When this happens you can interactively choose which parts of the changes to stage for committing. To do that:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git add -p file.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Git then shows each part of the patch in sequence, asking what you want to do with them. You can stage the change, not stage it, split it into smaller chunks, decide later or even edit the patch manually.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-576778534433190866?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/576778534433190866/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/08/committing-part-of-file.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/576778534433190866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/576778534433190866'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/08/committing-part-of-file.html' title='Committing part of a file'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-4084577308869723157</id><published>2010-08-24T13:46:00.005+01:00</published><updated>2010-08-24T18:01:19.595+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='psbook'/><category scheme='http://www.blogger.com/atom/ns#' term='lpoptions'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='lpr'/><category scheme='http://www.blogger.com/atom/ns#' term='pdftops'/><title type='text'>Options for lpr</title><content type='html'>&lt;p&gt;I keep on forgetting these so it's about time I wrote down the ones I use most often.&lt;/p&gt;
&lt;p&gt;You can list the current options (it definitely includes the options I've set to default and I'm not sure if it also shows all other default options or just those which the Cups Gui on my machine has set) with &lt;code&gt;lpoptions&lt;/code&gt;. I've set it to print full duplex by default in the past with &lt;code&gt;lpoptions -o sides=two-sided-long-edge&lt;/code&gt; so that shows up when I run &lt;code&gt;loptions&lt;/code&gt; and everything's printed double sided by default.&lt;/p&gt;
&lt;p&gt;Sometimes I don't want to print double sided and so I'll do &lt;code&gt;lpr flyer.pdf -o copies=8 -o sides=one-sided&lt;/code&gt; to print off a bunch, one per sheet. (Could also do &lt;code&gt;-#8&lt;/code&gt; as on option to lpr for eight copies but that requires escaping.)&lt;/p&gt;
&lt;p&gt;I can also choose a printer other than the default one such as the colour laser with &lt;code&gt;lpr poster.pdf -P renoir&lt;/p&gt;
&lt;p&gt;To do a quick printout of a PDF 2-up and reordered so it can just be folded up into a pamphlet fresh out of the printer I can do this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pdftops somebook.pdf - | psbook | lpr -o number-up=2 -o sides=two-sided-short-edge&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To print an A4 page on an A3 printer and fill the page I &lt;i&gt;should&lt;/i&gt; (not tested) be able to do this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lpr a4poster.pdf -o media=A3 -o fit-to-page&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;landscape&lt;/code&gt; option might be necessary, I guess depending on the input. I'll come back and confirm what works at some point.&lt;/p&gt;

&lt;dl&gt;

&lt;dt&gt;&lt;code&gt;copies=&lt;i&gt;num&lt;/i&gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Number of copies. Also possible to use &lt;code&gt;-#&lt;i&gt;num&lt;/i&gt;&lt;/code&gt; but that might need escaping.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;landscape&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Rotates 90 degrees but doesn't resize the page. Printing an A4 portrait PDF will not fit on A4 media but it doesn't complain, it just cuts off what doesn't fit.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;number-up=&lt;i&gt;num&lt;/i&gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;The number of pages to print on each side.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;number-up-layout=lrtb|lrbt|rltb|rlbt|tblr|tbrl|btlr|btrl&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Choose how the pages are laid out when printing multiple pages on a side.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;sides=one-sided|two-sided-long-edge|two-sided-short-edge&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Choose how duplex works -- how the paper is turned over before the next page is printed. &lt;code&gt;two-sided-long-edge&lt;/code&gt; is desirable for 1-up printing, &lt;code&gt;two-sided-short-edge&lt;/code&gt; for 2-up.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;media=A4|whatever&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Choose the media size. If the printer only does as large as A4 and you tell it to use A3 and &lt;code&gt;fit-to-page&lt;/code&gt; it'll print on A4, sideways and too big. Doesn't sound useful but it potentially is.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;page-set=odd|even&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Print only odd or even pages.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;outputorder=normal|reverse&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;This takes effect after &lt;code&gt;number-up&lt;/code&gt; so you still get 1234 on a page, not 4321&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;cpi=&lt;i&gt;charsperinch&lt;/i&gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;The number of characters per inch when printing plain text (default 10).&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;lpi=&lt;i&gt;linesperinch&lt;/i&gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;The number of lines per inch when printing plain text (default 6).&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;columns=&lt;i&gt;num&lt;/i&gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;The number of columns to format when printing plain text.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;page-(left|right|top|bottom)=&lt;i&gt;margin&lt;/i&gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;The margin sizes in points when printing plain text.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;Collate=true|false&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Whether or not to collate the output pages -- that is, print all pages of the first copy followed by all pages of the next. By default it won't -- it'll print all copies of page 1, then all copies of page 2 and so on.&lt;/dd&gt;

&lt;dt&gt;&lt;code&gt;page-ranges=&lt;i&gt;page&lt;/i&gt;,&lt;i&gt;page&lt;/i&gt;,&lt;i&gt;start&lt;/i&gt;-&lt;i&gt;end&lt;/i&gt;&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;Choose which pages to print. This takes effect after &lt;code&gt;number-up&lt;/code&gt; so the second page is 5678 when printing 4-up. They're always printed in ascending order, regardless of the order the ranges are given in.&lt;/dd&gt;

&lt;/dl&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-4084577308869723157?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/4084577308869723157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/08/options-for-lpr.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/4084577308869723157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/4084577308869723157'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/08/options-for-lpr.html' title='Options for lpr'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-5529156307615298356</id><published>2010-07-07T23:40:00.005+01:00</published><updated>2010-07-07T23:51:00.883+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cat'/><category scheme='http://www.blogger.com/atom/ns#' term='uniq'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='sort'/><category scheme='http://www.blogger.com/atom/ns#' term='tr'/><title type='text'>Nice bit of piping</title><content type='html'>&lt;p&gt;I just made a nice little pipeline. I love the Linux shell.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cat file | tr A-Z a-z | tr -c a-z "\n" | sort | uniq -c | sort -nr&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That gives a nice ordered count of the most used words in the file. It's pretty dumb -- it breaks words at anything which isn't an alphabetical character -- but it does the trick.&lt;/p&gt;
&lt;p&gt;The first tr changes uppercase characters to lowercase, the second changes anything which isn't alphabetic to a newline. Then the words are sorted alphabetically, then uniq counts successive identical lines and outputs the count with the word, then that list is sorted numerically in descending order. Lovely.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-5529156307615298356?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/5529156307615298356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/07/nice-bit-of-piping.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5529156307615298356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5529156307615298356'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/07/nice-bit-of-piping.html' title='Nice bit of piping'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-6417078324298244532</id><published>2010-06-23T16:42:00.002+01:00</published><updated>2010-06-23T16:45:51.529+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='xml'/><title type='text'>Make a single-line XML stream pretty in vim</title><content type='html'>&lt;p&gt;Usually I view XML in Firefox as that's the quickest way to get a nice tree I can explore. But when I might want to edit it until today I've been loading it into vim and then if necessary adding newlines manually or using a fairly dumb regex to do it for me. Today I made a slightly more sophisticated one.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:%s/&amp;gt;\s*&amp;lt;/&amp;gt;\r&amp;lt;/g&lt;/code&gt; adds the newlines in decent places and then &lt;code&gt;gg=G&lt;/code&gt; indents it all.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-6417078324298244532?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/6417078324298244532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/06/make-single-line-xml-stream-pretty-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6417078324298244532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6417078324298244532'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/06/make-single-line-xml-stream-pretty-in.html' title='Make a single-line XML stream pretty in vim'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-6836176304259145994</id><published>2010-06-23T11:45:00.007+01:00</published><updated>2010-06-23T13:26:19.190+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='expr'/><category scheme='http://www.blogger.com/atom/ns#' term='awk'/><category scheme='http://www.blogger.com/atom/ns#' term='xrandr'/><category scheme='http://www.blogger.com/atom/ns#' term='xte'/><category scheme='http://www.blogger.com/atom/ns#' term='xmousepos'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='grep'/><category scheme='http://www.blogger.com/atom/ns#' term='sed'/><title type='text'>Moving the pointer to the top left corner of the screen</title><content type='html'>&lt;p&gt;I wanted a keystroke to send the pointer to the top left of the screen. Usually I do this by flipping to the other screen and back but on my laptop (with only one screen) this doesn't work.&lt;/p&gt;
&lt;p&gt;The solution was to use xmousepos to get the mouse position (want to know which screen we're on) and xte to set it. These are part of the xautomation package in Ubuntu/Debian.&lt;/p&gt;
&lt;p&gt;But how big are the screens? I'm using xrandr and assuming that the resolutions listed are in order left to right and no screens are up or down or rotated or anything crazy like that.&lt;/p&gt;
&lt;p&gt;Here's my script.&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;&lt;code&gt;#!/bin/sh

# get current horizontal mouse position
mousex=$(xmousepos | awk '{print $1}')

# walk through widths of screens (assuming they're in order left to right) until 
# the mouse is on the current screen, building offset from left
offset=0
for width in $(xrandr | grep '*' | awk '{print $1}' | sed -r 's/x.*//'); do
 [ $mousex -lt $(expr $offset + $width) ] &amp;&amp; break
 offset=$(expr $offset + $width)
done

# move the pointer to the top left of the screen
xte "mousemove $offset 0"&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;xte can send other fake input to X too such as clicking, pressing and releasing keys and so on. Potentially very useful.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-6836176304259145994?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/6836176304259145994/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/06/sending-fake-input-to-x.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6836176304259145994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6836176304259145994'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/06/sending-fake-input-to-x.html' title='Moving the pointer to the top left corner of the screen'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-7865667552045932461</id><published>2010-06-09T17:01:00.005+01:00</published><updated>2010-06-23T13:26:48.372+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='purple-remote'/><category scheme='http://www.blogger.com/atom/ns#' term='i3lock'/><category scheme='http://www.blogger.com/atom/ns#' term='vlock'/><title type='text'>Giving standard input to a program run in a shell script</title><content type='html'>&lt;p&gt;I modified my "lock screen" bash script just now to also work when X isn't running -- that is, I'm on a virtual console or shelled into my machine.&lt;/p&gt;
&lt;p&gt;The script as it was did this: use purple-remote to set my status to "away" (if it was "available" -- otherwise don't touch it), then run i3lock to lock the X screen, then switch the screen off to save power, then when i3lock exits restore my IM status if it was changed.&lt;/p&gt;
&lt;p&gt;I added an &lt;code&gt;if [ $DISPLAY ]&lt;/code&gt; test to branch for X and non-X, putting vlock in the place of the i3lock and xset commands for the non-X branch. It didn't like this, though -- vlock complained that it wasn't a virtual console.&lt;/p&gt;
&lt;p&gt;In order to get it working all I had to do was redirect the script's standard input to vlock like this: &lt;code&gt;vlock -a &amp;lt;/dev/stdin&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-7865667552045932461?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/7865667552045932461/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/06/giving-standard-input-to-program-run-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7865667552045932461'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7865667552045932461'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/06/giving-standard-input-to-program-run-in.html' title='Giving standard input to a program run in a shell script'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-6173057573952692518</id><published>2010-05-25T16:56:00.002+01:00</published><updated>2010-06-01T10:46:01.701+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='lpr'/><category scheme='http://www.blogger.com/atom/ns#' term='xsel'/><title type='text'>Printing from the command line</title><content type='html'>&lt;p&gt;I just selected some instructions from a Pidgin window and then typed &lt;code&gt;xsel | lpr&lt;/code&gt; in a terminal. Fantastic.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-6173057573952692518?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/6173057573952692518/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/05/printing-from-command-line.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6173057573952692518'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/6173057573952692518'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/05/printing-from-command-line.html' title='Printing from the command line'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-5737538993903410595</id><published>2010-05-25T14:38:00.006+01:00</published><updated>2010-05-25T14:44:31.766+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='gdb'/><category scheme='http://www.blogger.com/atom/ns#' term='ncurses'/><title type='text'>Debugging an already-running process</title><content type='html'>&lt;p&gt;I was getting a segfault in &lt;a href="http://pms.sourceforge.net/"&gt;pms&lt;/a&gt;, an ncurses application I contribute to, so I restarted the program within gdb. But since the bug seems to be something to do with resizing the console window the segfault never appeared again -- gdb doesn't pass the resize signal (or perhaps the new dimensions) on to the program running within it.&lt;/p&gt;
&lt;p&gt;I Googled around for a way to run gdb in a separate console window from the ncurses application. It's rather simple: change to the directory containing the binary so it can find the right one to debug against and run &lt;code&gt;gdb -p $(pidof pms)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That'll attach gdb to the already running process and pause it. Type &lt;code&gt;continue&lt;/code&gt; in the gdb window and await the crash.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-5737538993903410595?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/5737538993903410595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/05/debugging-already-running-process.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5737538993903410595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5737538993903410595'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/05/debugging-already-running-process.html' title='Debugging an already-running process'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-9047522098027085426</id><published>2010-04-30T14:02:00.004+01:00</published><updated>2010-04-30T17:13:41.951+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='irb'/><title type='text'>Ruby dependencies</title><content type='html'>&lt;p&gt;After getting a message that a module which was definitely installed was not found, a colleague eventually had the idea of checking the require path by running &lt;code&gt;puts $:&lt;/code&gt; in irb. Sure enough there was a different module with the same name earlier in the path than the one gem had installed. I uninstalled Ubuntu's own libxml-ruby and libxml-ruby1.8 and all was well. Interestingly, though, one of these (I forget which) was required to install the gem to begin with -- I guess it needed some header it provided. There's no separate -dev package (at least in this old release of Ubuntu).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-9047522098027085426?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/9047522098027085426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/04/ruby-dependency-hell.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/9047522098027085426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/9047522098027085426'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/04/ruby-dependency-hell.html' title='Ruby dependencies'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-2050508522224324103</id><published>2010-02-09T18:12:00.005Z</published><updated>2010-02-09T18:18:42.062Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='dpkg-query'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='dlocate'/><category scheme='http://www.blogger.com/atom/ns#' term='which'/><category scheme='http://www.blogger.com/atom/ns#' term='aptitude'/><title type='text'>Which package did a particular file come from</title><content type='html'>&lt;p&gt;I sometimes find myself trying to figure out which package a particular file came from. It's usually a binary I want available on a different Debian-based machine and an &lt;code&gt;aptitude search binaryname&lt;/code&gt; isn't returning anything.&lt;/p&gt;
&lt;p&gt;I think I've done this a few times before with dpkg-query commands of some kind but it was never simple enough to remember. I Googled the problem again today and came up with &lt;a href="http://www.howtogeek.com/howto/ubuntu/using-ubuntu-what-package-did-this-file-come-from/"&gt;an article&lt;/a&gt; introducing dlocate. It just has to be installed (its package is helpfully called "dlocate") and then to find out where pdfimages came from I just have to type &lt;code&gt;dlocate pdfimages&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But that gave me three lines of output -- I have a python script called pdfimages.py which came from python-reportlab, a gzipped manpage which came with poppler-utils and the binary I was actually looking for, also from poppler-utils. So I could have made the query more specific by giving a full path to the file -- quickest way with a binary is of course using which: &lt;code&gt;dlocate $(which pdfimages)&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-2050508522224324103?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/2050508522224324103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/02/which-package-did-particular-file-come.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2050508522224324103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2050508522224324103'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/02/which-package-did-particular-file-come.html' title='Which package did a particular file come from'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-1428105284372972081</id><published>2010-02-04T13:30:00.008Z</published><updated>2010-10-10T01:30:43.971+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='screen'/><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='SSH'/><category scheme='http://www.blogger.com/atom/ns#' term='unison'/><title type='text'>Merging a unison conflict with vim</title><content type='html'>&lt;p&gt;I use unison to synchronize various files. If I've edited the same file on both ends I can pick a version to keep or, until now, I aborted, manually merged them with vimdiff or some other tool and then started synchronizing again.&lt;/p&gt;
&lt;p&gt;But unison is able to call an external tool to merge for you. I figured out how to get it to use vimdiff -- add the option &lt;code&gt;-merge "Name * -&gt; urxvt -e vimdiff CURRENT1 CURRENT2"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I've used urxvt rather than my usual urxvtcd because urxvtcd immediately forks and unison continues as soon as the process it runs exits. So why did I run a terminal emulator at all rather than just running vimdiff in the existing terminal? It won't display. Not sure why -- probably to do with it not realizing it has a virtual terminal to output on or something.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update on 2010-10-10:&lt;/strong&gt; I had another shot at figuring it out. I tried using screen -- "Must be connected to a terminal". Then I tried using &lt;code&gt;ssh -t&lt;/code&gt; and screen -- same issue (but I think the error was from SSH this time rather than screen). Finally I found an option in screen's manual page which can start a screen session detached but yet not fork. That'll do. So now in default.prf (which my other unison config files include) I have these two lines:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;merge = Name * -&gt; screen -DmS unisonmerge vimdiff CURRENT1 CURRENT2
merge = Name .* -&gt; screen -DmS unisonmerge vimdiff CURRENT1 CURRENT2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second line is there because I found just now that dotfiles weren't included when only the first line was present.&lt;/p&gt;
&lt;p&gt;So here's what happens when a merge is now requested. A new screen session is started with the session name "unisonmerge". That's started detached but the process doesn't fork as it usually would and so unison stops, waiting. We get the shell to stop the process and give us a prompt by pressing ^Z, then attach the new screen session with &lt;code&gt;screen -RS unisonmerge&lt;/code&gt;. The vimdiff instance is running in that -- we merge the files (modifying only the one we want to keep, which will end up on both machines) and then exit. The screen session ends and we're back to the prompt. Then we bring unison back to the foreground with &lt;code&gt;fg&lt;/code&gt;. Unison continues.&lt;/p&gt;
&lt;p&gt;So now I can run unison on a headless server over SSH, merging when necessary.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-1428105284372972081?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/1428105284372972081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/02/merging-unison-conflict-with-vim.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1428105284372972081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1428105284372972081'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/02/merging-unison-conflict-with-vim.html' title='Merging a unison conflict with vim'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-8156912703494199434</id><published>2010-01-14T14:10:00.003Z</published><updated>2010-01-15T15:22:42.870Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='zsh'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>Postpone current command and run another first in zsh</title><content type='html'>&lt;p&gt;Sometimes I am halfway through writing a command and realize I need to run something else first. Just now I was writing a git commit command and realized I should edit the TODO file first. So usually I either ^U to clear the command and type &lt;code&gt;vim TODO&lt;/code&gt; before writing the commit command again or maybe open a new terminal or maybe even ^A to go to the start of the line and add &lt;code&gt;vim TODO;&lt;/code&gt; to the beginning.&lt;/p&gt;
&lt;p&gt;But zsh has a buffer stack which comes in useful here. The default key binding for &lt;code&gt;push-line&lt;/code&gt; is ESC q. This pushes the current buffer (command in progress) onto the stack and clears the buffer, then pops from the stack next time the prompt is reached. So halfway through typing a commit command I do ESC q, type &lt;code&gt;vim TODO&lt;/code&gt;, make whatever changes and exit and I'm back at a commandline with my half-typed commit command restored.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-8156912703494199434?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/8156912703494199434/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/01/postpone-current-command-and-run.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8156912703494199434'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8156912703494199434'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/01/postpone-current-command-and-run.html' title='Postpone current command and run another first in zsh'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-1666021393952261717</id><published>2010-01-12T17:09:00.001Z</published><updated>2010-01-12T17:10:51.974Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Textures'/><category scheme='http://www.blogger.com/atom/ns#' term='tab'/><category scheme='http://www.blogger.com/atom/ns#' term='guitar'/><title type='text'>Transcription of Stream of Consciousness by Textures</title><content type='html'>&lt;p&gt;Forgot to post that I've transcribed &lt;a href="http://tremby.net/tabs/textures%20--%20stream%20of%20consciousness.tab"&gt;Stream of Consciousness&lt;/a&gt;. It has some crazy fast bits.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-1666021393952261717?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/1666021393952261717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/01/transcription-of-stream-of.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1666021393952261717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1666021393952261717'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/01/transcription-of-stream-of.html' title='Transcription of Stream of Consciousness by Textures'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-8202912388893141392</id><published>2010-01-12T17:01:00.002Z</published><updated>2010-01-12T17:08:44.692Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>git stash</title><content type='html'>&lt;p&gt;I'm currently working on a project using git as my version control system.&lt;/p&gt;
&lt;p&gt;Often I'm halfway through making some change or other and an unrelated bug becomes apparent. It's an easy fix and so I go ahead and fix it but then I can't make a clean commit. I found out today how to handle this.&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Save the work in progress before the unrelated bug is fixed and run &lt;code&gt;git stash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The source tree is now clean -- the unfinished changes have disappeared. Reload the file in vim and fix the small bug.&lt;/li&gt;
&lt;li&gt;Commit the patch&lt;/li&gt;
&lt;li&gt;Restore (and merge if necessary) the stashed tree with &lt;code&gt;git stash pop&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-8202912388893141392?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/8202912388893141392/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2010/01/git-stash.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8202912388893141392'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8202912388893141392'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2010/01/git-stash.html' title='git stash'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-7143662497254773338</id><published>2009-12-22T01:50:00.003Z</published><updated>2009-12-22T01:53:59.358Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Textures'/><category scheme='http://www.blogger.com/atom/ns#' term='tab'/><category scheme='http://www.blogger.com/atom/ns#' term='guitar'/><title type='text'>Transcription of Drive by Textures</title><content type='html'>&lt;p&gt;I learnt Denying Gravity the other day (but didn't transcribe it). Today I thought since I know that and Regenesis maybe I'd learn the whole album. Transcribed Drive just now -- it's &lt;a href="http://tremby.net/tabs/textures%20--%20drive.tab"&gt;up on my tabs page&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-7143662497254773338?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/7143662497254773338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/12/transcription-of-drive-by-textures.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7143662497254773338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7143662497254773338'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/12/transcription-of-drive-by-textures.html' title='Transcription of Drive by Textures'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-8426737095053011388</id><published>2009-12-16T15:34:00.005Z</published><updated>2009-12-22T01:50:23.779Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='zip'/><title type='text'>Vim can read zip files</title><content type='html'>&lt;p&gt;I accidentally opened a zip file with vim just now. It gives a directory listing just like when opening a directory. Fantastic.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-8426737095053011388?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/8426737095053011388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/12/vim-can-read-zip-files.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8426737095053011388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8426737095053011388'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/12/vim-can-read-zip-files.html' title='Vim can read zip files'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-1494444075679114695</id><published>2009-12-04T11:04:00.005Z</published><updated>2009-12-16T15:12:23.517Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='Textures'/><category scheme='http://www.blogger.com/atom/ns#' term='tab'/><category scheme='http://www.blogger.com/atom/ns#' term='guitar'/><title type='text'>Transcription of Old Days Born Anew by Textures</title><content type='html'>&lt;p&gt;I finished transcribing Old Days Born Anew by Textures to guitar tablature. It's up with my other plaintext tabs at &lt;a href="http://tremby.net/tabs"&gt;my tabs page&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-1494444075679114695?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/1494444075679114695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/12/transcription-of-old-days-born-anew-by.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1494444075679114695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/1494444075679114695'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/12/transcription-of-old-days-born-anew-by.html' title='Transcription of Old Days Born Anew by Textures'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-5470221697511864606</id><published>2009-11-25T14:02:00.003Z</published><updated>2009-11-25T14:11:40.595Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><title type='text'>Ranges in vim</title><content type='html'>&lt;p&gt;I hadn't used ranges in vim (other than &lt;code&gt;%&lt;/code&gt; -- the whole file) until now. Thought it was about time I found out how to use them.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.&lt;/code&gt; is the current line, &lt;code&gt;$&lt;/code&gt; is the last line. Numbers mean that line so &lt;code&gt;1&lt;/code&gt; is the first line in the file. That's the most important stuff -- the full list is at &lt;code&gt;:he range&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;They can be followed with &lt;code&gt;+&lt;/code&gt; or &lt;code&gt;-&lt;/code&gt; which adds or takes one from the line number, or the plus or minus can be followed with a number to add or subtract that many.&lt;/p&gt;
&lt;p&gt;Ranges can be just one line or &lt;code&gt;&lt;i&gt;from&lt;/i&gt;,&lt;i&gt;to&lt;/i&gt;&lt;/code&gt;. So the range I need to use right now, current line until the end of the file, is &lt;code&gt;.,$&lt;/code&gt; and so to do my replacement I type &lt;code&gt;:.,$s/"#ffbaba"/errorcolour/g&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-5470221697511864606?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/5470221697511864606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/11/ranges-in-vim.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5470221697511864606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5470221697511864606'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/11/ranges-in-vim.html' title='Ranges in vim'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-8617297198938480809</id><published>2009-11-18T11:24:00.006Z</published><updated>2009-11-18T11:29:56.203Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='FTP'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='wget'/><category scheme='http://www.blogger.com/atom/ns#' term='Filezilla'/><title type='text'>Get everything from an FTP server</title><content type='html'>&lt;p&gt;When there's no shell access (and so no SCP) FTP is unavoidable. I'd usually do this with a Gui FTP program like Filezilla but I thought it was about time I figured out how to do it on the command line.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wget&lt;/code&gt; does the trick but I noticed it got stuck once or twice and then didn't move. When that happened I just hit ^C to stop it and then ran again with the &lt;code&gt;c&lt;/code&gt; switch (continue). That switch doesn't hurt the first time either so I'll include it below:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wget -cr --user &lt;i&gt;username&lt;/i&gt; --password &lt;i&gt;password&lt;/i&gt; ftp://&lt;i&gt;server&lt;/i&gt;&lt;/code&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-8617297198938480809?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/8617297198938480809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/11/get-everything-from-ftp-server.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8617297198938480809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8617297198938480809'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/11/get-everything-from-ftp-server.html' title='Get everything from an FTP server'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-2092710138278791560</id><published>2009-10-29T17:54:00.004Z</published><updated>2011-03-25T18:02:04.219Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='sprunge'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='imgur'/><title type='text'>imgur</title><content type='html'>&lt;p&gt;It's a bit like sprunge -- a pastebin for images. I wrote a bash script similar to my sprunge one to upload an image (given by filename) and return (and put on the clipboard) its new URL. The URL to delete it is given on stderr.&lt;/p&gt;
&lt;pre&gt;
#!/bin/bash
apikey="&amp;lt;API KEY&amp;gt;"
if [ $# -lt 1 ]; then
 echo "no file specified" &amp;gt;&amp;2
 exit 127
fi
response=$(curl -F "key=$apikey" -F "image=@$1" http://imgur.com/api/upload.xml 2&amp;gt;/dev/null)
if [ $? -ne 0 ]; then
 echo "request failed" &amp;gt;&amp;2
 exit 1
fi
url=$(echo $response | sed -r 's/.*&amp;lt;original_image&amp;gt;(.*)&amp;lt;\/original_image&amp;gt;.*/\1/')
deleteurl=$(echo $response | sed -r 's/.*&amp;lt;delete_page&amp;gt;(.*)&amp;lt;\/delete_page&amp;gt;.*/\1/')
echo $url
echo "Delete page: $deleteurl" &amp;gt;&amp;2
if [ $DISPLAY ]; then
 { which xsel &amp;gt;/dev/null 2&amp;gt;/dev/null &amp;&amp; echo -n $url | xsel; } \
  || { which xclip &amp;gt;/dev/null 2&amp;gt;/dev/null &amp;&amp; echo -n $url | xclip; } \
  || echo "haven't copied to the clipboard: no xsel or xclip" &amp;gt;&amp;2
else
 echo "haven't copied to the clipboard: no \$DISPLAY" &amp;gt;&amp;2
fi
&lt;/pre&gt;

&lt;p&gt;Update: a later version of this script is now featured on the imgur site. At the moment it's at the bottom of &lt;a href="http://imgur.com/tools/"&gt;the tools page&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-2092710138278791560?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/2092710138278791560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/10/imgur.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2092710138278791560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2092710138278791560'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/10/imgur.html' title='imgur'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-2805123635016311415</id><published>2009-10-29T14:47:00.002Z</published><updated>2009-10-29T14:48:45.647Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='tar'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>Package a source tree ignoring .svn directories</title><content type='html'>&lt;p&gt;Simple: &lt;code&gt;tar cvzf py-translate-svnr8.tar.gz --exclude=.svn py-translate&lt;/code&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-2805123635016311415?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/2805123635016311415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/10/package-source-tree-ignoring-svn.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2805123635016311415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2805123635016311415'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/10/package-source-tree-ignoring-svn.html' title='Package a source tree ignoring .svn directories'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-5456493400114285021</id><published>2009-10-26T10:17:00.005Z</published><updated>2009-10-26T10:26:08.846Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='grammar'/><title type='text'>Swim/swam/swum</title><content type='html'>&lt;p&gt;I couldn't figure out just now whether to use "swam" or "swum".&lt;/p&gt;
&lt;p&gt;Googled around a bit for something authoritative and came up with &lt;a href="http://books.google.co.uk/books?id=SSsaHgcuDM0C&amp;pg=PA25&amp;lpg=PA25&amp;dq=grammar+swim+swam+swum+oxford&amp;source=bl&amp;ots=KsDhuxSUcG&amp;sig=YxwykHsUxiV3GgSZgeaV6LS8dx8&amp;hl=en&amp;ei=Y3XlSoipN9vKjAfIoOmhBA&amp;sa=X&amp;oi=book_result&amp;ct=result&amp;resnum=3&amp;ved=0CBYQ6AEwAg#v=onepage&amp;q=&amp;f=false"&gt;a page from &lt;i&gt;A semantic approach to English grammar&lt;/i&gt; by Robert M W Dixon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This morning I swam. I have swum today. I swam already. I've already swum. Should have known it'd be that simple, really -- it seems if you use the verb "have" as an auxiliary you use the "swum" form. The technical term for when you use "swum" seems to be the "previous perfective aspect", both in the present ("I have swum") and past ("I had swum") -- otherwise use "swam" (or "swim", but it's obvious when to use that).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-5456493400114285021?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/5456493400114285021/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/10/swimswamswum.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5456493400114285021'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5456493400114285021'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/10/swimswamswum.html' title='Swim/swam/swum'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-4194090440292456941</id><published>2009-10-16T09:46:00.003+01:00</published><updated>2009-10-16T10:06:04.721+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='sed'/><title type='text'>Print a particular line of a file</title><content type='html'>&lt;p&gt;Saw this on &lt;a href="http://www.commandlinefu.com/commands/view/3802/to-print-a-specific-line-from-a-file"&gt;commandlinefu&lt;/a&gt; today.&lt;/p&gt;
&lt;p&gt;I'd been doing &lt;code&gt;command | head -5 | tail -1&lt;/code&gt; to print the 5th line but it makes more sense to use sed:&lt;br&gt;&lt;code&gt;command | sed -n 5p&lt;/code&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-4194090440292456941?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/4194090440292456941/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/10/print-particular-line-of-file.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/4194090440292456941'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/4194090440292456941'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/10/print-particular-line-of-file.html' title='Print a particular line of a file'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-2402743819824893307</id><published>2009-10-15T16:01:00.002+01:00</published><updated>2009-10-15T16:05:43.420+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vimperator'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='urxvt'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>Yanking URLs from the console</title><content type='html'>&lt;p&gt;It's a pain to select URLs manually from the console with the mouse for pasting into Vimperator or whatever other program. On the PMS IRC channel today smeea mentioned a perl extension for urxvt which scans for URLs and allows them to be yanked or put straight into a browser. It's on &lt;a href="http://www.jukie.net/~bart/blog/20070503013555"&gt;another fella called Bart's blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Very handy. Now I can ignore the mouse even more.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-2402743819824893307?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/2402743819824893307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/10/yanking-urls-from-console.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2402743819824893307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2402743819824893307'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/10/yanking-urls-from-console.html' title='Yanking URLs from the console'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-8276096255487339048</id><published>2009-10-06T15:47:00.005+01:00</published><updated>2009-10-06T16:21:52.107+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xargs'/><category scheme='http://www.blogger.com/atom/ns#' term='Imagemagick'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='find'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>find and xargs</title><content type='html'>&lt;p&gt;How hard can it be to send a bunch of filenames from find as arguments to a shell command? It turns out that it's much more difficult than it should be if you want to add another argument after the list of filenames.&lt;/p&gt;
&lt;p&gt;The problem today was that my colleague and I wanted to find all images with a particular name and send them to Imagemagick to put them all together. The Imagemagick command for that looks like&lt;br&gt;
&lt;code&gt;montage -size 1 -geometry 66x50 image*.png montage.png&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The issue is that Imagemagick isn't flexible. The output filename has to be the last argument and the input files can't be passed by standard input. The contenders for solving the problem are &lt;code&gt;find&lt;/code&gt;'s &lt;code&gt;-exec&lt;/code&gt; option and &lt;code&gt;xargs&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the first thing we tried was this:&lt;br&gt;
&lt;code&gt;find . -name "image*.png" -exec montage -size 1 -geometry 66x50 {} montage.png +&lt;/code&gt;&lt;br&gt;
but this gives a "missing argument to `exec'" error -- &lt;code&gt;find&lt;/code&gt; doesn't allow extra arguments after the placeholder.&lt;/p&gt;
&lt;p&gt;Then we tried using &lt;find&gt;xargs&lt;/find&gt;:&lt;br&gt;
&lt;code&gt;find . -print0 -name "image*.png" | xargs -0 -I {} montage -size 1 -geometry 66x50 {} montage.png&lt;/code&gt;&lt;br&gt;
but this runs the command once for each input unless we increase &lt;code&gt;xargs&lt;/code&gt;'s &lt;code&gt;-L&lt;/code&gt; (max input arguments per command) option back up from 1 (which is set by using the &lt;code&gt;-I&lt;/code&gt; option -- no idea why) to some arbitrary large number. This seems like a bad way to do things.&lt;/p&gt;
&lt;p&gt;The solution we came up with was to add an extra argument before the stream gets to &lt;code&gt;xargs&lt;/code&gt;:&lt;br&gt;
&lt;code&gt;find . -print0 -name "image*.png" | cat - &lt;(echo -ne "montage.png\0") | xargs -0 montage -size 1 -geometry 66x50&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is using process substitution to put montage and a null character as a file descriptor input to &lt;code&gt;cat&lt;/code&gt;, which adds that to the end of the first argument, its standard input.&lt;/p&gt;
&lt;p&gt;It's still a mess -- any better solutions?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-8276096255487339048?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/8276096255487339048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/10/find-and-xargs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8276096255487339048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8276096255487339048'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/10/find-and-xargs.html' title='find and xargs'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-7864865652648903932</id><published>2009-09-29T16:35:00.003+01:00</published><updated>2009-09-29T16:46:10.735+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>Herestrings in the shell</title><content type='html'>&lt;p&gt;Like heredoc syntax there is also herestring.&lt;/p&gt;
&lt;p&gt;For a heredoc you'd do something like
&lt;pre&gt;
cat &amp;gt;file &amp;lt;&amp;lt;EOF
This is a file
all this text will get written to the file
This dir contains
$(ls)
EOF
&lt;/pre&gt;
or, if expansion (command, parameter and arithmetic) isn't wanted, quote the word at the start: &lt;code&gt;cat &amp;gt;file &amp;lt;&amp;lt;"EOF"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Herestrings are a quicker way than using echo and piping to a command -- for instance to get torrent info I can do &lt;code&gt;ssh server deluge --ui=console &amp;lt;&amp;lt;&amp;lt;info&lt;/code&gt; rather than &lt;code&gt;echo info | ssh server deluge --ui=console&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-7864865652648903932?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/7864865652648903932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/09/herestrings-in-shell.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7864865652648903932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/7864865652648903932'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/09/herestrings-in-shell.html' title='Herestrings in the shell'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-2084647572030678442</id><published>2009-09-09T02:45:00.004+01:00</published><updated>2009-09-09T02:56:39.904+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='SSH'/><title type='text'>Put SSH in the background</title><content type='html'>&lt;p&gt;While looking for something unrelated in SSH's manpage I came across something I didn't know before: when logged into a remote machine various escape sequences are supported to talk to the local end of the connection.&lt;/p&gt;
&lt;p&gt;It's possible to do fancy things like add port forwardings or remove existing ones but I think the only one I'll now use commonly is ~^Z, which is like sending ^Z to your local shell and backgrounds the SSH process. You get your local shell back, can run whatever commands you need and then foreground SSH again with &lt;code&gt;fg&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The escape character (~ is the default) has to come after a newline character to be recognized as special.&lt;/p&gt;
&lt;p&gt;I've entirely forgotten what I was actually looking for in the manual.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-2084647572030678442?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/2084647572030678442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/09/put-ssh-in-background.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2084647572030678442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2084647572030678442'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/09/put-ssh-in-background.html' title='Put SSH in the background'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-8350968799540022649</id><published>2009-09-08T14:18:00.006+01:00</published><updated>2011-04-06T18:20:24.240+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='zsh'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><title type='text'>Brackets in bash</title><content type='html'>&lt;p&gt;Was just having a problem with a command which worked in the Z shell not working in bash and had to brush up on my knowledge of the difference between different types of brackets. Found a very useful &lt;a href="http://ss64.com/bash/syntax-brackets.html"&gt;article&lt;/a&gt; which explains it all.&lt;/p&gt;
&lt;p&gt;I was trying to use braces to group commands but have them execute in the current shell. Z shell liked it but bash didn't. The solution to my problem was to put whitespace around the braces and to terminate the last command with a semicolon before the closing brace. Apparently this stuff is required in bash for "historical reasons".&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-8350968799540022649?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/8350968799540022649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/09/brackets-in-bash.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8350968799540022649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/8350968799540022649'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/09/brackets-in-bash.html' title='Brackets in bash'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-2791636293782675443</id><published>2009-09-01T16:18:00.012+01:00</published><updated>2009-10-26T10:32:12.904Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='curl'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='sprunge'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='xsel'/><category scheme='http://www.blogger.com/atom/ns#' term='xclip'/><title type='text'>Sprunge</title><content type='html'>&lt;p&gt;&lt;a href="http://sprunge.us/"&gt;Sprunge&lt;/a&gt; is lovely. It's a pastebin but without all the crap. Here's my sprunge script:&lt;/p&gt;
&lt;pre&gt;
#!/bin/bash
url=$(curl -F 'sprunge=&lt;-' -H "Expect: " http://sprunge.us 2&gt;/dev/null)
if [ $? -ne 0 ]; then
 echo "request failed" &gt;&amp;2
 exit 1
fi
if [ $# -gt 0 ]; then
 url="$url?$1"
fi
echo $url
if [ $DISPLAY ]; then
 { which xsel &gt;/dev/null 2&gt;/dev/null &amp;&amp; echo -n $url | xsel; } \
  || { which xclip &gt;/dev/null 2&gt;/dev/null &amp;&amp; echo -n $url | xclip; } \
  || echo "haven't copied to the clipboard: no xsel or xclip" &amp;gt;&amp;2
else
 echo "haven't copied to the clipboard: no \$DISPLAY" &amp;gt;&amp;2
fi
&lt;/pre&gt;
&lt;p&gt;The empty Expect header is to get around a bug which rears its head if the request goes through a proxy like Squid.&lt;p&gt;
&lt;p&gt;This will pastebin the standard input and output the URL at which you can retrieve it. Additionally, if it can it'll put it on the X selection so you can paste it into IRC or whatever.&lt;/p&gt;
&lt;p&gt;Give a &lt;a href="http://pygments.org/docs/lexers/"&gt;filetype&lt;/a&gt; as an argument if you want the code highlighted. All that does is add a parameter to the URL which is printed and put on the clipboard.&lt;/p&gt;
&lt;p&gt;So I might run &lt;code&gt;some command | sprunge&lt;/code&gt; to show the command's output to someone or &lt;code&gt;sprunge python &amp;lt;script.py&lt;/code&gt; to show a buggy Python file to someone.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-2791636293782675443?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/2791636293782675443/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/09/sprunge.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2791636293782675443'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/2791636293782675443'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/09/sprunge.html' title='Sprunge'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-350912618931156648</id><published>2009-09-01T15:45:00.009+01:00</published><updated>2011-03-14T12:59:45.912Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='vim'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='xsel'/><category scheme='http://www.blogger.com/atom/ns#' term='xclip'/><title type='text'>Clipboard from the command line -- xclip versus xsel</title><content type='html'>&lt;p&gt;Until last week I'd been using &lt;a href="https://sourceforge.net/projects/xclip/"&gt;xclip&lt;/a&gt; to set and retrieve the X selection and X clipboard. But the syntax is a bit nasty -- to output the clipboard it's &lt;code&gt;xclip -selection clipboard -o&lt;/code&gt;. (I have since found out, thanks to the comments on this post, that it's not quite so bad, the options can be abbreviated as long as it's non-ambiguous.) I came across &lt;a href="http://www.vergenet.net/~conrad/software/xsel/"&gt;xsel&lt;/a&gt; and decided to give it a go. They both do roughly the same thing, but which is better?&lt;p&gt;
&lt;p&gt;Put simply, xsel is a little more advanced and has a nicer syntax.&lt;/p&gt;
&lt;p&gt;They can both be installed easily in Ubuntu with &lt;code&gt;sudo aptitude install xclip&lt;/code&gt; and &lt;code&gt;sudo aptitude install xsel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First of all, it's important to realize there are three selections X keeps track of: the primary X selection, the secondary X selection (but I've never actually seen this used) and the X clipboard.&lt;p&gt;
&lt;p&gt;The (primary) X selection is what is set when some text is selected and is usually retrieved when the middle mouse button is clicked.&lt;/p&gt;
&lt;p&gt;The X clipboard is what most programs set when the copy command is used, and what they retrieve from the owner when the paste command is used.&lt;/p&gt;
&lt;p&gt;I used the word "owner" there – in the X environment the program in which text is copied/selected takes "ownership" of the X clipboard or X selection. When another program asks for the contents of the clipboard or selection, X tells it which program is the owner and then the querying program asks the owning program for the contents directly. This means when the owning program exits the contents of the clipboard and selection are lost, unless something else takes over selection ownership. There exist system-tray type programs for this purpose.&lt;/p&gt;
&lt;p&gt;Both xsel and xclip use the primary X selection by default. To set it:&lt;br/&gt;
&lt;code&gt;some command | xsel&lt;/code&gt;&lt;br/&gt;
or&lt;br/&gt;
&lt;code&gt;some command | xclip&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The same so far. To retrieve it:&lt;br/&gt;
&lt;code&gt;xsel&lt;/code&gt;&lt;br/&gt;
or&lt;br/&gt;
&lt;code&gt;xclip -o&lt;/code&gt;&lt;br/&gt;
No output flag is required for xsel since it detects what stdin and stdout are and picks input or output mode automatically. See its &lt;a href="http://www.vergenet.net/~conrad/software/xsel/xsel.1x.html"&gt;manpage&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To set the X clipboard, xsel's command is much shorter:&lt;br/&gt;
&lt;code&gt;some command | xsel -b&lt;/code&gt;&lt;br/&gt;
as compared to xclip's equivalent&lt;br/&gt;
&lt;code&gt;some command | xclip -selection clipboard&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And to retrieve the X clipboard&lt;br/&gt;
&lt;code&gt;xsel -b&lt;/code&gt;&lt;br/&gt;
versus&lt;br/&gt;
&lt;code&gt;xclip -selection clipboard -o&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I often want to put the contents of a file into the selection. I do that with &lt;code&gt;xsel &amp;lt;file&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Other features&lt;/h2&gt;
&lt;p&gt;I've never found any of xclip's other features useful – it can be set to hold the selection until it has been retrieved &lt;i&gt;n&lt;/i&gt; times and then delete it, for instance. On the other hand, some of xsel's options look very useful. Some examples: &lt;code&gt;some command | xsel -a&lt;/code&gt; appends text to the selection, &lt;code&gt;xsel -d&lt;/code&gt; deletes the selection and &lt;code&gt;xsel -k&lt;/code&gt; keeps (takes over) the selection so that it won't be lost when the program which set it exits.&lt;/p&gt;
&lt;h2&gt;Getting at the selection and clipboard from vim&lt;/h2&gt;
&lt;p&gt;In vim you can read and write to both of these by using the "+ (X clipboard) and "* (X selection) registers. For example, to copy the current line to the X clipboard type &lt;code&gt;"+yy&lt;/code&gt; or to paste from the X selection type &lt;code&gt;"*p&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-350912618931156648?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/350912618931156648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2009/09/clipboard-from-command-line-xclip.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/350912618931156648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/350912618931156648'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2009/09/clipboard-from-command-line-xclip.html' title='Clipboard from the command line -- xclip versus xsel'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8567731656382992063.post-5598321495960666148</id><published>2008-09-25T19:45:00.001+01:00</published><updated>2009-09-01T16:10:04.328+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><title type='text'>Datetimes in PHP and MySQL</title><content type='html'>&lt;p&gt;Until a few days ago, I’d never properly figured out exactly how to handle datetimes in PHP – whether to use &lt;code&gt;&lt;a href="http://php.net/date"&gt;date&lt;/a&gt;()&lt;/code&gt; or &lt;code&gt;&lt;a href="http://php.net/gmdate"&gt;gmdate&lt;/a&gt;()&lt;/code&gt; in various situations, the best thing to do when dates are going to or coming from a database and how to handle timezones.&lt;/p&gt;
&lt;p&gt;I just sort of winged it, mostly using &lt;code&gt;date()&lt;/code&gt; and hoping timezones would just behave. But here’s what I’ve now found out.&lt;/p&gt;

&lt;h2&gt;Storing to and retrieving from MySQL&lt;/h2&gt;
&lt;p&gt;Datetimes are stored in MySQL very simply – the timezone doesn’t affect how they are stored or retrieved at all. And just so we’re clear, the timezone isn’t stored alongside a date either – only the familiar YYYY-MM-DD HH:MM:SS format. For this reason, it’s clearly essential to always store datetimes in a single timezone, and the best contender is &lt;a href="http://en.wikipedia.org/wiki/UTC"&gt;UTC&lt;/a&gt; since this gets around &lt;a href="http://en.wikipedia.org/wiki/Daylight_saving_time"&gt;daylight saving time&lt;/a&gt; issues and is… well, universal. (In fact, Unix timestamps aren’t true representations of UTC, but they’re damn close.)&lt;/p&gt;
&lt;p&gt;Whether to store these UTC datetimes as &lt;a href="http://en.wikipedia.org/wiki/Unix_timestamp"&gt;Unix timestamps&lt;/a&gt; (number of seconds since the start of 1970, UTC) or in columns of &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/datetime.html"&gt;the MySQL datetime datatype&lt;/a&gt; is up to you. I personally opt to use datetime fields since I often dig around with a commandline MySQL client – to understand the timestamps I’d have to put them through MySQL’s &lt;code&gt;FROM_UNIXTIME()&lt;/code&gt; every time.&lt;/p&gt;

&lt;p&gt;So given a Unix timestamp, perhaps generated by PHP’s &lt;code&gt;&lt;a href="http://php.net/time"&gt;time&lt;/a&gt;()&lt;/code&gt; or &lt;code&gt;&lt;a href="http://php.net/strtotime"&gt;strtotime&lt;/a&gt;()&lt;/code&gt;, you need to give it to MySQL in the format it wants, &lt;code&gt;Y-m-d H:i:s&lt;/code&gt;. You don’t want MySQL to store the the time in your local timezone, so use &lt;code&gt;gmdate()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get it back you’ll almost certainly want to put it through &lt;code&gt;strtotime()&lt;/code&gt; since a timestamp is the easiest form in which to manipulate a datetime in PHP. But just sticking the date string from the database through that function will make PHP interpret it as being in the server’s current timezone. This is no good if the server is not always in UTC, so specify the timezone: &lt;code&gt;strtotime($datestring . " UTC")&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Timezones in MySQL&lt;/h2&gt;
&lt;p&gt;As noted above, MySQL doesn’t care about timezones when storing or retrieving datetimes. The only time MySQL cares about timezones is when certain built-in date functions are used – &lt;code&gt;NOW()&lt;/code&gt;, for instance, returns the current datetime in the currently set timezone.&lt;/p&gt;
&lt;p&gt;I don’t actually use MySQL’s date functions very often, preferring to let PHP do the work. Check &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html"&gt;the MySQL manual&lt;/a&gt; to find out about the UTC functions if you need them, but the most important are &lt;code&gt;UTC_TIMESTAMP()&lt;/code&gt;, which returns the current datetime as a string, and &lt;code&gt;UNIX_TIMESTAMP()&lt;/code&gt;, which returns a Unix timestamp, funnily enough.&lt;/p&gt;
&lt;p&gt;What if you need to use &lt;code&gt;NOW()&lt;/code&gt; to get the current local datetime, but the server isn’t using the timezone you want? You can &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/time-zone-support.html"&gt;override the timezone&lt;/a&gt; which will be used for that session with a query such as &lt;code&gt;SET time_zone = '+01:00';&lt;/code&gt;. Set the timezone as soon as you connect to the database to whatever timezone PHP is in (info on setting that below) with &lt;code&gt;&lt;a href="http://php.net/mysql_query"&gt;mysql_query&lt;/a&gt;("SET time_zone = '" . date("P") . "';");&lt;/code&gt; soon after your &lt;code&gt;&lt;a href="http://php.net/mysql_connect"&gt;mysql_connect&lt;/a&gt;()&lt;/code&gt; call.&lt;/p&gt;

&lt;h2&gt;Timezones and daylight saving time in PHP&lt;/h2&gt;
&lt;p&gt;In order to show dates in suitable local time, it’s important to set the timezone. You can set the timezone which suits the site best (for instance a UK business would use UK time) or allow the user to choose their own. You could even try to detect the user’s location and apply a timezone based on that, but in my experience this is dodgy at best.&lt;/p&gt;
&lt;p&gt;I have some UK-centric sites hosted on a server in the USA and so before I generate any output I set the timezone with &lt;code&gt;&lt;a href="http://php.net/date_default_timezone_set"&gt;date_default_timezone_set&lt;/a&gt;("Europe/London");&lt;/code&gt; – the supported timezones are shown at &lt;a href="http://php.net/timezones"&gt;php.net/timezones&lt;/a&gt;. Daylight saving time is worked into it – use the timezone of a city which observes the daylight saving time rules you want your site to observe and everything will be handled for you. If you want to force a particular timezone with no daylight saving, there are suitable fixed timezones under the &amp;ldquo;Others&amp;rdquo; header such as UTC and Etc/GMT+5.&lt;/p&gt;
&lt;p&gt;One thing which I found very confusing at first is how PHP handles daylight saving time when you give it a datetime in the past or future. It turns out I needn’t have been confused – maybe it’s blindingly obvious, but it just didn’t click with me – PHP simply uses the state of daylight saving time at whatever date it is considering. For example, given a London timezone and a couple of timestamps &lt;code&gt;$xmas2008 = strtotime("2008-12-25 08:00"); $birthday = strtotime("1986-07-25 noon");&lt;/code&gt;, if you put them through &lt;code&gt;date()&lt;/code&gt; you get a &lt;a href="http://en.wikipedia.org/wiki/GMT"&gt;GMT&lt;/a&gt; datetime for &lt;code&gt;$xmas2008&lt;/code&gt; and a &lt;a href="http://en.wikipedia.org/wiki/British_Summer_Time"&gt;BST&lt;/a&gt; datetime for &lt;code&gt;$birthday&lt;/code&gt;. You can verify this by outputting a full-format date including an indication of timezone.&lt;/p&gt;

&lt;p&gt;What this means is that given a correctly set timezone, the output of &lt;code&gt;date()&lt;/code&gt; given a correct Unix timestamp will always make sense, no matter when the timestamp was stored, what date it represents nor what the current date is. As an illustration, say you publish a blog post just after midnight once a week all year. Even though the state of daylight saving time changed twice (if you’re in an area which observes DST, at least), the blog posts will always show as just after midnight. If you then change the timezone to a region at roughly the same longitude but which doesn’t observe DST (Iceland is an example of an always-GMT region), you’ll see that some of your blog posts show as just after 11pm – these are the ones you made during DST – and the rest still show as just after midnight. Use &lt;code&gt;date()&lt;/code&gt; with a format including the timezone to see for yourself.&lt;/p&gt;

&lt;h2&gt;User-entered datetimes&lt;/h2&gt;
&lt;p&gt;The only thing left is taking datetimes from user input. The best option here is to feed the user input through &lt;code&gt;strtotime()&lt;/code&gt; without a timezone suffix, and hence have PHP interpret the time in whatever timezone applied (or will apply) on the given date. If you allow your users to set a custom timezone, this means the datetimes will (should) be interpreted exactly as the user intends. The other option would be to demand UTC input, but not everyone would know, if the date is a few months away, whether to shift the date by an hour or not and, if so, in which direction to shift it.&lt;/p&gt;

&lt;h2&gt;To sum up&lt;/h2&gt;
&lt;ul&gt;
 &lt;li&gt;Set suitable timezones in both PHP and MySQL with &lt;code&gt;date_default_timezone_set("Europe/London"); mysql_query("SET time_zone = '" . date("P") . "';");&lt;/code&gt;&lt;/li&gt;
 &lt;li&gt;Store and manipulate datetimes in PHP as Unix timestamps, converting them for display with &lt;code&gt;date()&lt;/code&gt; only when necessary. I like to give HTML datetimes a &lt;code&gt;title&lt;/code&gt; attribute with a full-format date such as &lt;code&gt;Y-m-d H:i:s P&lt;/code&gt; (you can use &lt;code&gt;T&lt;/code&gt; instead of &lt;code&gt;P&lt;/code&gt; to show the timezone’s abbreviation rather than its offset from UTC) so things are totally clear.&lt;/li&gt;

 &lt;li&gt;To store a datetime in MySQL, store it as a timestamp (integer) directly or store the output of &lt;code&gt;gmdate("Y-m-d H:i:s", $timestamp)&lt;/code&gt; to a column with the datetime datatype – &lt;code&gt;gmdate()&lt;/code&gt; rather than &lt;code&gt;date()&lt;/code&gt; to ensure a UTC datetime is stored&lt;/li&gt;
 &lt;li&gt;To retrieve a datetime stored in a datetime column, use &lt;code&gt;strtotime($datestring . " UTC")&lt;/code&gt; to ensure it is &lt;i&gt;interpreted&lt;/i&gt; as UTC&lt;/li&gt;

 &lt;li&gt;Put datetime input from users directly into &lt;code&gt;strtotime()&lt;/code&gt; to get a timestamp, then store or display it as above&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope this helps someone who is as confused as I was. If you have any other datetime-related tips, go ahead and leave a comment.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8567731656382992063-5598321495960666148?l=trembits.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trembits.blogspot.com/feeds/5598321495960666148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://trembits.blogspot.com/2008/09/datetimes-in-php-and-mysql.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5598321495960666148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8567731656382992063/posts/default/5598321495960666148'/><link rel='alternate' type='text/html' href='http://trembits.blogspot.com/2008/09/datetimes-in-php-and-mysql.html' title='Datetimes in PHP and MySQL'/><author><name>Bart Nagel</name><uri>http://www.blogger.com/profile/09322287750886186240</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
