<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>gooli.org</title>
	<atom:link href="http://www.gooli.org/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.gooli.org/blog</link>
	<description>on software development and related issues</description>
	<pubDate>Mon, 26 May 2008 08:17:28 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Wrong keyboard layout</title>
		<link>http://www.gooli.org/blog/wrong-keyboard-layout/</link>
		<comments>http://www.gooli.org/blog/wrong-keyboard-layout/#comments</comments>
		<pubDate>Mon, 26 May 2008 08:16:44 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/wrong-keyboard-layout/</guid>
		<description><![CDATA[How many times have you typed an entire URL looking at your keyboard only to lift your eyes and see that you&#8217;ve used the wrong keyboard layout? How many times have you typed your email into some obscure form on the internet only to find that you&#8217;ve typed some unintelligible gibberish in Hebrew, Russian or [...]]]></description>
			<content:encoded><![CDATA[<p>How many times have you typed an entire URL looking at your keyboard only to lift your eyes and see that you&#8217;ve used the wrong keyboard layout? How many times have you typed your email into some obscure form on the internet only to find that you&#8217;ve typed some unintelligible gibberish in Hebrew, Russian or Farsi?</p>
<p>Well, no more! <a href="http://www.gooli.org/blog/" target="_blank">Recaps</a>, that tiny utility that lets you switch your keyboard layout using the almost forgotten <span style="font-weight: bold">Capslock</span> key, can now fix text you&#8217;ve typed with the wrong language selected. Just hit <span style="font-weight: bold">Ctrl-Capslock</span> as soon as you discover your mistake and the text will be replaced with what you actually intended to type. I&#8217;ve only tested it with Hebrew and Russian, the languages I personally use on a regular basis, but it should work with any language supported by Windows. Let me know if it doesn&#8217;t.</p>
<p><a href="http://technorati.com/tag/layout" class="performancingtags" rel="tag"></a></p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/wrong-keyboard-layout/&amp;title=Wrong+keyboard+layout" title="Add 'Wrong keyboard layout' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'Wrong keyboard layout' to Del.icio.us" alt="Add 'Wrong keyboard layout' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/wrong-keyboard-layout/&amp;title=Wrong+keyboard+layout" title="Add 'Wrong keyboard layout' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'Wrong keyboard layout' to digg" alt="Add 'Wrong keyboard layout' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/wrong-keyboard-layout/" title="Add 'Wrong keyboard layout' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'Wrong keyboard layout' to Technorati" alt="Add 'Wrong keyboard layout' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/wrong-keyboard-layout/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Digging into Python&#8217;s PYC files</title>
		<link>http://www.gooli.org/blog/digging-into-pythons-pyc-files/</link>
		<comments>http://www.gooli.org/blog/digging-into-pythons-pyc-files/#comments</comments>
		<pubDate>Fri, 25 Jan 2008 12:20:23 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Python]]></category>

		<category><![CDATA[Testuff]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/digging-into-pythons-pyc-files/</guid>
		<description><![CDATA[One of the first things we needed to do when we started working on Testuff, was to figure out how are we going to update the installed desktop clients. This is one of those problems that seems to usually fall under the NIH syndrome, and like many others before me, I invented my own scheme. [...]]]></description>
			<content:encoded><![CDATA[<p>One of the first things we needed to do when we started working on <a href="http://www.testuff.com">Testuff</a>, was to figure out how are we going to update the installed desktop clients. This is one of those problems that seems to usually fall under the <a href="http://en.wikipedia.org/wiki/Not_Invented_Here">NIH</a> syndrome, and like many others before me, I invented my own scheme. The gist of it is a <a href="http://download.testuff.com/release/version.xml">version.xml</a> file that sits alongside the setup file for the newest release and looks something like this:</p>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;update-info version="0.8.0[1212]&#8220;&gt;
    &lt;update file=&#8221;TestuffSetup.exe&#8221; from-version=&#8221;all&#8221;/&gt;
    &lt;update file=&#8221;TestuffUpdate.exe&#8221; from-version=&#8221;0.7.1[1110]&#8220;/&gt;
    &lt;md5hashes&gt;
        &lt;file md5=&#8221;3a23dd6eff6fd6c1d0fbfcbfb0d57221&#8243; path=&#8221;async.pyc&#8221;/&gt;
        &lt;file md5=&#8221;0d1ea490a18c65cec7ba8715b5ea9e69&#8243; path=&#8221;atexit.pyc&#8221;/&gt;
        &lt;file md5=&#8221;166723a4330a98b573119326fc689322&#8243; path=&#8221;base64.pyc&#8221;/&gt;
        &lt;file md5=&#8221;01c1bda049936de570ed922424c057a8&#8243; path=&#8221;BeautifulSoup.pyc&#8221;/&gt;
    &lt;/md5hashes&gt;
&lt;/update-info&gt;</pre>
<p>When the Testuff client launches, it gets the version.xml file from the server and compares its version to the version attribute of the <em>update-info</em> tag. If the client&#8217;s version is wrong, it checks the <em>update</em> tags to see which update it should download and install. We generate two separate setup files - one to update the most recent version to the new one called and another to update all the other (older) versions.</p>
<p>Aside from the info about which version of the client should use which update file, version.xml also contains the MD5 hashes for each file in the distribution. That might seem like a lot of wasted space and time, but it&#8217;s actually there for a very good reason. When our setup building script is creating <em>TestuffUpdate.exe</em>, it too downloads version.xml from our server. It then tries to determine which files have changed or have been added since the last version by comparing the MD5 hashes in version.xml to the the hashes of the actual files that have been generated by the build. Any file that is different is added to the update so we can be sure we haven&#8217;t missed any essential component in the update.</p>
<p>Recently I discovered that our update files are much larger than they should be. We release a new version with just a couple of fixes in a single module, and the size of the update is half the size of the full install. As it turned out, that most of the PYC files were marked as changed and added to the update. That didn&#8217;t seem right, especially for things like threading.pyc, which is a Python module that shouldn&#8217;t change unless you upgrade to a different version of Python, which we didn&#8217;t (still stuck at 2.4.4 I&#8217;m afraid). That got me curious enough to go digging in the, apparently undocumented, binary structure of the PYC files.</p>
<blockquote><p>This module contains functions that can read and write Python values in a binary  format. The format is specific to Python, but independent of machine  architecture issues (e.g., you can write a Python value to a file on a PC,  transport the file to a Sun, and read it back there). <strong>Details of the format are  undocumented on purpose</strong>; it may change between Python versions (although it  rarely does).</p></blockquote>
<p>The first thing I did was compare the two threading.pyc files - the one from the current distribution and the one just generated by the build script. The result showed there was difference in only two bytes:</p>
<pre>D:GooliDevTempcompare&gt;fc /b threading-old.pyc threading-new.pyc
Comparing files threading-old.pyc and threading-new.pyc
00000004: 6E CE
00000005: F7 6A</pre>
<p>Only two bytes differ, and they are right at the beginning of the file? That looks suspiciously like a version or a timestamp in the file header. Since the PYC file structure is undocumented, I went looking for the details in Python&#8217;s source code, but the answer was actually closer to home - in the <a href="http://docs.python.org/lib/compiler.html">compiler</a> package. A file called pycodegen.py in Python\Lib\compiler contains the following code:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;"><span style="color: #ff7700;font-weight:bold;">def</span> getPycHeader<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; mtime = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">getmtime</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">filename</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; mtime = <span style="color: #dc143c;">struct</span>.<span style="color: black;">pack</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;&amp;lt;i&#8217;</span>, mtime<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">MAGIC</span> + mtime</div>
<p>So, the PYC header file contains a magic number that identifies the Python release and the modification time of the original source file as the number of seconds since the epoch. That shouldn&#8217;t be a problem - the threading module hasn&#8217;t changed and should have the same timestamp. But as we&#8217;ve seen, the PYC files were different. How can that be?</p>
<p>Acting on a hunch, I wrote a short script to read the header from the PYC file and print the embedded date:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>, <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">struct</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> timedef print_internal_date<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; f = <span style="color: #008000;">open</span><span style="color: black;">&#40;</span>filename, <span style="color: #483d8b;">&quot;rb&quot;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; data = f.<span style="color: black;">read</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">8</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; mtime = <span style="color: #dc143c;">struct</span>.<span style="color: black;">unpack</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;&amp;lt;i&quot;</span>, data<span style="color: black;">&#91;</span><span style="color: #ff4500;">4</span>:<span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #dc143c;">time</span>.<span style="color: black;">asctime</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">time</span>.<span style="color: black;">gmtime</span><span style="color: black;">&#40;</span>mtime<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>print_internal_date<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;threading-old.pyc&quot;</span><span style="color: black;">&#41;</span><br />
print_internal_date<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;threading-new.pyc&quot;</span><span style="color: black;">&#41;</span></div>
<p>Which printed the following results:</p>
<pre>Mon Mar 13 22:51:26 2006
Mon Mar 13 12:51:26 2006</pre>
<p>Notice anything odd about them? They are <em>exactly</em> 10 hours apart. At first I thought I might actually be looking at two different versions of threading.py, but the chances of two edits being exactly 10 hours apart right down to the second is practically non-existent. It had to be something with time zones. I live and work in Israel, which is at GMT+2:00. The default timezone for Windows is Pacific time, which is GMT-8:00. Exactly 10 hours apart. However, no matter how I tweak the Regional Settings on my computer, all the PYC files I generate here have the same timestamp. Perhaps it has to do with the timezone you have set when you install Python. If I ever find out, I&#8217;ll let you know.</p>
<p>But that wasn&#8217;t the point of this post. The point was to figure out what PYC files look inside and we did that, at least in part - they start with a magic number that is different for each Python version (check out the comments in <a href="http://svn.python.org/view/python/tags/r244/Python/import.c?rev=52384&amp;view=markup">import.c</a>), and they have an embedded timestamp of the source code they got generated from after that. The rest is generated by the marshal module and can be read by it to get the code objects and the global data in the module.</p>
<p>Another thing to be learned from this is that we really should always build the Testuff client on the same machine, which is why I&#8217;m heading to the office right now to burn a copy of the VMWare image I created with everything needed to build Testuff. We got a new version with a couple of important fixes to our Mantis support to release today.</p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/digging-into-pythons-pyc-files/&amp;title=Digging+into+Python%26%238217%3Bs+PYC+files" title="Add 'Digging into Python&#8217;s PYC files' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'Digging into Python&#8217;s PYC files' to Del.icio.us" alt="Add 'Digging into Python&#8217;s PYC files' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/digging-into-pythons-pyc-files/&amp;title=Digging+into+Python%26%238217%3Bs+PYC+files" title="Add 'Digging into Python&#8217;s PYC files' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'Digging into Python&#8217;s PYC files' to digg" alt="Add 'Digging into Python&#8217;s PYC files' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/digging-into-pythons-pyc-files/" title="Add 'Digging into Python&#8217;s PYC files' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'Digging into Python&#8217;s PYC files' to Technorati" alt="Add 'Digging into Python&#8217;s PYC files' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/digging-into-pythons-pyc-files/feed/</wfw:commentRss>
		</item>
		<item>
		<title>A very simple Win+R enhancement</title>
		<link>http://www.gooli.org/blog/a-very-simple-winr-enhancement/</link>
		<comments>http://www.gooli.org/blog/a-very-simple-winr-enhancement/#comments</comments>
		<pubDate>Mon, 07 Jan 2008 11:26:47 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/a-very-simple-winr-enhancement/</guid>
		<description><![CDATA[I use the Win+R combination to run almost all the applications I use. Hit Win+R, type firefox, and Firefox runs. Hit Win+R, type winword, and Word starts. Hit Win+R&#8230;, well you get the idea.
The Run dialog is great to quickly access applications you use, but it&#8217;s only usable for programs that put their folder in [...]]]></description>
			<content:encoded><![CDATA[<p>I use the Win+R combination to run almost all the applications I use. Hit Win+R, type firefox, and Firefox runs. Hit Win+R, type winword, and Word starts. Hit Win+R&#8230;, well you get the idea.</p>
<p>The Run dialog is great to quickly access applications you use, but it&#8217;s only usable for programs that put their folder in the PATH environment variable, which not all programs do. I&#8217;ve been adding programs I liked to the PATH for years when a friend of mine showed me a neat trick. You create a folder called Shortcuts somewhere (say C:\Shortcuts) and put it <em>at the start</em> of your PATH environment variable and then create shortcuts to your favorite programs in that folder. All those shortcuts are now available to you via using Win+R. A nice bonus is the fact that you can name the shortcuts any way you like. I usually have a www.lnk file pointing to Firefox, note.lnk pointing to Notepad and word.lnk pointing to Word.</p>
<p>There&#8217;s only one thing missing here to make this trick into an fully featured program launcher. We need a way to quickly create shortcuts to programs, preferably by right-clicking on them in Windows Explorer. You can do that without installing anything using a couple of obscure Windows features - the <a href="http://support.microsoft.com/kb/310270">Send To</a> menu in the right-click menu of each file and the fact that Windows can run *.vbs files with VBScript code in it using something called <a href="http://en.wikipedia.org/wiki/Windows_Script_Host">WSH</a>.</p>
<p>Here&#8217;s the source of a script that creates a link to whichever was passed to it in the command line (executable, folder, etc.).</p>
<div class="ch_code_container" style="font-family: monospace;height:300px;">SHORTCUTS_PATH = <span style="color: #ff0000;">&quot;C:\Shortcuts&quot;</span></p>
<p><span style="color: #b1b100;">If</span> WScript.<span style="color: #66cc66;">Arguments</span>.<span style="color: #b1b100;">Count</span> &lt; <span style="color: #cc66cc;">1</span> <span style="color: #b1b100;">Then</span><br />
&nbsp; &nbsp; WScript.<span style="color: #66cc66;">Echo</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;Missing parameters&quot;</span><span style="color: #66cc66;">&#41;</span><br />
<span style="color: #b1b100;">Else</span><br />
&nbsp; &nbsp; <span style="color: #808080;">&#8216; create shorcut name</span><br />
&nbsp; &nbsp; commandPath = WScript.<span style="color: #66cc66;">Arguments</span><span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; dot = <span style="color: #b1b100;">InStrRev</span><span style="color: #66cc66;">&#40;</span>commandPath, <span style="color: #ff0000;">&quot;.&quot;</span><span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">If</span> dot = <span style="color: #cc66cc;">0</span> <span style="color: #b1b100;">Then</span> dot = <span style="color: #b1b100;">Len</span><span style="color: #66cc66;">&#40;</span>commandPath<span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; slash = <span style="color: #b1b100;">InStrRev</span><span style="color: #66cc66;">&#40;</span>commandPath, <span style="color: #ff0000;">&quot;\&quot;</span><span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">If</span> slash = <span style="color: #cc66cc;">0</span> <span style="color: #b1b100;">Then</span> slash = <span style="color: #cc66cc;">1</span><br />
&nbsp; &nbsp; commandDir = <span style="color: #b1b100;">Mid</span><span style="color: #66cc66;">&#40;</span>commandPath, <span style="color: #cc66cc;">1</span>, slash<span style="color: #cc66cc;">-1</span><span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; shortcutName = <span style="color: #b1b100;">Mid</span><span style="color: #66cc66;">&#40;</span>commandPath, slash<span style="color: #cc66cc;">+1</span>, dot-slash<span style="color: #cc66cc;">-1</span><span style="color: #66cc66;">&#41;</span></p>
<p>&nbsp; &nbsp; <span style="color: #808080;">&#8216; ask for shortcut name</span><br />
&nbsp; &nbsp; shortcutName = <span style="color: #b1b100;">InputBox</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;Enter shortcut name&quot;</span>, <span style="color: #ff0000;">&quot;Shortcut&quot;</span>, shortcutName<span style="color: #66cc66;">&#41;</span></p>
<p>&nbsp; &nbsp; <span style="color: #808080;">&#8216; create shortcut</span><br />
&nbsp; &nbsp; shortcutPath = SHORTCUTS_PATH &amp; <span style="color: #ff0000;">&quot;\&quot;</span> &amp; shortcutName &amp; <span style="color: #ff0000;">&quot;.lnk&quot;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">Set</span> WshShell = WScript.<span style="color: #b1b100;">CreateObject</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">&quot;WScript.Shell&quot;</span><span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">Set</span> oShellLink = WshShell.<span style="color: #66cc66;">CreateShortcut</span><span style="color: #66cc66;">&#40;</span>shortcutPath<span style="color: #66cc66;">&#41;</span><br />
&nbsp; &nbsp; oShellLink.<span style="color: #66cc66;">TargetPath</span> = commandPath<br />
&nbsp; &nbsp; oShellLink.<span style="color: #66cc66;">WindowStyle</span> = <span style="color: #cc66cc;">1</span><br />
&nbsp; &nbsp; oShellLink.<span style="color: #66cc66;">IconLocation</span> = commandPath &amp; <span style="color: #ff0000;">&quot;, 0&quot;</span><br />
&nbsp; &nbsp; oShellLink.<span style="color: #66cc66;">WorkingDirectory</span> = commandDir<br />
&nbsp; &nbsp; oShellLink.<span style="color: #66cc66;">Save</span><br />
<span style="color: #b1b100;">End</span> <span style="color: #b1b100;">If</span></div>
<p>The Send To menu is actually a folder located at C:\Documents and Settings\&lt;YourUser&gt;\SendTo. This folder contains links to various programs and you can add shortcuts to your own programs there. When you right-click a file, and choose one of the Send To items, the corresponding program is called, and given the name of the file you right-clicked as a command line parameter. So, if you put a link to the script above in the Send To folder, you&#8217;ll be able to easily create links in the C:\Shortcuts folder.</p>
<p>In case you lost me somewhere along the way, and in case I ever need to set this up myself again, here are the instructions to set things up:</p>
<ol>
<li>Create C:\Shortcuts</li>
<li>Create C:\Shortcuts\create_shortcut.vbs and paste the above code into it.</li>
<li>Add C:\Shortcuts <em>to the start</em> of your PATH environment variable (Win+Pause &gt; Advanced &gt; Environment Variables).</li>
<li>Hit Win+R, type sendto, hit Enter. That should get you to your Send To folder.</li>
<li>Create a shortcut here to C:\Shortcuts\create_shortcut.vbs and call it Shortcuts.</li>
</ol>
<p>To add a program to the launcher:</p>
<ol>
<li>Right click a program or a folder (you can do it even on programs in your Start Menu).</li>
<li>Send To &gt; Shortcuts.</li>
<li>In the dialog change the shortcut name to something short and catchy.</li>
<li>Test it : Hit Win+R, type the shortcut name and hit Enter.</li>
</ol>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/a-very-simple-winr-enhancement/&amp;title=A+very+simple+Win%2BR+enhancement" title="Add 'A very simple Win+R enhancement' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'A very simple Win+R enhancement' to Del.icio.us" alt="Add 'A very simple Win+R enhancement' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/a-very-simple-winr-enhancement/&amp;title=A+very+simple+Win%2BR+enhancement" title="Add 'A very simple Win+R enhancement' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'A very simple Win+R enhancement' to digg" alt="Add 'A very simple Win+R enhancement' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/a-very-simple-winr-enhancement/" title="Add 'A very simple Win+R enhancement' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'A very simple Win+R enhancement' to Technorati" alt="Add 'A very simple Win+R enhancement' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/a-very-simple-winr-enhancement/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Right to left (BiDi) support in Firefox</title>
		<link>http://www.gooli.org/blog/right-to-left-bidi-support-in-firefox-3/</link>
		<comments>http://www.gooli.org/blog/right-to-left-bidi-support-in-firefox-3/#comments</comments>
		<pubDate>Mon, 31 Dec 2007 15:46:12 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/right-to-left-bidi-support-in-firefox-3/</guid>
		<description><![CDATA[Whenever I install Firefox on a new computer, I find myself hitting Ctrl-Shift-X to switch the text box direction to now avail. Unfortunately the way to turn this option on is quite hidden.
You have to navigate to about:config, type bidi in the filter box, and double click the line that says bidi.browser.ui. That will set [...]]]></description>
			<content:encoded><![CDATA[<p>Whenever I install Firefox on a new computer, I find myself hitting Ctrl-Shift-X to switch the text box direction to now avail. Unfortunately the way to turn this option on is quite hidden.</p>
<p>You have to navigate to <a href="about:config">about:config</a>, type <em>bidi</em> in the filter box, and double click the line that says <em>bidi.browser.ui</em>. That will set its value to <em>true</em>. All you have to do now is restart Firefox and the View-&gt;Switch Page Direction and Edit-&gt;Switch Text Direction menu items will be added along with the Ctrl-Shift-X shortcut.</p>
<p>Hope this helps some poor soul someday.</p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/right-to-left-bidi-support-in-firefox-3/&amp;title=Right+to+left+%28BiDi%29+support+in+Firefox" title="Add 'Right to left (BiDi) support in Firefox' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'Right to left (BiDi) support in Firefox' to Del.icio.us" alt="Add 'Right to left (BiDi) support in Firefox' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/right-to-left-bidi-support-in-firefox-3/&amp;title=Right+to+left+%28BiDi%29+support+in+Firefox" title="Add 'Right to left (BiDi) support in Firefox' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'Right to left (BiDi) support in Firefox' to digg" alt="Add 'Right to left (BiDi) support in Firefox' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/right-to-left-bidi-support-in-firefox-3/" title="Add 'Right to left (BiDi) support in Firefox' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'Right to left (BiDi) support in Firefox' to Technorati" alt="Add 'Right to left (BiDi) support in Firefox' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/right-to-left-bidi-support-in-firefox-3/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Win32: GetFocus across process boundries</title>
		<link>http://www.gooli.org/blog/win32-getfocus-across-process-boundries/</link>
		<comments>http://www.gooli.org/blog/win32-getfocus-across-process-boundries/#comments</comments>
		<pubDate>Sat, 29 Dec 2007 18:10:55 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/win32-finding-the-focused-windows/</guid>
		<description><![CDATA[I&#8217;ve been using Miranda for all my chats for a few years now, since the time it was a nightmare to install and configure. The chief reason I like it is the fact that it supports Hebrew amazingly well, via the TabSRMM plugin. You can even set the right-to-left settings for the chat window on [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been using <a href="http://www.miranda-im.org/">Miranda</a> for all my chats for a few years now, since the time it was a nightmare to install and configure. The chief reason I like it is the fact that it supports Hebrew amazingly well, via the <a href="http://addons.miranda-im.org/details.php?action=viewfile&amp;id=1401">TabSRMM</a> plugin. You can even set the right-to-left settings for the chat window on a person-by-person basis.</p>
<p>However, the latest version of Miranda broke my language switching gizmo, <a href="http://www.gooli.org/blog/recaps/">Recaps</a>. Every once in a while the CapsLock key would stop switching the language in the Miranda chat window, and even restarting Recaps didn&#8217;t help. The basic thing Recaps does upon detecting that CapsLock has been pressed is send an <a href="http://msdn2.microsoft.com/en-us/library/ms632630.aspx">WM_INPUTLANGCHANGEREQUEST</a> message the the current foreground window, which is obtained via the <a href="http://msdn2.microsoft.com/en-us/library/ms633505.aspx">GetForegroundWindow</a> Win32 API function. Apparently I overlooked the fact that the MSDN specifically says the aforementioned message is sent to the window that <em>has the keyboard focus</em>, which I clear wasn&#8217;t doing. GetForegroundWindow just gets you the current top-level window, not the actual control that has the keyboard focus.</p>
<p>All I had to do was just replace the GetForegroundWindow call something that gets the window that has the focus and be done with it. And <a href="http://msdn2.microsoft.com/en-us/library/ms646294%28VS.85%29.aspx">GetFocus</a> seemed to be just what I needed. Only there was a catch:</p>
<blockquote><p>The <strong>GetFocus</strong> function retrieves the handle to the window that has the keyboard focus, <em>if the window is attached to the calling thread&#8217;s message queue</em>.</p></blockquote>
<p>That isn&#8217;t good enough of course as the whole point was finding out which window had the focus when that window belonged to a different application, running in a different process, in a different thread. There are <a href="http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c5767/">a few techniques</a> to inject code into a remote process, but they all require creating a separate DLL and tricking the target process into loading it either via <a href="http://msdn2.microsoft.com/en-us/library/ms644990.aspx">hooks</a> or using the <a href="http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c5767/">CreateRemoteThread/LoadLibrary</a> trick. I was getting ready to dive into that dark abyss when I stumbled upon the <a href="http://msdn2.microsoft.com/en-us/library/ms681956%28VS.85%29.aspx">AttachThreadInput</a> function:</p>
<blockquote><p>Windows created in different threads typically process input independently of each other. That is, they have their own input states (<em>focus</em>, active, capture windows, key state, queue status, and so on), and they are not synchronized with the input processing of other threads. By using the <strong>AttachThreadInput </strong>function, a thread can attach its input processing to another thread. This also allows threads to share their input states, so they can call the SetFocus function to set the keyboard focus to a window of a different thread. This also allows threads to get key-state information. These capabilities are not generally possible.</p></blockquote>
<p>So, all I had to do was call AttachThreadInput to connect my main UI thread to the thread responsible for the current foreground window, call GetFocus to find out which window has the focus, and then call AttachThreadInput again to give the control of the input back to the original thread.</p>
<p>Here&#8217;s the code to do that:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">HWND RemoteGetFocus<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span><br />
<span style="color: #000000;">&#123;</span><br />
&nbsp; &nbsp; HWND hwnd = GetForegroundWindow<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;<br />
&nbsp; &nbsp; DWORD remoteThreadId = GetWindowThreadProcessId<span style="color: #000000;">&#40;</span>hwnd, <span style="color: #0000ff;">NULL</span><span style="color: #000000;">&#41;</span>;<br />
&nbsp; &nbsp; DWORD currentThreadId = GetCurrentThreadId<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;<br />
&nbsp; &nbsp; AttachThreadInput<span style="color: #000000;">&#40;</span>remoteThreadId, currentThreadId, <span style="color: #0000ff;">TRUE</span><span style="color: #000000;">&#41;</span>;<br />
&nbsp; &nbsp; HWND focused = GetFocus<span style="color: #000000;">&#40;</span><span style="color: #000000;">&#41;</span>;<br />
&nbsp; &nbsp; AttachThreadInput<span style="color: #000000;">&#40;</span>remoteThreadId, currentThreadId, <span style="color: #0000ff;">FALSE</span><span style="color: #000000;">&#41;</span>;<br />
&nbsp; &nbsp; <span style="color: #0000ff;">return</span> focused;<br />
<span style="color: #000000;">&#125;</span></div>
<p>This function will only work if called from a thread that has created a message queue, which is the case for a standard Win32 main thread that runs in a message loop.</p>
<p>Miranda and Recaps now play well together.</p>
<p>Yay!</p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/win32-getfocus-across-process-boundries/&amp;title=Win32%3A+GetFocus+across+process+boundries" title="Add 'Win32: GetFocus across process boundries' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'Win32: GetFocus across process boundries' to Del.icio.us" alt="Add 'Win32: GetFocus across process boundries' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/win32-getfocus-across-process-boundries/&amp;title=Win32%3A+GetFocus+across+process+boundries" title="Add 'Win32: GetFocus across process boundries' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'Win32: GetFocus across process boundries' to digg" alt="Add 'Win32: GetFocus across process boundries' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/win32-getfocus-across-process-boundries/" title="Add 'Win32: GetFocus across process boundries' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'Win32: GetFocus across process boundries' to Technorati" alt="Add 'Win32: GetFocus across process boundries' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/win32-getfocus-across-process-boundries/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Testuff is finally usable</title>
		<link>http://www.gooli.org/blog/133/</link>
		<comments>http://www.gooli.org/blog/133/#comments</comments>
		<pubDate>Thu, 20 Dec 2007 11:28:49 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Testuff]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/133/</guid>
		<description><![CDATA[It&#8217;s a very exciting time for us here at Testuff as our product has finally reached a point where it can actually be used by real people. You can&#8217;t use testing software unless you can report bugs, and until now we could only report bugs to Trac. Now, with the addition of support for Bugzilla [...]]]></description>
			<content:encoded><![CDATA[<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed">It&#8217;s a very exciting time for us here at Testuff as our product has finally reached a point where it can actually be used by real people. You can&#8217;t use testing software unless you can report bugs, and until now we could only report bugs to Trac. Now, with the addition of support for Bugzilla and FogBugz, we really do integrate with several bug trackers as our home page claims, and the new test editor make it a breeze to add and edit test cases.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed">I&#8217;m going to talk about some of the new things we did for this version in the rest of this post, but if you just want to take a look at the new Testuff, <a href="http://www.testuff.com/download">just grab it from our site</a>.</p>
<p><strong>Eating Bonzo for breakfast</strong><a href="http://gooli.org/blog/wp-content/uploads/2007/12/testeditor.PNG" title="New Test Editor"><img src="http://gooli.org/blog/wp-content/uploads/2007/12/testeditor.PNG" style="margin: 10px; float: right" alt="New Test Editor" height="218" width="256" /></a><br />
They say eating your own dog food is important. Although I personally prefer a good steak, you can&#8217;t argue with some of the best minds of the software industry. Following the canine oriented software practice we finally sat down to create some tests for Testuff in, well, Testuff. And boy, was it awkward to write tests with our test editor. So we wrote a new test editor, which makes typing tests easy. Just separate the steps with a blank line and the editor will do the rest. You can also copy-paste tests into the new editor from almost anywhere â€“ Word, Excel, the web, etc.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed">We helped manually convert a bunch of tests for some of our clients who had a few hundred test cases written down in Excel. Took about half an hour to get it done including reorganizing the tests making sure there were no duplicates.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed"><strong>Integrate early, integrate often</strong><br />
It was kind of ridiculous that the Testuff front page said we supported integration with various bug trackers and when you scrolled down to the list, you saw we actually only supported <a href="http://trac.edgewall.org/">Trac</a>. We started out with Trac as we use it ourselves, but we now added support for two more popular bug trackers. We&#8217;re are going to add support for even more bug trackers, as that seems to be what our users want most.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed"><o:p></o:p><a href="http://www.bugzilla.org/">Bugzilla</a> is probably the oldest guy on the block. With almost 10 years of experience under their belts, the guys there know how to manage defects and have seen it all. <a href="http://avatraxiom.livejournal.com/58084.html">A interesting recent post</a> seems to indicate Bugzilla may be <a href="http://www.perlmonks.com/?node_id=561229">dying along side Perl</a>, but itâ€™s still widely used and loved by many however ugly I may think it&#8217;s UI is.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed">Many software developers know the name of Joel Spolsky. I was first introduced to <a href="http://www.joelonsoftware.com/Archive.html">his excellent articles</a> when I was a young officer in the Israeli army trying to lead a team of soldiers-developers through the mud of an enormous C++/MFC HR planning system. Some of the articles were amusing, some interesting and a few were a real eye-opener to me. His company, FogCreek, makes a bug tracking and project management software called <a href="http://www.fogcreek.com/FogBugz/">FogBugz</a>. Personally, I prefer the more Spartan approach of Trac and like to tinker with it&#8217;s Python code base, but FogBugz has many followers and it is now offered as an on-demand service, which makes it a great match with Testuff.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed"><strong>Those damn proxies</strong><br />
Being a desktop application with a web-based backend, the Testuff client needs to access our server in order to work. It turned out that accessing the internet through a proxy is a challenging task, which Python doesn&#8217;t help with nearly enough. I ended up wrapping the WinHTTP library with ctypes code and using it to connect to the internet using the settings in Internet Explorer. That seems to be working with all our clients so far, but it&#8217;s not a portable solution. I&#8217;ll be taking a look at <a href="http://pycurl.sourceforge.net/">pycurl</a> next, to see if it works as well as promised on their website. I&#8217;ll post a full report on the proxy fighting process sometime soon.<o:p> </o:p></p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed">Oh, yeah, and you can finally change the password you use to connect to Testuff.<o:p> That doesn&#8217;t have anything to do with the proxy support, but this stupid omission we made in previous versions doesn&#8217;t justify it&#8217;s own heading, right? </o:p></p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed"><strong>What&#8217;s next?</strong><br />
Right now weâ€™re hard at work on our next version, 0.8. There will be support for even more bug trackers that our customers asked for (<a href="http://www.mantisbt.org/" target="_blank">Mantis</a>, <a href="http://www.elementool.com/" target="_blank">Elementool</a>, <a href="http://www.atlassian.com/software/jira/" target="_blank">Jira</a> and maybe another one or two if we have the time), and, if I can fix that damn segfault, a Linux version of the Testuff client.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed">You can download Testuff from <a href="http://www.testuff.com/download">www.testuff.com/download</a>.</p>
<p class="MsoNormal" dir="ltr" style="text-align: left; direction: ltr; unicode-bidi: embed"><o:p> </o:p></p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/133/&amp;title=Testuff+is+finally+usable" title="Add 'Testuff is finally usable' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'Testuff is finally usable' to Del.icio.us" alt="Add 'Testuff is finally usable' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/133/&amp;title=Testuff+is+finally+usable" title="Add 'Testuff is finally usable' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'Testuff is finally usable' to digg" alt="Add 'Testuff is finally usable' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/133/" title="Add 'Testuff is finally usable' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'Testuff is finally usable' to Technorati" alt="Add 'Testuff is finally usable' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/133/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Touch typing in multiple languages - Recaps</title>
		<link>http://www.gooli.org/blog/touch-typing-in-multiple-languages-recaps/</link>
		<comments>http://www.gooli.org/blog/touch-typing-in-multiple-languages-recaps/#comments</comments>
		<pubDate>Mon, 10 Dec 2007 10:55:02 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/touch-typing-in-multiple-languages-recaps/</guid>
		<description><![CDATA[I learned touch typing a long time ago. Since I spend most of my waking hours in front of a computer typing either text or code, touch typing is something I can&#8217;t live without. Sometimes however, I am faced with a daunting task of writing an email or a document in a mix of two [...]]]></description>
			<content:encoded><![CDATA[<p>I learned touch typing a long time ago. Since I spend most of my waking hours in front of a computer typing either text or code, touch typing is something I can&#8217;t live without. Sometimes however, I am faced with a daunting task of writing an email or a document in a mix of two languages. Technical documents in Hebrew for instance, usually contain quite a lot of English terms. I can touch type in Hebrew as well as I can in English, but when the time comes to switch between languages, that weird Alt-Shift combination really kills my flow. I might be nitpicking a bit here, but I can&#8217;t tell you how many times I pressed Shift-Alt instead of Alt-Shift and wound up in the application&#8217;s menu instead of changing the current language.<a href="http://gooli.org/blog/wp-content/uploads/2007/12/image1.png"><img style="border: 0px none ; margin: 5px;" alt="image" src="http://gooli.org/blog/wp-content/uploads/2007/12/image-thumb1.png" align="right" border="0" height="198" width="299" /></a></p>
<p>Then there&#8217;s the CapsLock key. I don&#8217;t think anybody uses it nowadays, and even the touch typists seem to just HOLD THE SHIFT WITH THEIR PINKY and type what needs to be in capital letters. I wrote a small program called <a href="http://www.gooli.org/blog/recaps">Recaps</a> a while ago that converts CapsLock into a language switching key. Now I can&#8217;t live without it. I find myself instinctively hitting CapsLock to switch languages never thinking about it, even on computers I didn&#8217;t install it on. Needless to say it&#8217;s one of the first things I install on a computer I need to work on.</p>
<p>I talked to an old friend of mine last night who said he was using Recaps and spreading it around but he was missing a feature. When there were more three or more languages installed on the computer, Recaps would just cycle through all of them, like Alt-Shift does. Most times however, you only use two languages at any given time, typically English and your native tongue, and only need to switch between these two.</p>
<p>Doing this in Win32 API was a bitch, but I finally got a tray icon and a small menu to work. The menu shows the list of languages currently installed on your computer with check boxes next to them. Hitting CapsLock now only cycles through the languages that are currently enabled and even saves the active languages between runs.</p>
<p>You can download source and binaries for the new 0.3 version from my <a href="http://www.gooli.org/blog/recaps">Recaps page</a>.</p>
<p>I&#8217;d love to know if anybody finds it as useful as I do.</p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/touch-typing-in-multiple-languages-recaps/&amp;title=Touch+typing+in+multiple+languages+-+Recaps" title="Add 'Touch typing in multiple languages - Recaps' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'Touch typing in multiple languages - Recaps' to Del.icio.us" alt="Add 'Touch typing in multiple languages - Recaps' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/touch-typing-in-multiple-languages-recaps/&amp;title=Touch+typing+in+multiple+languages+-+Recaps" title="Add 'Touch typing in multiple languages - Recaps' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'Touch typing in multiple languages - Recaps' to digg" alt="Add 'Touch typing in multiple languages - Recaps' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/touch-typing-in-multiple-languages-recaps/" title="Add 'Touch typing in multiple languages - Recaps' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'Touch typing in multiple languages - Recaps' to Technorati" alt="Add 'Touch typing in multiple languages - Recaps' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/touch-typing-in-multiple-languages-recaps/feed/</wfw:commentRss>
		</item>
		<item>
		<title>What &#34;gooli&#34; really means</title>
		<link>http://www.gooli.org/blog/what-gooli-really-means/</link>
		<comments>http://www.gooli.org/blog/what-gooli-really-means/#comments</comments>
		<pubDate>Sun, 02 Dec 2007 09:12:00 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/what-gooli-really-means/</guid>
		<description><![CDATA[Apparently the nick name I&#8217;ve been called since my first week at the army has different meanings in different languages. And some are not so pleasant :)
Sarah Siegel writes:
&#8220;Channa, I don&#8217;t want to forget to ask you for a word today. How do you say, &#8216;Bull&#8217; in Kannada?&#8221;&#8220;Gooli, Ma&#8217;am.&#8221;

And the urban dictionary says:

Goolies
Noun:1. Private parts2. [...]]]></description>
			<content:encoded><![CDATA[<p>Apparently the nick name I&#8217;ve been called since my first week at the army has different meanings in different languages. And some are not so pleasant :)</p>
<p>Sarah Siegel <a href="http://sarahsiegelstories.blogspot.com/2007/11/kannada-word-of-day-gooli.html">writes</a>:</p>
<blockquote><p>&#8220;Channa, I don&#8217;t want to forget to ask you for a word today. How do you say, &#8216;Bull&#8217; in Kannada?&#8221;<br />&#8220;<em>Gooli</em>, Ma&#8217;am.&#8221;</p>
</blockquote>
<p>And the urban dictionary <a href="http://www.urbandictionary.com/define.php?term=goolies">says</a>:<br />
<blockquote>
<p>Goolies
<p>Noun:<br />1. Private parts<br />2. Family Jewels
<p><em>&#8220;I kicked him in the goolies&#8221;</em></p>
</blockquote>
<p>Balls of bull. Nice!</p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/what-gooli-really-means/&amp;title=What+%26quot%3Bgooli%26quot%3B+really+means" title="Add 'What &quot;gooli&quot; really means' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'What &quot;gooli&quot; really means' to Del.icio.us" alt="Add 'What &quot;gooli&quot; really means' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/what-gooli-really-means/&amp;title=What+%26quot%3Bgooli%26quot%3B+really+means" title="Add 'What &quot;gooli&quot; really means' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'What &quot;gooli&quot; really means' to digg" alt="Add 'What &quot;gooli&quot; really means' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/what-gooli-really-means/" title="Add 'What &quot;gooli&quot; really means' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'What &quot;gooli&quot; really means' to Technorati" alt="Add 'What &quot;gooli&quot; really means' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/what-gooli-really-means/feed/</wfw:commentRss>
		</item>
		<item>
		<title>A simple lexer in Python</title>
		<link>http://www.gooli.org/blog/a-simple-lexer-in-python/</link>
		<comments>http://www.gooli.org/blog/a-simple-lexer-in-python/#comments</comments>
		<pubDate>Sat, 20 Oct 2007 22:59:54 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Development]]></category>

		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/a-simple-lexer-in-python/</guid>
		<description><![CDATA[I&#8217;m taking a course on building compilers at the Israeli Open University and just learned how to use flex. It occurred to me that building a simple lexical analyzer should be quite easy with Python&#8217;s re module. A typical lexical analyzer read a stream of text input and splits it into a list of tokens. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m taking a course on building compilers at the <a href="http://www-e.openu.ac.il/">Israeli Open University</a> and just learned how to use <a href="http://dinosaur.compilertools.net/">flex</a>. It occurred to me that building a simple <a href="http://en.wikipedia.org/wiki/Lexical_analysis">lexical analyzer</a> should be quite easy with Python&#8217;s <a href="http://docs.python.org/lib/module-re.html">re module</a>. A typical lexical analyzer read a stream of text input and splits it into a list of tokens. The simplest example of such a thing is the <i>split</i> function which takes a sentence and returns the list of words in it.</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">s = <span style="color: #483d8b;">&quot;A simple lexer in Python&quot;</span><br />
s.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<span style="color: black;">&#91;</span><span style="color: #483d8b;">&#8216;A&#8217;</span>, <span style="color: #483d8b;">&#8217;simple&#8217;</span>, <span style="color: #483d8b;">&#8216;lexer&#8217;</span>, <span style="color: #483d8b;">&#8216;in&#8217;</span>, <span style="color: #483d8b;">&#8216;Python&#8217;</span><span style="color: black;">&#93;</span></div>
<p>The problem becomes more complex when you need to separate the tokens you find into different kinds, words and numbers, for instance. We&#8217;ll use <a href="http://99-bottles-of-beer.net/">a well known lyric</a> as our sample text:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">s = <span style="color: #483d8b;">&quot;&quot;</span><span style="color: #483d8b;">&quot;99 bottles of beer on the wall, 99 bottles of beer.<br />
Take one down and pass it around, 98 bottles of beer on the wall.&quot;</span><span style="color: #483d8b;">&quot;&quot;</span></div>
<p>The first thing we need to do is build a regular expression that recognizes words and another one that recognizes numbers. Although there are shorter ways to build those regular expressions, I like the less obscure form:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">wordsRegex = <span style="color: #483d8b;">&quot;[A-Za-z]+&quot;</span><br />
numbersRegex = <span style="color: #483d8b;">&quot;[0-9]+&quot;</span></div>
<p>We could now use findall on the string and get all the numbers and words out of it.</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;"><span style="color: #dc143c;">re</span>.<span style="color: black;">findall</span><span style="color: black;">&#40;</span>wordsRegex, s<span style="color: black;">&#41;</span><br />
<span style="color: black;">&#91;</span><span style="color: #483d8b;">&#8216;bottles&#8217;</span>, <span style="color: #483d8b;">&#8216;of&#8217;</span>, <span style="color: #483d8b;">&#8216;beer&#8217;</span>, <span style="color: #483d8b;">&#8216;on&#8217;</span>, <span style="color: #483d8b;">&#8216;the&#8217;</span>, <span style="color: #483d8b;">&#8216;wall&#8217;</span>, <span style="color: #483d8b;">&#8216;bottles&#8217;</span>, <span style="color: #483d8b;">&#8216;of&#8217;</span>, <span style="color: #483d8b;">&#8216;beer&#8217;</span>, <span style="color: #483d8b;">&#8216;Take&#8217;</span>, <span style="color: #483d8b;">&#8216;one&#8217;</span>, <span style="color: #483d8b;">&#8216;down&#8217;</span>, <span style="color: #483d8b;">&#8216;and&#8217;</span>, <span style="color: #483d8b;">&#8216;pass&#8217;</span>, <span style="color: #483d8b;">&#8216;it&#8217;</span>, <span style="color: #483d8b;">&#8216;around&#8217;</span>, <span style="color: #483d8b;">&#8216;bottles&#8217;</span>, <span style="color: #483d8b;">&#8216;of&#8217;</span>, <span style="color: #483d8b;">&#8216;beer&#8217;</span>, <span style="color: #483d8b;">&#8216;on&#8217;</span>, <span style="color: #483d8b;">&#8216;the&#8217;</span>, <span style="color: #483d8b;">&#8216;wall&#8217;</span><span style="color: black;">&#93;</span></p>
<p><span style="color: #dc143c;">re</span>.<span style="color: black;">findall</span><span style="color: black;">&#40;</span>numbersRegex, s<span style="color: black;">&#41;</span><br />
<span style="color: black;">&#91;</span><span style="color: #483d8b;">&#8216;99&#8242;</span>, <span style="color: #483d8b;">&#8216;99&#8242;</span>, <span style="color: #483d8b;">&#8216;98&#8242;</span><span style="color: black;">&#93;</span></div>
<p>But wait, you say, that isn&#8217;t what we wanted at all! We need to get the tokens in the order of their appearance in text and still get the type of each token. Something along the lines of </p>
<div class="ch_code_container" style="font-family: monospace;height:100%;"><span style="color: #ff7700;font-weight:bold;">for</span> tokenType, tokenText <span style="color: #ff7700;font-weight:bold;">in</span> lexer<span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> tokenType, tokenText</div>
<p>would be really nice. </p>
<p>In order to do that, we&#8217;ll need to combine both regular expressions into one and iterate on the result of findall examining each token to decide on its type.</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">regex = <span style="color: #483d8b;">&quot;(%s)|(%s)&quot;</span> % <span style="color: black;">&#40;</span>wordsRegex, numbersRegex<span style="color: black;">&#41;</span><br />
<span style="color: #483d8b;">&#8216;([A-Za-z]+)|([0-9]+)&#8217;</span><br />
<span style="color: #dc143c;">re</span>.<span style="color: black;">findall</span><span style="color: black;">&#40;</span>regex, s<span style="color: black;">&#41;</span><br />
<span style="color: black;">&#91;</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8221;</span>, <span style="color: #483d8b;">&#8216;99&#8242;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;bottles&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;of&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;beer&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <br />
<span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;on&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;the&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;wall&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8221;</span>, <span style="color: #483d8b;">&#8216;99&#8242;</span><span style="color: black;">&#41;</span>, <br />
<span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;bottles&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;of&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;beer&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;Take&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>,<br />
&nbsp;<span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;one&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;down&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;and&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;pass&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <br />
<span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;it&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;around&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8221;</span>, <span style="color: #483d8b;">&#8216;98&#8242;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;bottles&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <br />
<span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;of&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;beer&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;on&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;the&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span>, <span style="color: black;">&#40;</span><span style="color: #483d8b;">&#8216;wall&#8217;</span>, <span style="color: #483d8b;">&#8221;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span></div>
<p>As you can see, the result of the call to findall is a list of tuples, each containing a single match. If you look closely at the way I&#8217;ve combined the two regular expressions, you&#8217;ll see that each part is surrounded with parenthesis and that there&#8217;s a pipe (|) between the expressions. The compound regular expression matches either a number rf a word and each tuple in the return value of findall contains the matches for each parenthesized part of the regexp. However, since we combined the parts using a pipe (|), only one of the parts matches each time.</p>
<p>Using that knowledge we can now construct a simple loop that shows the token type for each of the words in the lyric:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;"><span style="color: #ff7700;font-weight:bold;">for</span> t <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">re</span>.<span style="color: black;">findall</span><span style="color: black;">&#40;</span>regex, s<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> t<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;word&quot;</span>, t<span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">elif</span> t<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;number&quot;</span>, t<span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span><span style="color: black;">&#93;</span></div>
<p>We now have most of the knowledge we need to build ourselves a lexer that will take a list of regular expressions and some text and return (or even better, generate) an list of tokens and their types. We&#8217;ll need to combine the regular expressions for each token into one big regex using pipes, scan the string, and gather the tokens and their types.</p>
<p>Our usage code looks like this:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;">definitions = <span style="color: black;">&#91;</span><br />
&nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;word&quot;</span>, <span style="color: #483d8b;">&quot;[A-Za-z]+&quot;</span><span style="color: black;">&#41;</span>,<br />
&nbsp; &nbsp; <span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;number&quot;</span>, <span style="color: #483d8b;">&quot;[0-9]+&quot;</span><span style="color: black;">&#41;</span>,<br />
<span style="color: black;">&#93;</span></p>
<p>lex = Lexer<span style="color: black;">&#40;</span>definitions<span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">for</span> tokenType, tokenValue <span style="color: #ff7700;font-weight:bold;">in</span> lex.<span style="color: black;">parse</span><span style="color: black;">&#40;</span>s<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">print</span> tokenType, tokenValue</div>
<p>And here is the code for the lexer itself:</p>
<div class="ch_code_container" style="font-family: monospace;height:100%;"><span style="color: #ff7700;font-weight:bold;">class</span> Lexer<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, definitions<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">definitions</span> = definitions<br />
&nbsp; &nbsp; &nbsp; &nbsp; parts = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> name, part <span style="color: #ff7700;font-weight:bold;">in</span> definitions:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parts.<span style="color: black;">append</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;(?P&amp;lt;%s&amp;gt;%s)&quot;</span> % <span style="color: black;">&#40;</span>name, part<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">regexpString</span> = <span style="color: #483d8b;">&quot;|&quot;</span>.<span style="color: black;">join</span><span style="color: black;">&#40;</span>parts<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">regexp</span> = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">regexpString</span>, <span style="color: #dc143c;">re</span>.<span style="color: black;">MULTILINE</span><span style="color: black;">&#41;</span></p>
<p>
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> parse<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, text<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># yield lexemes</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> match <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">regexp</span>.<span style="color: black;">finditer</span><span style="color: black;">&#40;</span>text<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; found = <span style="color: #008000;">False</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> name, rexp <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">definitions</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m = match.<span style="color: black;">group</span><span style="color: black;">&#40;</span>name<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> m <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">None</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">yield</span> <span style="color: black;">&#40;</span>name, m<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">break</span></div>
<p>Some notes on the implementation are in order. I&#8217;ve used the little known (?P&lt;name&gt;&#8230;) syntax for naming the parenthesized groups of regular expressions. Using that syntax the expression (?P&lt;word&gt;[A-Za-z]) matches a word and that match is accessible with match.group(&#8217;word&#8217;) where match is a re.Match object.</p>
<p>In order to speed things up a bit, I&#8217;ve compiled the regular expression when the Lexer object is created, used the finditer function instead of findall, and made parse a generator instead of a list returning function.</p>
<p>Using this simple lexer implementation it was quite simple to create a Python-to-HTML converter with syntax highlighting that works well enough to highlight the code of the highlighter itself!</p>
<p>The code for the lexer and syntax highlighter example are available <a href="/snippets/pylexer.py">here</a> and on my <a href="snippets">snippets page</a>. You can also see the result of running the syntax highlighter on itself <a href="/snippets/pylexer.html">here</a>.</p>
<p>Enjoy lexing and let me know if you found this useful.</p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/a-simple-lexer-in-python/&amp;title=A+simple+lexer+in+Python" title="Add 'A simple lexer in Python' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'A simple lexer in Python' to Del.icio.us" alt="Add 'A simple lexer in Python' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/a-simple-lexer-in-python/&amp;title=A+simple+lexer+in+Python" title="Add 'A simple lexer in Python' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'A simple lexer in Python' to digg" alt="Add 'A simple lexer in Python' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/a-simple-lexer-in-python/" title="Add 'A simple lexer in Python' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'A simple lexer in Python' to Technorati" alt="Add 'A simple lexer in Python' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/a-simple-lexer-in-python/feed/</wfw:commentRss>
		</item>
		<item>
		<title>DreamHost PR stunt?</title>
		<link>http://www.gooli.org/blog/dreamhost-pr-stunt/</link>
		<comments>http://www.gooli.org/blog/dreamhost-pr-stunt/#comments</comments>
		<pubDate>Fri, 19 Oct 2007 10:39:56 +0000</pubDate>
		<dc:creator>gooli</dc:creator>
		
		<category><![CDATA[Marketing]]></category>

		<guid isPermaLink="false">http://www.gooli.org/blog/dreamhost-pr-stunt/</guid>
		<description><![CDATA[DreamHost is a web hiosting company. I&#8217;ve never hosted anything with them, but now I might. They&#8217;ve been EVICTED from their office spaces for drunken behaviour and other types of misconduct and they&#8217;ve blogged about it, with pictures and all (you should also read the comments, they are quite funny).
Would you host your website with [...]]]></description>
			<content:encoded><![CDATA[<p>DreamHost is a web hiosting company. I&#8217;ve never hosted anything with them, but now I might. They&#8217;ve been EVICTED from their office spaces for drunken behaviour and other types of misconduct and <a href="http://blog.dreamhost.com/2007/10/04/we-keep-it-moving/">they&#8217;ve blogged about it</a>, with pictures and all (you should also read the comments, they are quite funny).</p>
<p>Would you host your website with a company whose offices look like this?</p>
<p><a href="http://gooli.org/blog/wp-content/uploads/2007/10/image.png" atomicselection="true"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="300" alt="image" src="http://gooli.org/blog/wp-content/uploads/2007/10/image-thumb.png" width="400" border="0"></a>&nbsp;</p>
<p>&nbsp;</p>
<p>I don&#8217;t really know what to think about this. Many &#8220;serious&#8221; companies I&#8217;ve delat with in the past provide crappy service although their offices are sparky clean and they don&#8217;t do silly things like consume enough alcohol to&nbsp;get evicted. On the other hand it does seem ensettling that the company that you rely on to keep your data safe behaves like a college fraternity.</p>
<p>I did write about them however as <a href="http://michael.susens-schurter.com/blog/2007/10/05/dreamhost-never-ceases-to-amaze/">others have done</a>&nbsp;and that&#8217;s got to be worth something. After all, there&#8217;s no such thing as bad press, right?</p>
<!-- Social Bookmarks BEGIN -->
<div class="social_bookmark">
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://del.icio.us/post?url=http://www.gooli.org/blog/dreamhost-pr-stunt/&amp;title=DreamHost+PR+stunt%3F" title="Add 'DreamHost PR stunt?' to Del.icio.us"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/delicious.png" title="Add 'DreamHost PR stunt?' to Del.icio.us" alt="Add 'DreamHost PR stunt?' to Del.icio.us" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://digg.com/submit?phase=2&amp;url=http://www.gooli.org/blog/dreamhost-pr-stunt/&amp;title=DreamHost+PR+stunt%3F" title="Add 'DreamHost PR stunt?' to digg"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/digg.png" title="Add 'DreamHost PR stunt?' to digg" alt="Add 'DreamHost PR stunt?' to digg" /></a>
<a class="social_img" onclick="window.open(this.href, '_blank', 'scrollbars=yes,menubar=no,height=600,width=750,resizable=yes,toolbar=no,location=no,status=no'); return false;" href="http://www.technorati.com/faves?add=http://www.gooli.org/blog/dreamhost-pr-stunt/" title="Add 'DreamHost PR stunt?' to Technorati"><img src="http://gooli.org/blog/wp-content/plugins/social_bookmarks/technorati.png" title="Add 'DreamHost PR stunt?' to Technorati" alt="Add 'DreamHost PR stunt?' to Technorati" /></a>
</div>
<!-- Social Bookmarks END -->
]]></content:encoded>
			<wfw:commentRss>http://www.gooli.org/blog/dreamhost-pr-stunt/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
