<?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-1625401331703494000</id><updated>2012-01-30T05:15:18.564-08:00</updated><category term='flash'/><category term='javascript'/><category term='documentation'/><category term='Arabic'/><category term='contracts'/><category term='gadgets'/><category term='computer go'/><category term='blogspot'/><category term='Zend Framework'/><category term='boost'/><category term='web development'/><category term='Windows'/><category term='intl/ICU'/><category term='gnome'/><category term='php|a'/><category term='C++'/><category term='git'/><category term='regexes'/><category term='animation'/><category term='makefile'/><category term='oauth'/><category term='unicode'/><category term='thunderbird'/><category term='Japanese'/><category term='artificial intelligence'/><category term='bidirectional text'/><category term='apache'/><category term='linux'/><category term='i18n'/><category term='centos'/><category term='commandline'/><category term='threads'/><category term='cloud computing'/><category term='data presentation'/><category term='mysql'/><category term='php'/><category term='security'/><category term='AIR'/><category term='AS3'/><category term='graphics'/><category term='ASP'/><category term='Doctrine ORM'/><category term='web services'/><category term='networking'/><category term='jquery'/><category term='parallelization'/><category term='clusters'/><category term='DB'/><category term='twitter'/><category term='optimization'/><category term='parser'/><category term='ubuntu'/><category term='testing'/><category term='MLSN'/><category term='svn'/><category term='R'/><title type='text'>Darren's Developer Diary</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default?start-index=101&amp;max-results=100'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>110</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-8146756370844446227</id><published>2012-01-24T19:23:00.000-08:00</published><updated>2012-01-24T19:23:36.808-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='unicode'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='Japanese'/><title type='text'>Umlauts, pound signs and more!</title><content type='html'>Until I learnt this tip, whenever I needed some fancy character (£,°,á,ß, etc.) I went hunting for somewhere to copy and paste it from. Not any more!&lt;br /&gt;&lt;br /&gt;I assigned my previously useless windows key to be my &lt;i&gt;Compose&lt;/i&gt; key. To type the above pound sign I pressed the windows key, then pressed the L key, then pressed the minus key. Scharfes-S (ß) is simply windows key then press the S key twice. To type á (a with an accent) you press &lt;span style="font-size: large;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;'&lt;/span&gt;&lt;/span&gt; (shift-7) and &lt;span style="font-size: large;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;a&lt;/span&gt;&lt;/span&gt; after the window key.&lt;br /&gt;&lt;br /&gt;(Note: until I wrote this post I thought you had to hold the Compose key down while pressing the other keys. That works too, but it is easier to treat it as a modal switch. In other words, you press the Compose key to enter &lt;i&gt;Compose Mode&lt;/i&gt;, then the next two characters you type are interpreted together. And if the two characters you type have no special meaning then nothing happens. Either way you exit Compose Mode after your two keypresses.)&lt;br /&gt;&lt;br /&gt;See here for &lt;a href="http://www.hermit.org/Linux/ComposeKeys.html" target="_blank"&gt;the full list of all the compose options&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Naturally the above is for linux. This page &lt;a href="http://superuser.com/questions/64178/compose-key-on-windows" target="_blank"&gt;answers how to do it on Windows&lt;/a&gt; and &lt;a href="http://tlt.its.psu.edu/suggestions/international/accents/codemac.html" target="_blank"&gt;here is the same concept on a Mac&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Bonus Tip. Ever wanted to write a Japanese post office sign ( 〒 ) ? Enter your Japanese IME and type yuubin (ゆうびん) (then press space). The ～ symbol can be done by typing kara (から).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;˙unɟ ɹoɟ ʇsnɾ ˙uʍop ǝpısdn ʇxǝʇ ɹnoʎ suɹnʇ ʇɐɥʇ &lt;a href="http://www.sherv.net/flip.html" target="_blank"&gt;ǝʇıs ɐ puıɟ&lt;/a&gt; :dıʇ snuoq ɹǝɥʇouɐ&lt;br /&gt;(Yes, this is part of Unicode too.)&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8146756370844446227?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8146756370844446227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8146756370844446227' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8146756370844446227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8146756370844446227'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2012/01/umlauts-pound-signs-and-more.html' title='Umlauts, pound signs and more!'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6964541708552903121</id><published>2011-11-16T06:07:00.001-08:00</published><updated>2011-11-16T06:26:10.196-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='centos'/><category scheme='http://www.blogger.com/atom/ns#' term='boost'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>boost on centos (vs. on ubuntu)</title><content type='html'>You'd think porting a C++ program from one 64-bit linux to another would be trivial. But, no. A program developed with no issues on Ubuntu 10.04 was a lot of trouble to get to compile on Centos 5.6.&lt;br /&gt;But get it to compile I did... read on for the secret words you have to utter... &lt;br /&gt;&lt;br /&gt;First thing I did was:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; yum erase boost boost-devel&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This got rid of the very old 1.33 library. I then ran this:&lt;br /&gt;&lt;br /&gt;&lt;pre wrap=""&gt;  &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;yum install boost141 boost141-devel boost141-program-options&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt; boost141-regex boost141-thread boost141-system&lt;/span&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre style="font-family: inherit;" wrap=""&gt;I then had to hack the makefile to add this to my CFLAGS:&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;  -I/usr/include/boost141/&lt;/span&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;That got me compiling. But not linking. I changed my LDFLAGs to look like this but still it would not link:&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;  -L/usr/lib64/boost141 -lboost_regex -lboost_program_options -lboost_system -lboost_thread&lt;/span&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;I played around with various -L settings, until I had a breakthrough. I noticed the above line complained with:&lt;/pre&gt;&lt;pre wrap=""&gt;  &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;/usr/bin/ld: cannot find -lboost_thread&lt;/span&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;But some different -L settings instead gave me:&lt;/pre&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; /usr/bin/ld: cannot find -lboost_regex&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Blink and you miss it, and indeed I had. My above line was linking with most of Boost, just not boost_thread. In other words, I was &lt;i&gt;really close&lt;/i&gt; and hadn't realized it. A bit more poking around discovered that Ubuntu calls it "boost_thread", while Centos calls it "boost_thread-mt" (careful, the first one is an underline, the second one is a hyphen). So, the final magic line for Centos was:&lt;br /&gt;&lt;br /&gt;&lt;pre wrap=""&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;  -L/usr/lib64/boost141 -lboost_regex -lboost_program_options -lboost_system -lboost_thread-mt&lt;/span&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="font-family: inherit;" wrap=""&gt;NOTE: I did not do any post-installation steps after the yum install steps. I saw some people suggesting copying files from one place to another but I did not need to. (Also, -L/usr/lib64 should be sufficient, as every thing is symlinked; maybe even the -L flag is not needed at all... but I'm in the "If it ain't broke." mindset now, so no more experimentation for me!)&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre wrap=""&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6964541708552903121?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6964541708552903121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6964541708552903121' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6964541708552903121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6964541708552903121'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/11/boost-on-centos-vs-on-ubuntu.html' title='boost on centos (vs. on ubuntu)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-8240741216836609663</id><published>2011-11-12T17:21:00.001-08:00</published><updated>2011-11-12T18:30:54.392-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><title type='text'>Actual costs: rackspace cloud</title><content type='html'>A few months back I decided to put a 24/7 script on a &lt;a href="http://www.rackspace.com/cloud/cloud_hosting_products/servers/pricing/" target="_blank"&gt;Rackspace Cloud&lt;/a&gt; instance, instead of the more obvious Amazon EC2 choice. The reason at the time was my needs were low CPU but relatively high bandwidth and diskspace usage and it worked out cheaper.&lt;br /&gt;&lt;br /&gt;Now I've had a few invoices in I am relieved to say there was no catch. My past three invoices have been $11.99, $11.99 and $12.20 (USD). This is for a minimal CPU spec (256MB, 1.6% of a quad core CPU, 10GB disk), 1.1 to 1.3 GB/month of outgoing bandwidth each month (there is no charge for incoming bandwidth), and cloud storage rising from 4 to 8GB. 90% of the monthly cost is for the machine, and the cloud storage has risen from $0.63 to $1.15. The bandwidth is not costing much at all.&lt;br /&gt;&lt;br /&gt;In contrast on Amazon EC2, the micro instance would cost $15.65 (including $1 for 10GB of EBS storage), while a small instance would cost $62.25/month, of which $0.03 is the bandwidth usage. (The first year of that micro instance would be free &lt;i&gt;if you are a new customer&lt;/i&gt;, but I am not.)&lt;br /&gt;&lt;br /&gt;So, at the CPU bottom-end, Rackspace is winning on cost. The other feature of Rackspace Cloud that I love is there is an automatic daily backup of the full disk image, and that backup is stored in the cloud storage. (Storing that backup is basically all my $1/month cloud storage costs.)&lt;br /&gt;&lt;br /&gt;What do I not like? I keep using up my 10GB disk space. But there seems no way to move to 20GB without doubling the CPU spec and doubling the monthly cost; with Amazon micro I'd just increase the EBS storage space. With an Amazon small instance I'd get 160GB and would not care.&lt;br /&gt;&lt;br /&gt;What do I not like about Rackspace &lt;i&gt;and&lt;/i&gt; Amazon? It is that you just get a basic linux distro. You have to spend time installing, configuring and maintaining. And the configuration is not trivial; I've kept a log of all I've had to do, and it includes things like moving ssh off of port 22, setting up an iptables firewall, installing a mail server (not a POP server, just enough so I can *send* email alerts), and writing my own low-diskspace email alert script. The latter was done just the other day after my application broke,  yet again, because the machine had run out of disk space.&lt;br /&gt;&lt;br /&gt;P.S. As I want 24/7, and have not mentioned the need to scale, what about cheaper shared hosting? Well, I couldn't find a VPS, that gives me root access and no restrictions, for under $10/month. It seems Rackspace is winning that fight too?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8240741216836609663?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8240741216836609663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8240741216836609663' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8240741216836609663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8240741216836609663'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/11/actual-costs-rackspace-cloud.html' title='Actual costs: rackspace cloud'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-8492108182791815827</id><published>2011-11-06T18:43:00.000-08:00</published><updated>2011-11-06T18:43:41.244-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>PHP, Proxies, HTTPS: v2, v3 or v23?!</title><content type='html'>From a PHP http client, using HTTPS via a proxy, I started getting a "400 bad request" error from Apache. I knew it &lt;i&gt;could&lt;/i&gt; work because it worked last week. The apache error log message was:&lt;br /&gt;&amp;nbsp;&amp;nbsp; Hostname 127.0.0.1 provided via SNI and hostname mytest.local provided via HTTP are different&lt;br /&gt;&lt;br /&gt;My first troubleshooting mistake was messing around with server-side settings: I found out what SNI meant, but as far as I could see I wasn't using it. Then, &lt;i&gt;finally&lt;/i&gt;, I remembered I could use curl as a test http client, and it was working fine. I removed all my server-side changes and curl was still connecting fine. So now I knew I'd broken something client-side. I added the -v flag to curl to see the exact headers it is sending. We're both sending the same headers.&lt;br /&gt;&lt;br /&gt;Finally I remembered I'd changed this line:&lt;br /&gt;&amp;nbsp; stream_socket_enable_crypto($fp,true,STREAM_CRYPTO_METHOD_SSLv23_CLIENT);&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://php.net/stream_socket_enable_crypto" target="_blank"&gt;PHP docs&lt;/a&gt; give no guidance on which option to choose, but "v23" sounded like it would work with version 2 or version 3, and maybe do all kinds of auto-negotiation behind the scenes. Which had to be a good thing. I'm sure I'd tested after changing, but I must have tested non-HTTPS or without the proxy by mistake. When I changed back to this line, everything worked again:&lt;br /&gt;&amp;nbsp; stream_socket_enable_crypto($fp,true,STREAM_CRYPTO_METHOD_SSLv3_CLIENT);&lt;br /&gt;&lt;br /&gt;I hope that helps someone, as google was no use for me (all the hits about the SNI name and HTTP name difference were due to Apache being case-sensitive about the name comparison, which was not the problem here).&lt;br /&gt;&lt;br /&gt;By the way if you want to know how to do http connections, with a proxy, using PHP, supporting both HTTP and HTTPS, &lt;a href="https://github.com/fennb/phirehose/issues/11#issuecomment-2649337" target="_blank"&gt;I've described it here&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8492108182791815827?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8492108182791815827/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8492108182791815827' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8492108182791815827'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8492108182791815827'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/11/php-proxies-https-v2-v3-or-v23.html' title='PHP, Proxies, HTTPS: v2, v3 or v23?!'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3349396230889130001</id><published>2011-10-28T00:59:00.000-07:00</published><updated>2011-10-28T00:59:18.811-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><title type='text'>"Git" into this good habit</title><content type='html'>This time no story giving insight in to my life as a superhero developer; instead straight to the facts. When you write a .gitignore file, precede each entry with a forward slash. So instead of writing:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; *.zip&lt;br /&gt;You should write:&lt;br /&gt;&amp;nbsp;&amp;nbsp; /*.zip&lt;br /&gt;&lt;br /&gt;It means zip files only get ignored in the same directory as the .gitignore file, and not in each subdirectory. Do this on principle, and optimize later if you find it is an extension you really want ignoring project-wide. (In other words wait for the noise to appear before using .gitignore to suppress noise.)&lt;br /&gt;&lt;br /&gt;Especially important for websites: I may have a temporary zip file in the root of the website from a designer, but deeper in the site I may have zip files that users are supposed to download.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3349396230889130001?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3349396230889130001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3349396230889130001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3349396230889130001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3349396230889130001'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/10/git-into-this-good-habit.html' title='&quot;Git&quot; into this good habit'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-164922122183032008</id><published>2011-10-22T19:12:00.000-07:00</published><updated>2011-10-22T19:12:37.866-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web services'/><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Frapi (PHP web service API system)</title><content type='html'>I read about &lt;a href="https://github.com/frapi/frapi"&gt;Frapi&lt;/a&gt; in &lt;a href="http://www.phparch.com/magazine/2011-2/may/"&gt;PHP Architect (May 2011)&lt;/a&gt;, and spent a couple of hours trying it out. It is quite interesting, but I don't think I will be using it. It is a full web-interface for making the API. This is what makes it cool, but also its biggest disadvantage. There is a lot of code involved, meaning there is a lot to learn if you need to change it and lots of places for bugs and security exploits to crop up.&lt;br /&gt;&lt;br /&gt;It comes with a documentation generator, which could be really useful. This feature is still incomplete (for instance there are no links to it yet, see &lt;a href="http://groups.google.com/group/frapi-general/browse_thread/thread/cf2fa2610dd7e5e0"&gt;here&lt;/a&gt;, PDF generation didn't work properly), but it looks okay.&lt;br /&gt;&lt;br /&gt;There is one specific limitation: I could not create an action with an optional parameter, at least not using the router.&amp;nbsp; E.g. if my action is called "ddd", then I can call /ddd (no parameter). But I cannot call "/ddd/77". (I can give an optional parameter with /ddd?id=77). Or I can define a router as "/ddd/:id" so that I can call "/ddd/77". But in that case id is now required and I cannot use "/ddd".&lt;br /&gt;&lt;br /&gt;Another disadvantage is no built-in support for oAuth. (I did find an &lt;a href="https://github.com/davidcoallier/frapi-oauth2"&gt;oauth extension for Frapi&lt;/a&gt; but did not try it as the integration seems quite rough still.)&lt;br /&gt;&lt;br /&gt;Incidentally if you were looking for a full application built on Zend Framework this may make the perfect study case. Apparently only the admin interface uses ZF, and the actual web services do not; but as far as I can tell they are closely tied and you need to keep both together even on your production servers.&lt;br /&gt;&lt;br /&gt;Overall, because making a web service in PHP is not that hard, the advantages are slight and not enough to outweigh the disadvantages (large codebase, inflexible structure, etc.).&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-164922122183032008?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/164922122183032008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=164922122183032008' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/164922122183032008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/164922122183032008'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/10/frapi-php-web-service-api-system.html' title='Frapi (PHP web service API system)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-8739203352036661455</id><published>2011-10-20T04:57:00.000-07:00</published><updated>2011-10-20T04:57:17.374-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>R: what to do when the jitter gets too much</title><content type='html'>The subject sounds like this will be a discussion of an over-full bladder... instead, brace yourself for some serious functional programming.&lt;br /&gt;&lt;br /&gt;So, I had this little bit of R to generate a "rough" exponent curve (as test data):&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; x=1:40&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; y=exp( jitter( x, factor=30 ) ^ 0.5 )&lt;/div&gt;&lt;br /&gt;The problem was it was sometimes giving NA values - too much jitter!&lt;br /&gt;I started off with this fix:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; y[is.na(y)]=exp( x^0.5 )&lt;/div&gt;&lt;br /&gt;I.e. wherever it was an NA use the un-jittered value.&lt;br /&gt;&lt;br /&gt;Unfortunately it was often complaining with "number of items to replace is not a multiple of replacement length". In this case this was not a warning to ignore, because it revealed I was doing something very wrong. &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;exp(x)&lt;/span&gt; is a vector of 40 items. &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;y[is.na(y)]&lt;/span&gt; is a vector of however many entries in y are NA. The dangerous thing is if there is just one NA in y, but it is the 5th element, it will be set to &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;exp(1)^0.5&lt;/span&gt;, instead of &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;exp(5)^0.5&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Let's cut straight to the solution:&lt;br /&gt;&amp;nbsp; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;y[is.na(y)]=exp(x[is.na(y)]^0.5)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;x[is.na(y)]&lt;/span&gt; means the values out of x where the same-sized y-vector has a NA in that position. So, for the above example where just the 5th element in y is NA, then &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;x[is.na(y)]&lt;/span&gt; will be "5".&lt;br /&gt;&lt;br /&gt;Simple when you know how. (?!)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8739203352036661455?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8739203352036661455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8739203352036661455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8739203352036661455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8739203352036661455'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/10/r-what-to-do-when-jitter-gets-too-much.html' title='R: what to do when the jitter gets too much'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-845523994472438199</id><published>2011-10-11T22:22:00.000-07:00</published><updated>2011-10-11T22:22:50.462-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>Renaming files is easy!</title><content type='html'>What do you mean? Of course renaming is easy! From the GUI just click it and type the new name. From the linux commandline just type &lt;code&gt;mv oldname newname&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;But what about when you have 200 *.JPEG files and you need to rename them to be *.jpg? In the past I've written throwaway PHP scripts to do just this. Well, it turns out linux has a command called &lt;code&gt;rename&lt;/code&gt;, that can use regexes. To rename those jpegs:&lt;pre&gt;&lt;br /&gt;  rename 's/JPEG/jpg/' *.JPEG&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What about a few thousand log files, and I want to remove the datestamp portion from the filename (because I'm going to keep each day's logs in their own subdirectory)? How about this:&lt;pre&gt;&lt;br /&gt;  mkdir 20110203&lt;br /&gt;  mv *.20110203.log 20110203/&lt;br /&gt;  rename 's/.20110203//' 20110203/*&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(See &lt;a href="http://darrendev.blogspot.com/2010/10/bash-loops.html"&gt;here for how to then use a bash loop&lt;/a&gt; to loop through all the datestamps you have to deal with.)&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-845523994472438199?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/845523994472438199/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=845523994472438199' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/845523994472438199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/845523994472438199'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/10/renaming-files-is-easy.html' title='Renaming files is easy!'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-4808939145446204262</id><published>2011-09-17T20:29:00.000-07:00</published><updated>2011-09-21T02:26:22.933-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>Add a temporary static IP address</title><content type='html'>At home, with wired ethernet, my (Ubuntu) notebook has a few static IP addresses that I use for developing websites. Out of the house, I use wicd, so I have a dynamic IP address, and those static IPs don't exist. wicd configuration is too complex for me to understand, so I just accept this, but it caught me short the other day when I needed to have both an internet connection and to be able to work on a website running on my notebook.&lt;br /&gt;&lt;br /&gt;I failed then, but I'm ready for next time. To temporarily add a static IP address you simply do (as root):&lt;br /&gt;&lt;pre&gt;ifconfig eth0:3 10.10.10.10 netmask 255.255.0.0&lt;br /&gt;&lt;/pre&gt;I'm choosing "eth0:3" for the interface; it can be any unused number after the colon, and you never need to care what this is. netmask can really be anything for our purposes. The 10.10.10.10 is the IP address I've given it. Test with this:&lt;br /&gt;&lt;pre&gt;ping 10.10.10.10&lt;br /&gt;&lt;/pre&gt;To set up a quick virtual host create a file under /etc/apache2/conf.d called 10.10.10.10.conf (any filename is fine) with these contents:&lt;br /&gt;&lt;pre&gt;&amp;lt;virtualhost 10.10.10.10:80=""&amp;gt;&lt;br /&gt;    DocumentRoot "/var/www/somewhere"&lt;br /&gt;    ServerName 10.10.10.10&lt;br /&gt;&amp;lt;/virtualhost&amp;gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;div style="font-size: large;font-weight:bold"&gt;Tidyup&lt;/div&gt;To remove &lt;i&gt;just&lt;/i&gt; the interface that you added above, use this command:&lt;pre&gt;&lt;br /&gt;ip addr del 10.10.10.10/32 dev eth0:3&lt;br /&gt;&lt;/pre&gt;Or, to restore the network to boot defaults (useful if you have done lots of changes) you can do:&lt;pre&gt;&lt;br /&gt;ifdown -a&lt;br /&gt;ifup -a&lt;br /&gt;&lt;/pre&gt;Either way to then remove the apache config: delete the 10.10.10.10.conf file you created and restart apache.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4808939145446204262?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4808939145446204262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4808939145446204262' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4808939145446204262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4808939145446204262'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/09/add-temporary-static-ip-address.html' title='Add a temporary static IP address'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-648131078896677502</id><published>2011-09-12T06:08:00.000-07:00</published><updated>2011-09-12T06:08:02.827-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='parallelization'/><category scheme='http://www.blogger.com/atom/ns#' term='web services'/><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>node.js: Good Tutorial</title><content type='html'>Chatting with a friend at the weekend, &lt;i&gt;node.js&lt;/i&gt; came up; I'd only vaguely heard of it, but apparently it is what all the kewl kids are into. Server-side javascript, right up my street.&lt;br /&gt;&lt;br /&gt;I took a very quick look yesterday, and it sounded interesting: especially for speed-critical websites. So, today I took a deeper look. First up you should know the &lt;a href="http://nodejs.org/"&gt;official website&lt;/a&gt; has a documentation link that only goes to the API documentation. In real terms that means node.js is officially undocumented. Then from the Wiki I found a link to a free e-book called Mastering Node. I won't give the link as it is (currently) over-priced: poorly organized and unfinished.&lt;br /&gt;&lt;br /&gt;I was&amp;nbsp; getting a bit demoralized when a search found &lt;a href="http://www.nodebeginner.org/"&gt;http://www.nodebeginner.org/&lt;/a&gt;. Now this is more like it. In fact this is an excellent tutorial, that goes right from raw beginner to a reasonably complex mini-application. (I read it in HTML format, but as an ebook it is 60 pages, so you can get an idea of how involved it is going to get.)&lt;br /&gt;&lt;br /&gt;I followed along, and it was fairly easy, though I did have a lot of trouble outputting the POST-ed data. It always said "undefined" in my browser. I could do a console.log() of the variable just before and just after writing it to the browser and it was set correctly. I cleared the cache repeatedly, and tried a second browser. Annoyingly I didn't solve that problem in the end.&lt;br /&gt;&lt;br /&gt;The main point of this blog post is to recommend the above tutorial/ebook if you're interested in getting a feel for node.js, but &lt;b&gt;What Do I Think About &lt;i&gt;node.js&lt;/i&gt;&lt;/b&gt;?&lt;br /&gt;&lt;br /&gt;Hhhmmm. First and foremost, it should only be used by expert programmers. It is a bit like C++ in that it is going to be easy to shoot yourself in the foot. Asynchronous programming is hard. But if you use a synchronous programming-style you will lock up the whole server. I'm thinking in terms of the web server example application here (which is the use-case I had in mind for it). Asynchronous programming is hard. Yes, I know I already said that but I don't think you thought through what that means in the Real World. What will happen is programmers will take a short-cut: they'll use little bits of synchronous code for jobs they know are so quick that no-one will notice. (Even the above tutorial does this: fs.renameSync)&amp;nbsp; The problem is any job involving any I/O device (like a hard disk file system or a TCP/IP socket) will take 10 times longer to finish than average, about 1% of the time. (I made that statistic up, but the principle is true, so stay with me...)&lt;br /&gt;&lt;br /&gt;What does that mean? When that happens it will lock the whole web server up, and all the other requests will block. Take the extreme case of doing a sync action that results in a time-out because the resource has gone offline. If the time-out is 30 seconds, the whole web server is down for 30 seconds. All of your customers are getting time-outs. Every image comes up broken.&lt;br /&gt;&lt;br /&gt;Another nice thing about the Node Beginner tutorial is the links to deeper information... and nested in one of the comments is a link to a paper comparing threads and events: &lt;a href="http://www.usenix.org/events/hotos03/tech/full_papers/vonbehren/vonbehren_html/index.html"&gt;Why Events Are A Bad Idea&lt;/a&gt;&amp;nbsp; Well, their conclusion is in the title, but if you look at their charts the important thing to learn is that well-written event handling code and well-written threading code are basically as quick as each other. (You won't ever reach the right-side of the charts in a real website on a single server; their example is just serving a static image. So the differences are only of interest to academics.)&lt;br /&gt;&lt;br /&gt;But, although dealing with threads is hard, asynchronous programming is even harder, IMHO.&lt;br /&gt;&lt;br /&gt;Now, if node.js had a web server module where it maintained a thread pool and each new request got its own thread, then I could program in a synchronous style in my thread, happy in the knowledge I won't break the web server, and also happy I'll be able to meet my deadlines...&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-648131078896677502?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/648131078896677502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=648131078896677502' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/648131078896677502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/648131078896677502'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/09/nodejs-good-tutorial.html' title='node.js: Good Tutorial'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-8252976506663034593</id><published>2011-09-02T02:10:00.000-07:00</published><updated>2011-09-02T02:10:01.604-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='oauth'/><title type='text'>Using twitter oauth from commandline</title><content type='html'>The twitteroauth library is the most recommended PHP library for using oauth for API access to Twitter. It also supports the commandline approach (which can work completely behind a firewall, no need for a web server to host a callback page), but it is not very well documented.&lt;br /&gt;(Note: when I say &lt;i&gt;behind a firewall&lt;/i&gt; you do still need web access, because you need to login to twitter to get a PIN code; but this is much less demanding than needing to set up a web server on a public IP address.)&lt;br /&gt;&lt;br /&gt;Anyway, after I'd worked it out, I put my &lt;a href="https://github.com/DarrenCook/twitteroauth"&gt;sample code up on github&lt;/a&gt;. The three files to look at are oob1.php, oob2.php and oob3.php. Here is how you use them:&lt;br /&gt;&lt;b&gt;&lt;br /&gt;Step One:&lt;/b&gt;&lt;br /&gt;   Edit config.php, to set:&lt;pre&gt;      define('OAUTH_CALLBACK', 'oob');&lt;/pre&gt;&lt;br /&gt;  (If not already done, go to &lt;a href="https://dev.twitter.com/apps"&gt;https://dev.twitter.com/apps&lt;/a&gt;, create and configure the app, and add the consumer key and secret to config.php.)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step Two:&lt;/b&gt;&lt;pre&gt;   php oob1.php&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Step Three:&lt;/b&gt;&lt;br /&gt;   Visit the URL it tells you to, and approve the application&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step Four:&lt;/b&gt;&lt;pre&gt;   php oob2.php 1234567&lt;/pre&gt;  (where 1234567 is the PIN number you got at the end of step three)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step Five:&lt;/b&gt;&lt;pre&gt;   php oob3.php account/verify_credentials&lt;/pre&gt; (this command will show your account; see the oob3.php source code for other supported commands, and some shortcuts.)&lt;br /&gt;&lt;br /&gt;Let me know if you have any problems with, or questions about, these files.&lt;br /&gt;If you find bugs, or want to encourage its inclusion in the main twitteroauth library, you can comment on the &lt;a href="https://github.com/abraham/twitteroauth/pull/100"&gt;github pull request&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8252976506663034593?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8252976506663034593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8252976506663034593' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8252976506663034593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8252976506663034593'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/09/using-twitter-oauth-from-commandline.html' title='Using twitter oauth from commandline'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7293343056607635405</id><published>2011-08-09T23:54:00.000-07:00</published><updated>2011-09-02T01:53:57.878-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='regexes'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Debugging Regexes</title><content type='html'>Cue: dramatic music. There I was under pressure, enemy fire going off in all directions, and my unit test had started complaining. The test regex was 552 characters long, the text being tested was almost as long, and each run of the unit test takes 30 seconds. Talk about finding a needle in a haystack. James Bond only had to choose between cutting the red or the blue wire. He had it easy.&lt;br /&gt;&lt;br /&gt;But I lived to tell the tale. Playing the Bond Girl in this scenario was &lt;a href="http://www.regextester.com/"&gt;http://www.regextester.com/&lt;/a&gt; (I actually used &lt;a href="http://www.regextester.com/index2.html"&gt;version 2&lt;/a&gt; which, though alpha, worked fine).&lt;br /&gt;&lt;br /&gt;It still wasn't smooth sailing. The above site assumes the regex is surrounded by /.../ but mine wasn't. So, first I had my unit test output the regex, then I escaped it correctly for use with /.../ then pasted it into the Regex Tester. I also pasted in the text to test. It should match; it doesn't. So I put the cursor at the end of my regex and deleted a few characters at a time. After deleting about two-thirds (!!) of it, finally the text turned red and I had a match. I could see exactly where the match stopped and realize what was missing in my regex. I fixed the regex (simultaneously in RegexMatcher and in my unit test script) and repeated. I had to delete back to almost the same point. It took half a dozen passes before the whole regex matched.&lt;br /&gt;&lt;br /&gt;The code looks to be open source javascript. So maybe I will hack on it, to automate the above process (my better Bond Girl, if you like): I would give the regex, the target text, say I expect a match, and it will find the longest regex that matches and show me how much of the target text got matched. (Ah, it uses ajax requests to back-end PHP for the preg and ereg versions, and that code is not available; but at least I could do this for javascript regexes.)&lt;br /&gt;&lt;br /&gt;Enough with poking around inside today's Bond Girl. Down the cocktail, jacket on, back to the field...&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7293343056607635405?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7293343056607635405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7293343056607635405' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7293343056607635405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7293343056607635405'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/08/debugging-regexes.html' title='Debugging Regexes'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-4206409120852243692</id><published>2011-08-01T18:32:00.000-07:00</published><updated>2011-08-04T19:02:49.458-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>svn, ssh, /bin/false, git, /etc, etc.</title><content type='html'>I just spent almost two hours troubleshooting an svn server. It was set up to allow ssh access from a few users over ssh as described in &lt;a href="http://jimmyg.org/blog/2007/subversion-over-svnssh-on-debian.html"&gt;this helpful blog post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Today I realized none of the svn clients could run svn update: "svn: Network connection closed unexpectedly"&lt;br /&gt;&lt;br /&gt;Part of the challenge was that I'd not run "svn update" in 1-2 months, so I had a big time frame for breakage. But all I could remember changing recently was commenting out some lines in my iptables firewall, so I spent quite a while staring at that. And I'd updated some packages and rebooted.&lt;br /&gt;&lt;br /&gt;I spent a lot of time looking at ssh verbose output. To do this use&lt;br /&gt;&lt;pre&gt;export SVN_SSH="ssh -vv"&lt;/pre&gt;(-v for quite verbose, -vv for more, -vvv for even more, etc.) No error messages: it connects, runs the svnserve command, passes up some environmental variables, sends some data, and then simply loses the connection.&lt;br /&gt;&lt;br /&gt;Then I wondered if the svn user had somehow lost permission for something important. It has /bin/false as its shell, so I gave it /bin/bash, used su to become root, then "su - svn". Ran the svnserve command. It is fine. Looked at the repository. It is fine. Ran svnadmin to check for locks. None.&lt;br /&gt;&lt;br /&gt;Giving up, I started crafting an email to post &lt;i&gt;somewhere&lt;/i&gt; asking for help. I went back to get the exact error message svn update was giving me... and it worked. Yep, the other client works now too. My first guess was running svnadmin had quietly fixed something. But then, on a hunch, I changed svn's shell back to /bin/false. Yep, that broke it. It seems the svn user has to have a valid login shell.&lt;br /&gt;&lt;br /&gt;Okay, problem solved. But I'm sure that svn has always had /bin/false, and now I wanted to know if that is true, and if and when it changed. It is too late now, but ready for next time, I decided to put all of /etc into a git repository (no need for a central repository, so git is far better choice than svn for this). The git commands (all run as root) are trivial:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;cd /etc&lt;br /&gt;git init&lt;br /&gt;git add .&lt;br /&gt;git commit -m "Initial files" -a&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;I found this page of &lt;a href="http://pupeno.com/blog/revisioning-etc-with-git/"&gt;someone who has done something similar&lt;/a&gt;, and he suggested sending the git status report from a daily cron, so I did that too.&lt;br /&gt;&lt;br /&gt;It is so easy, and disk space so plentiful, that I think I will do it on all my linux machines.&lt;br /&gt;&lt;br /&gt;UPDATE: /etc has some files that get edited a lot, so to reduce noise this is my .gitignore file so far:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;cups/printers.conf*&lt;br /&gt;cups/subscriptions.conf*&lt;br /&gt;emacs/site-start*&lt;br /&gt;mtab&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;(Use "git rm --cached cups/printers.conf*" (for instance) to stop git tracking the files if they've already been added.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4206409120852243692?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4206409120852243692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4206409120852243692' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4206409120852243692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4206409120852243692'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/08/svn-ssh-binfalse-git-etc-etc.html' title='svn, ssh, /bin/false, git, /etc, etc.'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-8477910637288275764</id><published>2011-07-31T01:19:00.000-07:00</published><updated>2011-07-31T01:19:55.560-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='optimization'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>Rcpp: embedding C++ in R scripts</title><content type='html'>Rcpp is an R package to aid in making R extensions written in C++. It also includes a side-project for embedding R code in a C++ program (including being able to use all the R packages), but most amazing of all is the integration with R's inline package. Let me show you a minimal (but complete) script:&lt;br /&gt;&lt;pre&gt;library('inline')&lt;br /&gt;src='Rcpp::List output(1);output[0]="Hello World";return output;'&lt;br /&gt;fun=cxxfunction(signature(),src,plugin="Rcpp")&lt;br /&gt;fun()&lt;br /&gt;&lt;/pre&gt;Yes, it is the classic Hello World script, the output looks like:&lt;br /&gt;&lt;pre&gt;[[1]]&lt;br /&gt;[1] "Hello World"&lt;br /&gt;&lt;/pre&gt;What do I call this amazing? The contents of &lt;i&gt;src&lt;/i&gt; is the body of a C++ function. The cxxfunction takes your C++ code, sends it to your C++ compiler, compiles it with the necessary headers, and finally embeds it in your R session as an R extension. It just works, there is no catch (*).&lt;br /&gt;&lt;br /&gt;(I could have used a character object instead of a list. The Rcpp::List is just like an R list, meaning each element can be of a different type. If you've always wanted something like this in C++, also take a look at boost::any.)&lt;br /&gt;&lt;br /&gt;The cxxfunction() call takes 1.9s to run on my machine, so the overhead to start-up the compiler and run the compilation process is not too bad.&lt;br /&gt;&lt;br /&gt;Okay, not a motivating example, but the Rcpp project is not just technically amazing, it is also well documented. You can find some more interesting examples in the documentation, and if this has got your interest you will enjoy &lt;a href="http://www.youtube.com/watch?v=UZkaZhsOfT4"&gt;this video (1.5hrs) of a talk&lt;/a&gt; the Rcpp main authors did at Google.&lt;br /&gt;&lt;br /&gt;By the way, if anyone knows of a PHP project for embedding C++ like this, please let me know!&lt;br /&gt;&lt;br /&gt;*: You need to install the &lt;a href="http://cran.r-project.org/web/packages/Rcpp/index.html"&gt;Rcpp&lt;/a&gt; and inline packages, and you need R version 2.12.0 or later. If you are using Ubuntu and your default version of R is too old, there are good instructions here on &lt;a href="http://cran.r-project.org/bin/linux/ubuntu/README"&gt;getting the latest R version&lt;/a&gt; as a deb package.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8477910637288275764?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8477910637288275764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8477910637288275764' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8477910637288275764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8477910637288275764'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/07/rcpp-embedding-c-in-r-scripts.html' title='Rcpp: embedding C++ in R scripts'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-878670203466519911</id><published>2011-07-17T18:55:00.000-07:00</published><updated>2011-07-17T18:55:36.853-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>R: extract column from xts object as a vector</title><content type='html'>A quickie for the R language... you will look at the answer and think it is hardly worth writing a blog post over. Well it turns out that everyone has thought the same... which is why it took me over half an hour of failed searching and trial and error to work it out.&lt;br /&gt;&lt;br /&gt;I've an XTS object, x, and I've added a column which is the sum of the other columns:&lt;br /&gt;&lt;pre&gt;&amp;nbsp; x$sum=apply(x,1,sum)&lt;/pre&gt;&lt;code&gt;print(x$sum)&lt;/code&gt; shows me the sum for each row, along with the datestamp of that row. I.e. &lt;code&gt;x$sum&lt;/code&gt; is still an XTS object. Normally this is good, but I wanted it as a simple vector, without the dates associated. Here is how you do that:&lt;br /&gt;&lt;pre&gt;&amp;nbsp; as.vector(x$sum)&lt;/pre&gt;Why did I want that? Simply because &lt;code&gt;summary(x$sum)&lt;/code&gt; uses up 7 lines, half of it being noise about the datestamps; &lt;code&gt;summary(as.vector(x$sum))&lt;/code&gt; is 2 lines, all signal, no noise.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-878670203466519911?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/878670203466519911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=878670203466519911' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/878670203466519911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/878670203466519911'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/07/r-extract-column-from-xts-object-as.html' title='R: extract column from xts object as a vector'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2435921174233419427</id><published>2011-07-14T17:26:00.000-07:00</published><updated>2011-07-14T17:26:40.069-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>Customize less: less annoying</title><content type='html'>Open ~/.bashrc (or ~/.bash_profile) and add this line at the end:&lt;br /&gt;&lt;pre&gt;LESS=-FRX;export LESS&lt;/pre&gt;Don't ask why, just trust me. ('cos actually I don't know what it means either...)&lt;br /&gt;&lt;br /&gt;This was the magic incantation to tell "git log" to wrap commit messages (instead of just chopping off everything extra).&lt;br /&gt;&lt;br /&gt;But it has had the delightful side-effect of man now works properly. About 6 or 7 years ago I used to be able to type "man whatever", press q, and the advice would stay on screen. Then linux (all distros as far as I could tell) changed to hiding the man page as soon as you press q. Frequently I'd have to have two terminal windows open, simply so I could keep the man page open while I type my command. Yes, you're ahead of me; the above LESS command fixes this. If I'd known it would be so easy I'd have done this years ago!!&lt;br /&gt;&lt;br /&gt;Even better, the help system in the R language was working just like man pages (and it was even more annoying there), and that has been fixed too.&lt;br /&gt;&lt;br /&gt;It is a miracle, I tell you, &lt;b&gt;&lt;i&gt;&lt;span style="font-size: large;"&gt;a miracle!&lt;/span&gt;&lt;/i&gt;&lt;/b&gt; July 13th should go down as "Saint LESS=-FRX" day; in 100 years time it will be a public holiday and children will be taught about it in schools. They might even be taught what it means...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2435921174233419427?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2435921174233419427/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2435921174233419427' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2435921174233419427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2435921174233419427'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/07/customize-less-less-annoying.html' title='Customize less: less annoying'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-5769504505061827166</id><published>2011-07-04T01:58:00.000-07:00</published><updated>2011-07-13T19:42:17.602-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='parallelization'/><category scheme='http://www.blogger.com/atom/ns#' term='threads'/><category scheme='http://www.blogger.com/atom/ns#' term='clusters'/><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>R: foreach parallelization</title><content type='html'>I was experimenting with the &lt;a href="http://cran.r-project.org/web/packages/foreach/index.html"&gt;foreach&lt;/a&gt; and &lt;a href="http://cran.r-project.org/web/packages/doMC/index.html"&gt;doMC&lt;/a&gt; packages in R, which make your code multi-threaded. It actually took a bit of refactoring to use foreach(), as there seems to be no shared memory between the threads - they each took their own copy of the in-scope variables. So I had to return my results as a one-row data frame from each iteration, and combine them when the foreach loop had finished.&lt;br /&gt;&lt;br /&gt;Here are my results; the user CPU column was giving me strange numbers, so I'm just going to list total time spent (wall clock time). I have three different loops, so here are the base timings (running the foreach loop in sequential mode, without doMC loaded) for each:&lt;pre&gt;4.33         7.72         16.14&lt;/pre&gt;My CPU has 4 cores, but the OS sees it as 8 virtual cores. Here are my results for 1, 2, 4 and 8 threads:&lt;pre&gt;1   4.36         7.90         16.20&lt;br /&gt;  2   2.50 [2.18]  4.30 [3.95]   8.80 [8.10]  [8-15% slower]&lt;br /&gt;  4   1.46 [1.09]  2.50 [1.98]   5.06 [4.05]  [25-35% slower]&lt;br /&gt;  8   1.32 [0.55]  2.30 [0.99]   4.30 [2.02]  [110-140% slower]&lt;/pre&gt;&lt;br /&gt;All times are in seconds, and this loop represents most of the time spent in my script, so while the results are a long way from linear, they represent a useful speed-up. The numbers in square brackets show the speeds if I had got linear improvement.&lt;br /&gt;&lt;br /&gt;By the way, my foreach loop had 200 to 250 iterations. The above results tell me that when each foreach loop iteration does more work we get better efficiency. This is fairly course parallelization, which suggests to me that there is lots of room for improvement in the doMC() code.&lt;br /&gt;UPDATE: When running with top, and cores set to 4, I notice 4 new instances of R appear each time it hits the foreach loop, and then they disappear after. I.e. it appears to be using processes, not threads, and creating them on-demand! No wonder my results indicate so much overhead!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-5769504505061827166?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/5769504505061827166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=5769504505061827166' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5769504505061827166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5769504505061827166'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/07/r-foreach-parallelization.html' title='R: foreach parallelization'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2127286317703022037</id><published>2011-07-03T17:29:00.000-07:00</published><updated>2011-07-28T16:16:29.731-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Apache: use both PHP module and PHP-CGI</title><content type='html'>I had a need the other day to configure apache so it uses the PHP Apache module for all directories except one, where I wanted it to use the cgi version of PHP. The apache module is more efficient, but runs as part of the apache process. I wanted one URL to run in its own process. (I was troubleshooting and had a hunch this might help; luckily it did in this case!) Instructions for both Linux and Windows follow.&lt;br /&gt;&lt;br /&gt;On Ubuntu I needed some preparation: 1. install the php5-cgi package (different from php5-cli, which is what we use when using php from the commandline); 2. enable the actions module.&lt;br /&gt;On Windows I was using xampp, which follows an Everything-But-The-Kitchen-Sink philosophy, and all I needed was already there.&lt;br /&gt;&lt;br /&gt;Here is the code I added (to my VirtualHost). First for Ubuntu:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;ScriptAlias /usephpcgi /usr/bin&lt;br /&gt;Action  application/x-httpd-php-cgi  /usephpcgi/php5-cgi&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;And then for Windows XAMPP:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;ScriptAlias /usephpcgi C:/xampp/php/&lt;br /&gt;Action  application/x-httpd-php-cgi  /usephpcgi/php-cgi.exe&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;(That is copy and paste from working configurations, so I think the trailing slash must be optional!)&lt;br /&gt;&lt;br /&gt;Then for both Apache and Windows:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;AddType application/x-httpd-php-cgi .phpcgi&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;Now, I have to admit my dirty secret: I cheated. Instead of enabling php-cgi for all *.php in one directory, I left *.php going to the Apache PHP module, and I created the *.phpcgi extension to use the cgi binary. Initially this was simply because I managed to get it working that way; but on reflection I realized I preferred it: I can switch a script between using php module and php-cgi just by changing the extension; also I can use php-cgi anywhere in my VirtualHost. If that does not sound so useful I should explain the script in question is already hidden between an Alias, something like this, so no public-facing URLs need to change:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;AliasMatch ^/admin/(.*?)/(.*)$ /path/to/admin.phpcgi&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;What about my original plan to configure it for a directory, without changing the file extensions? I had trouble with this, and gave up on this; but Ben kindly left a comment, so now I know how to do it. First, you still need the ScriptAlias and Action directives shown above. Then it is simply this.&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&amp;lt;Directory /var/www/path/to&amp;gt;&lt;br /&gt;    &amp;lt;FilesMatch "\.ph(p3?|tml)$"&amp;gt;&lt;br /&gt;        SetHandler application/x-httpd-php-cgi&lt;br /&gt;    &amp;lt;/FilesMatch&amp;gt;&lt;br /&gt;&amp;lt;/Directory&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;As Ben explains (see comment#1 below) the reason we need the FilesMatch inside the Directory is because mod-php is setting a global FilesMatch directive; and that takes priority over our attempts to use AddType or AddHandler for a directory.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2127286317703022037?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2127286317703022037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2127286317703022037' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2127286317703022037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2127286317703022037'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/07/apache-use-both-php-module-and-php-cgi.html' title='Apache: use both PHP module and PHP-CGI'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-4821194632683513817</id><published>2011-06-21T20:18:00.000-07:00</published><updated>2011-06-21T20:18:44.162-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>R: removing columns in bulk (xts, matrix, data frame, etc.)</title><content type='html'>I've an R object, z, with a number of intermediate-working columns I want to delete. In this case it is a quantmod object, but the solution I will show applies for xts and zoo objects, and also for matrices (which is what xts, etc. are under the surface). It also applies to data frames.&lt;br /&gt;&lt;br /&gt;(By the way, I use = for assignment, not &amp;lt;-, in the below.)  Here is the long-winded approach; setting a column to NULL removes it: &lt;br /&gt;&lt;pre&gt;  z$close1=NULL&lt;br /&gt;  z$close2=NULL&lt;br /&gt;  z$close3=NULL&lt;br /&gt;  z$close4=NULL&lt;br /&gt;  z$close5=NULL&lt;br /&gt;  z$open1=NULL&lt;br /&gt;  z$open2=NULL&lt;br /&gt;  z$open3=NULL&lt;br /&gt;  z$open4=NULL&lt;br /&gt;  z$open5=NULL&lt;br /&gt;&lt;/pre&gt;However using a string for the column name won't work. In other words, these don't work:&lt;br /&gt;&lt;pre&gt;  z['open1']=NULL   #This one does work for data frames though.&lt;br /&gt;  z[['open1']]=NULL&lt;br /&gt;&lt;/pre&gt;The solution I eventually worked out is:&lt;br /&gt;&lt;pre&gt;  z=z[, setdiff(colnames(z),c('close1','close2','close3','close4','close5','open1','open2','open3','open4','open5')) ]&lt;br /&gt;&lt;/pre&gt;colnames(z) gets the current list of columns.&lt;br /&gt;I then use setdiff() to subtract from that the list of columns I want to remove.&lt;br /&gt;The z[,...] syntax means take a copy with just these columns (keeping all rows).&lt;br /&gt;&lt;br /&gt;If you don't understand my motivation, I should first say I had more like 30 fields, not just the 10 shown in the example above. But also I like doing things programmatically, as it can avoid introducing typos. For instance, the above solution could be rewritten as:&lt;br /&gt;&lt;pre&gt;  #Remove the openN and closeN columns&lt;br /&gt;  z=z[, setdiff(colnames(z),c(paste('close',1:5,sep=''),paste('open',1:5,sep='') )) ]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4821194632683513817?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4821194632683513817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4821194632683513817' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4821194632683513817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4821194632683513817'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/06/r-removing-columns-in-bulk-xts-matrix.html' title='R: removing columns in bulk (xts, matrix, data frame, etc.)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3867728492421658320</id><published>2011-06-16T19:48:00.000-07:00</published><updated>2011-06-16T19:48:04.163-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>PHP PDO: so hard to debug</title><content type='html'>I wrote a simple PDO helper function to update fields in a certain database table. The fields are given as the key/value pairs in $d, and my function looked like this:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;$q='UPDATE MyTable SET lastupdate=:lastupdate';&lt;br /&gt;foreach($d as $key=&gt;$value)$q.=', '.$key.'=:'.$key;&lt;br /&gt;$q.=' WHERE username=:username';&lt;br /&gt;&lt;br /&gt;$statement=$dbh-&gt;prepare($q);&lt;br /&gt;foreach($d as $key=&gt;$value)$statement-&gt;bindParam(':'.$key,$value);&lt;br /&gt;$statement-&gt;bindParam(':lastupdate',date('Y-m-d H:i:s'));&lt;br /&gt;$statement-&gt;bindParam(':username',$username);&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;It all looks reasonable doesn't it? Create the SQL, then assign the values to. But it didn't work. My $d array looked like:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;array( 'status'=&gt;'expired', 'mode'=&gt;'' )&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;Instead of getting set to expired, the status field ended up blanked out. Yet lastupdate and username got set. This had me scratching my head for ages.&lt;br /&gt;PDO has a debug function that is next to useless: it tells you the parameters, but not the values you've assigned to them. Incredibly annoying.&lt;br /&gt;&lt;br /&gt;Have you spotted my bug yet?&lt;br /&gt;&lt;br /&gt;Here's the answer. Though all the examples in the PHP documentation use &lt;i&gt;bindParam()&lt;/i&gt;, the function to assign a value is &lt;i&gt;bindValue()&lt;/i&gt;. You should always use &lt;i&gt;bindValue()&lt;/i&gt;, unless you actually need the advanced functionality that &lt;i&gt;bindParam()&lt;/i&gt; gives you. What advanced functionality you wonder? Instead of assigning the value immediately, it attaches a reference, and uses the final value of that reference variable. You're ahead of me: in my foreach loop the $value variable changes on each iteration.&lt;br /&gt;&lt;br /&gt;If PDO had a decent debug function I'd have discovered that in half the time. Oh well, now I know!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3867728492421658320?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3867728492421658320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3867728492421658320' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3867728492421658320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3867728492421658320'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/06/php-pdo-so-hard-to-debug.html' title='PHP PDO: so hard to debug'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-1330173342553958232</id><published>2011-05-26T00:05:00.000-07:00</published><updated>2011-06-24T05:58:26.519-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>svn to git: central repository setup</title><content type='html'>The best thing about git is you can just start working; no messing around setting up the central repository, working out secure access, etc. But the learning curve for git, coming from svn, is much bigger than say from cvs to svn. The thing I'd not got my head around was &lt;i&gt;what do you do when you need that central repository&lt;/i&gt;? For instance, when you have a 2nd developer! I think I've got it now, so here is my guide on it.&lt;br /&gt;&lt;br /&gt;Let's assume it is a website ("mysite"), and we have three machines involved:&lt;br /&gt;devel: where most development is done&lt;br /&gt;store: the central repository server&lt;br /&gt;www: the web server&lt;br /&gt;&lt;br /&gt;The website is on devel currently, under git, and we want to check it out to www.&lt;br /&gt;If not a website, but just two developers, then in this example "www" is the machine of that second developer.&lt;br /&gt;&lt;br /&gt;For simplicity we'll assume all developers have ssh access to the "store" machine. And that all our git repositories will be kept under /var/git/&lt;br /&gt;&lt;br /&gt;1. On store:&lt;br /&gt;cd /var/git/&lt;br /&gt;mkdir mysite.git&lt;br /&gt;cd mysite.git&lt;br /&gt;git init --bare&lt;br /&gt;&lt;br /&gt;(see update#1 below, if your git does not support --bare)&lt;br /&gt;&lt;br /&gt;2. On devel (from the root of the website)&lt;br /&gt;git remote add origin darren@store:/var/git/mysite.git&lt;br /&gt;git push origin master&lt;br /&gt;(the "origin master" part is just needed the first time)&lt;br /&gt;&lt;br /&gt;3. On www, &lt;br /&gt;cd /var/www/&lt;br /&gt;git clone darren@store:/var/git/mysite.git my.example.com&lt;br /&gt;(this creates /var/www/my.example.com and checks out the current version of the site)&lt;br /&gt;(you need to configure your web server to not serve the .git directory, as well as any other files end users should not see)&lt;br /&gt;&lt;br /&gt;4. If we make a change on devel and want to update www.&lt;br /&gt;First on devel, do the commits, then:&lt;br /&gt;git push&lt;br /&gt;Then on www:&lt;br /&gt;git pull&lt;br /&gt;&lt;br /&gt;5. If we make a change on www, and want to update devel.&lt;br /&gt;First on www, do the commits, then:&lt;br /&gt;git push&lt;br /&gt;Then on devel do:&lt;br /&gt;git pull origin master&lt;br /&gt;&lt;br /&gt;Not quite symmetrical is it? To fix this, so in future you just need to do "git pull" from the devel machine too, edit .git/config. You'll see these lines:&lt;br /&gt;&lt;br /&gt;[remote "origin"]&lt;br /&gt;url = darren@store:/var/git/mysite.git&lt;br /&gt;fetch = +refs/heads/*:refs/remotes/origin/*&lt;br /&gt;&lt;br /&gt;Don't touch them but, just above them, add this block:&lt;br /&gt;&lt;br /&gt;[branch "master"]&lt;br /&gt;remote = origin&lt;br /&gt;merge = refs/heads/master&lt;br /&gt;&lt;br /&gt;(unless you are doing something clever, you can use that verbatim). Now "git pull" works without having to specify "origin master" any more.&lt;br /&gt;&lt;br /&gt;So, "git commit ...;git push" is like "svn commit ...". And "git pull" is like "svn checkout".&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;UPDATE #1: Older versions of git don't have the --bare function. Do these steps (on "store" machine) instead:&lt;blockquote&gt;&lt;pre&gt;cd /var/git/&lt;br /&gt;mkdir mysite.git&lt;br /&gt;cd mysite.git&lt;br /&gt;git init&lt;br /&gt;mv .git/* .&lt;br /&gt;rmdir .git&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;UPDATE #2: What to do if you already have some files on www, with some differences from what is in git? E.g. I had a test and live version of a website, with no source control; the test version had a number of extra files. I scp-ed the live version to devel, created a git repository. Then I set it up and push-ed it to "store". Then on my test site, I had to do these steps:&lt;blockquote&gt;&lt;pre&gt;#Temporarily move current test files out of the way&lt;br /&gt;mv my.example.com{,.old}&lt;br /&gt;#Get the git version&lt;br /&gt;git clone darren@store:/var/git/mysite.git my.example.com&lt;br /&gt;#Merge in the test site&lt;br /&gt;cd my.example.com&lt;br /&gt;cp -a ../my.example.com.old/* .&lt;br /&gt;#See what is different&lt;br /&gt;git status&lt;br /&gt;#For all changed files, revert to the git (live server) version&lt;br /&gt;git reset --hard&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;By the end of all this, git status will just list (as untracked) the files that were on test but were not on live.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1330173342553958232?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1330173342553958232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1330173342553958232' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1330173342553958232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1330173342553958232'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/05/svn-to-git-central-repository-setup.html' title='svn to git: central repository setup'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-1136364286647422292</id><published>2011-05-24T21:28:00.000-07:00</published><updated>2011-05-24T21:28:22.680-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>PHP, PDO, SQLite, mysterious lock problem</title><content type='html'>Let's start with the conclusion:&lt;br /&gt;If doing a prepare() or query() with PDO and sqlite, and then you want to do something else with sqlite in &lt;i&gt;the same PHP function&lt;/i&gt; then unset the first PDOStatement, before trying to do that something else.&lt;br /&gt;&lt;br /&gt;As an example here is my code, to get a unique ID ($dbh is a PDO connection to an sqlite):&lt;br /&gt;&lt;br /&gt;function get_next_id($dbh){&lt;br /&gt;$q='SELECT next FROM MyNextId';&lt;br /&gt;$obj=$dbh-&amp;gt;query($q);   //Throws on error&lt;br /&gt;$d=$obj-&amp;gt;fetch(PDO::FETCH_NUM);&lt;br /&gt;$next_id=$d[0];&lt;br /&gt;&lt;br /&gt;$q='UPDATE MyNextId SET next=next+1';&lt;br /&gt;$row_count=$dbh-&amp;gt;exec($q);   //Throws on error&lt;br /&gt;if($row_count==0)throw new Exception("Failed to execute ($q)\n");&lt;br /&gt;&lt;br /&gt;return $next_id;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;It works on Ubuntu 10.04, with sqlite 3.6.22, but fails on Centos 5.6, with sqlite 3.3.6, with this message:&lt;br /&gt;exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 6 database table is locked'&lt;br /&gt;&lt;br /&gt;I went through the whole changelog from 3.3.6 to 3.6.22, but got no clues (though I am now impressed with how active and organized the sqlite development is). But finally I tracked down this article on &lt;a href="http://technosophos.com/content/sqlite-database-table-locked-errors-pdo"&gt;someone getting similar errors&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And that was it. I could have used $obj-&gt;&lt;a href="http://php.net/manual/en/pdostatement.closecursor.php"&gt;closeCursor()&lt;/a&gt;, but deleting $obj is just as good:&lt;br /&gt;&lt;br /&gt;function get_next_id($dbh){&lt;br /&gt;$q='SELECT next FROM MyNextId';&lt;br /&gt;$obj=$dbh-&amp;gt;query($q);   //Throws on error&lt;br /&gt;$d=$obj-&amp;gt;fetch(PDO::FETCH_NUM);&lt;br /&gt;$next_id=$d[0];&lt;br /&gt;&lt;div style="color: red;"&gt;unset($obj);&lt;/div&gt;&lt;br /&gt;$q='UPDATE MyNextId SET next=next+1';&lt;br /&gt;$row_count=$dbh-&amp;gt;exec($q);   //Throws on error&lt;br /&gt;if($row_count==0)throw new Exception("Failed to execute ($q)\n");&lt;br /&gt;&lt;br /&gt;return $next_id;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;If you are doing just one PDO action per function then there is no need, because exiting the function will automatically do the unset.&lt;br /&gt;&lt;br /&gt;(I don't know why this is a problem on sqlite 3.3.6 but not sqlite 3.6.22... in fact, I suspect it may be due a difference in the PDO or PHP version or configuration instead. Apologies for the loose end!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1136364286647422292?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1136364286647422292/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1136364286647422292' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1136364286647422292'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1136364286647422292'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/05/php-pdo-sqlite-mysterious-lock-problem.html' title='PHP, PDO, SQLite, mysterious lock problem'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2134485366998216017</id><published>2011-05-22T03:10:00.000-07:00</published><updated>2011-05-22T03:10:12.434-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='web services'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>oauth for php (and Ubuntu)</title><content type='html'>There is a "standard" PHP OAuth library, &lt;a href="http://php.net/manual/en/book.oauth.php"&gt;documented in the manual&lt;/a&gt;, and which is installed via pecl. There is no package shortcut under Ubuntu; you still have to use pecl&lt;br /&gt;  sudo pecl install oauth&lt;br /&gt;&lt;br /&gt;If you get a complaint about no "phpize", install the "php5-dev" ubuntu package. And then if you get an error in "php_pcre.h" when compiling, then you need to install the "libpcre3-dev" ubuntu package.&lt;br /&gt;&lt;br /&gt;Finally, you need to enable it. Still as root:&lt;br /&gt;  cd /etc/php5/conf.d&lt;br /&gt;  echo "extension=oauth.so" &gt;oauth.ini&lt;br /&gt;&lt;br /&gt;(This creates a config file just for oauth; you could also simply put the extension line in php.ini.)&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Finally,&lt;/i&gt; "php -m" should list "OAuth", and you can create a OAuth object in your php scripts.&lt;br /&gt;&lt;br /&gt;Ubuntu package manager lists "liboauth-php"; the minimal information, and the lack of mention of pecl, should have given me the clue that it is something different.&lt;br /&gt;&lt;br /&gt;Also different is this: &lt;a href="http://code.google.com/p/oauth-php/"&gt;http://code.google.com/p/oauth-php/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2134485366998216017?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2134485366998216017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2134485366998216017' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2134485366998216017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2134485366998216017'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/05/oauth-for-php-and-ubuntu.html' title='oauth for php (and Ubuntu)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2174887362849857347</id><published>2011-05-16T19:26:00.000-07:00</published><updated>2011-05-16T19:26:37.153-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='thunderbird'/><title type='text'>One man's feature request...</title><content type='html'>...is another man's bug.&lt;br /&gt;Since Thunderbird 3, if you selected part of a mail then hit reply, only your selected part gets quoted. Incredibly annoying for those of us who think out loud with our mouse.&lt;br /&gt;&lt;br /&gt;But it can be switched off. Edit|Preferences, Advanced tab, Config Editor. Then in the search box type:&lt;br /&gt;   mailnews.reply_quoting_selection&lt;br /&gt;Change it from true to false (double-click that line is enough).&lt;br /&gt;&lt;br /&gt;It turns out this was actually a Thunderbird feature request, dating from 2000! https://bugzilla.mozilla.org/show_bug.cgi?id=23394&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2174887362849857347?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2174887362849857347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2174887362849857347' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2174887362849857347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2174887362849857347'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/05/one-mans-feature-request.html' title='One man&apos;s feature request...'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-1361829689223197672</id><published>2011-02-07T00:49:00.000-08:00</published><updated>2011-03-02T04:50:06.435-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Zend Framework'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>ZendForm: inserting static text</title><content type='html'>I'm not a fan of Zend Form, or Zend Framework generally: too much structure, too much work to do anything slightly unusual. Trying to add static text to your form epitomizes this. Note, you have to work through the $form object, as in the view page this is all there is:&lt;br /&gt;&lt;pre&gt;&amp;nbsp; &amp;lt;?=$form ?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you are using php 5.3, there is a quite reasonable solution:&lt;br /&gt;&lt;pre&gt;&amp;nbsp; $form-&amp;gt;addElement('text','whatever_id',array(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'decorators'=&amp;gt;array(array('Callback',array('callback'=&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; function(){return "&amp;lt;h3&amp;gt;Whatever&amp;lt;/h3&amp;gt;";}))),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yes, that is as minimal as I can get it, and yes, you must give a 'whatever_id' that is unique on your form, but you have complete control over what is output, and nothing else gets output.&lt;br /&gt;&lt;br /&gt;If you are using php 5.2, there are two approaches. The first way is equivalent to the above, just slightly more verbose:&lt;br /&gt;&lt;pre&gt;&amp;nbsp; $form-&amp;gt;addElement('text','whatever_id',array(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'decorators'=&amp;gt;array(array('Callback',array('callback'=&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; create_function('','return "&amp;lt;h3&amp;gt;Whatever&amp;lt;/h3&amp;gt;";')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ))),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The second way is a different approach:&lt;br /&gt;&lt;pre&gt;&amp;nbsp; $form-&amp;gt;addElement('text','whatever_id',array(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'label'=&amp;gt;E('&amp;lt;h3&amp;gt;Whatever&amp;lt;/h3&amp;gt;'),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'helper'=&amp;gt;'formNote',&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 'decorators'=&amp;gt;array(array('Label',array('escape'=&amp;gt;false))),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yes, four lines, and every line matters. Here is what gets output:&lt;br /&gt;&amp;nbsp; &amp;lt;label class="optional" for="whatever_id"&amp;gt;&amp;lt;/label&amp;gt;&amp;lt;h3&amp;gt;Whatever&amp;lt;/h3&amp;gt;&lt;br /&gt;&lt;br /&gt;I cannot stop the &amp;lt;label&amp;gt; tag appearing; If you can, let me know. (It does not appear to the end user, just in the HTML source, so this is not a serious issue.)&lt;br /&gt;&lt;br /&gt;(2011-03-02: Edited to show the create_function() approach in php 5.2)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1361829689223197672?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1361829689223197672/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1361829689223197672' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1361829689223197672'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1361829689223197672'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/02/zendform-inserting-static-text.html' title='ZendForm: inserting static text'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-8713901543368976115</id><published>2011-01-28T16:14:00.000-08:00</published><updated>2011-01-28T16:14:12.038-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer go'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><title type='text'>Using Amazon EC2 for Shodan Go Bet</title><content type='html'>I've put up a short article on how Amazon EC2 was used for the &lt;a href="http://dcook.org/gobet/"&gt;Shodan Go Bet&lt;/a&gt; event held at the end of December 2010:&lt;br /&gt;&lt;a href="http://dcook.org/gobet/using_amazon_ec2.html"&gt;http://dcook.org/gobet/using_amazon_ec2.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It is not about computer go, but instead will be of interest if you have wanted to see a concrete example of how EC2 is used, just how fast the fastest choice is, exactly how much it costs, how the Amazon EC2 Windows instances work (I used remote desktop running on linux!), etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8713901543368976115?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8713901543368976115/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8713901543368976115' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8713901543368976115'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8713901543368976115'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/01/using-amazon-ec2-for-shodan-go-bet.html' title='Using Amazon EC2 for Shodan Go Bet'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2770681201963540377</id><published>2011-01-18T20:45:00.000-08:00</published><updated>2011-01-18T20:45:15.148-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>Buffalo Air Station setup on existing home LAN</title><content type='html'>I found this surprisingly difficult; though it turns out the steps involved are quite easy...once you know how.&lt;br /&gt;&lt;br /&gt;For this article I'll assume your existing LAN is 10.0.0.0/8 with 10.0.0.1 as the default gateway. I'm going to give the wireless LAN router 10.1.2.3. Substitute 10.0.0.1 and 10.1.2.3 below for something suitable for your LAN (and change the 255.0.0.0 network mask accordingly).&lt;br /&gt;&lt;br /&gt;These instructions assume factory reset status, with the "router" switch (on the case) set to "On", rather than "Off" or "Auto". The AirStation is connected to the LAN hub using the Internet socket (the blue one).&lt;br /&gt;&lt;br /&gt;1. Connect a computer to the Air Station directly (with ethernet cable, to one of the four LAN sockets), and set your computer to use DHCP, so that it gets an IP address that can see the Air Station! (Most likely your computer will receive 192.168.11.2.)&lt;br /&gt;Note: The computer you connect with does not need to have a wireless card in it; it does have to have wired ethernet though.&lt;br /&gt;Tip: Don't use your main computer for this step; that way you will be able to still use your main computer for: a) googling when you hit problems; b) ping tests (see step 5 below).&lt;br /&gt;&lt;br /&gt;2. Connect by browser to 192.168.11.1&lt;br /&gt;   Use root as the username, with blank password.&lt;br /&gt;&lt;br /&gt;3. Internet/LAN | Internet&lt;br /&gt;  IP Manual:  10.1.2.3&lt;br /&gt;              255.0.0.0&lt;br /&gt;  Extended:&lt;br /&gt;    Default GW: 10.0.0.1&lt;br /&gt;           DNS: (whatever DNS server you use: this is what will be handed out to wireless clients that connect using DHCP)&lt;br /&gt;&lt;br /&gt;4. Wait for it to restart.  (I also enabled pings at this point too.)&lt;br /&gt;&lt;br /&gt;5. Then cycle the power; this appears to be essential.&lt;br /&gt;Now you should be able to ping 10.1.2.3 from outside, and also ping from the connected computer to 10.0.0.1. And if you can do that then you can also connect to internet! You are done, time for a nice cuppa.&lt;br /&gt;&lt;br /&gt;6. Test from a wireless device to make sure you are required to type in the key.&lt;br /&gt;&lt;br /&gt;7. Set a root password.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2770681201963540377?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2770681201963540377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2770681201963540377' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2770681201963540377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2770681201963540377'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2011/01/buffalo-air-station-setup-on-existing.html' title='Buffalo Air Station setup on existing home LAN'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-382511335897066260</id><published>2010-12-07T16:45:00.000-08:00</published><updated>2010-12-07T18:10:10.480-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>scp with multiple targets: ssh-add</title><content type='html'>I sometimes have the need to upload the same file to two or more locations on the target server. In fact I even have a script to help me, which goes something like this:&lt;pre&gt;scp file1 file2 remote01:/path/to/somewhere/&lt;br /&gt; scp file1 file2 remote01:/path/to/another/&lt;/pre&gt;The problem is I need to type the password for remote01 twice. I had never managed to find some clever scp syntax to allow specifying two destinations, and &lt;a href="http://lists.tlug.jp/ML/1011/msg00034.html"&gt;a post on the TLUG mailing list&lt;/a&gt; confirmed that. But what I did learn from TLUG is that there is something called ssh-agent that can store passphrases for key pairs; this plugged a gap in my knowledge. There are three ways to login in via ssh/scp:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Give your password&lt;/li&gt;&lt;li&gt;Make a keypair with no passphrase&lt;/li&gt;&lt;li&gt;Make a keypair with a passphrase&lt;/li&gt;&lt;/ol&gt;The second way is used to allow scp from cron jobs, to automate copying files between two machines. I'd never really got the point of the third way: if you have to type a passphrase why not just use the first method, and save messing around making a key pair. (Well, yes, there is better security: a log-in then requires both something I have and something I know; you have to turn off PasswordAuthentication on your ssh server for this to have any meaning though. Thanks to Kalin for this comment.)&lt;br /&gt;&lt;br /&gt;But it turns out there is this program called ssh-agent that remembers passphrases for you. And I found it is already running in the background on Ubuntu.&lt;br /&gt;&lt;br /&gt;Enough chat, let's look at the solution. First, I created a one-off keypair for this script, on my machine, using something like this:&lt;pre&gt;cd ~/.ssh&lt;br /&gt;  ssh-keygen -t dsa -C me@example.com -f key_for_remote01&lt;br /&gt;  chmod 600 key_for_remote01*&lt;br /&gt;  scp -p key_for_remote01.pub remote01:~&lt;/pre&gt;When generating the keypair give a reasonably secure passphrase (you will have to type it in each time, and it is only of use to people in possession of key_for_remote1, so no need for a 20-random-character monster; I believe it is perfectly fine for it to be the same as your normal ssh login password for remote01).&lt;br /&gt;&lt;br /&gt;Then log in to remote01 and append key_for_remote01.pub to ~/.ssh/authorized_keys. If that file does not exist then you can just rename key_for_remote01.pub to authorized_keys and move it into ~/.ssh/&lt;br /&gt;&lt;br /&gt;(By the way, there is no need to put your private half of the keypair in ~/.ssh/ but that seems as good as place as anywhere else.)&lt;br /&gt;&lt;br /&gt;Now, I modified my script as follows:&lt;pre&gt;ssh-add -t 120 ~/.ssh/key_for_remote01&lt;br /&gt; scp -i ~/.ssh/key_for_remote01 file1 file2 remote01:/path/to/somewhere/&lt;br /&gt; scp -i ~/.ssh/key_for_remote01 file1 file2 remote01:/path/to/another/&lt;br /&gt; ssh-add -d ~/.ssh/key_for_remote01&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What happens is the ssh-add line will ask you for your passphrase. The two scp lines then work automatically. Finally the &lt;code&gt;ssh-add -d&lt;/code&gt; stops it caching your passphrase (forcing you to type your passphrase each time you run this script).&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;-t 120&lt;/code&gt; parameter says the passphrase will expire after two minutes. This is just in case the batch file doesn't complete and so does not get chance to run &lt;code&gt;ssh-add -d&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; you can use that same key pair for other machines. Basically anywhere you put the *.pub half of the key pair will let you login. And you can login from anywhere you have the private half of the key pair.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; the timeout/deletion code above is deliberate for this application, but you don't have to do it this way. By allowing it to cache it permanently you would only be prompted for your passphrase once, and then all future ssh and scp logins would be automatic. They will be cleared when you log-out of gnome (on ubuntu, at least) or shutdown your machine.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; If you don't want to specify -i each time you use ssh/scp then you can add an entry to your ~/.ssh/config file, like this:&lt;pre&gt;Host remote01&lt;br /&gt;        Hostname 10.1.2.3&lt;br /&gt;        Port 22&lt;br /&gt;        IdentityFile ~/.ssh/key_for_remote01&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; I used -C with my real email address. This is put in the public key, and I wanted the administrator of remote01 to know who put the key there. Without -C it defaulted to "myusername@myhost". The administrator of remote01 knows nothing about my machine names so this seemed unreasonable and I decided to use -C. But the advantage of that default is it seems ssh-agent knows about that name and will prompt automatically the first time you try to use ssh/scp, which means there is no need to run ssh-add first. I have not worked out yet if ssh-agent can be told to know about my email address too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-382511335897066260?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/382511335897066260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=382511335897066260' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/382511335897066260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/382511335897066260'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/12/scp-with-multiple-targets-ssh-add.html' title='scp with multiple targets: ssh-add'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3784943146686221696</id><published>2010-11-28T18:42:00.000-08:00</published><updated>2010-11-28T18:42:43.140-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Troubleshooting php and xinetd with strace</title><content type='html'>In &lt;a href="http://darrendev.blogspot.com/2010/11/php-53-ticks-pcntlsignal.html"&gt;a previous entry&lt;/a&gt; I mentioned I was trying to track down a difference in a php script between two machines. I've now got closer, close enough in fact to fix the problem but not to understand it. As a troubleshooter, the big discovery for me was how useful strace can be.&lt;br /&gt;&lt;br /&gt;First, a PHP code snippet:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;while(1){&lt;br /&gt;stream_set_blocking(STDIN,true);&lt;br /&gt;$s=trim(fgets(STDIN));&lt;br /&gt;if($GLOBALS['log_fp'])fwrite($GLOBALS['log_fp'],$s."\n");&lt;br /&gt;$params=explode(" ",$s);&lt;br /&gt;$command=array_shift($params);&lt;br /&gt;...&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;The first two lines say listen on stdin for a command, and wait forever for it to arrive. When it does we log it and then split it up into a command and parameters.&lt;br /&gt;&lt;br /&gt;This script is used over xinetd. Xinetd listens on a port for us, and when it gets something it feeds it to stdin; php script output is then fed back over the socket to the client.&lt;br /&gt;&lt;br /&gt;Here is the setup:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;machine A: ubuntu 8.04, 32-bit, php 5.2&lt;/li&gt;&lt;li&gt;machine B: ubuntu 10.04, 64-bit, php 5.3&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Both machines run an indentical version of xinetd: "xinetd Version 2.3.14 libwrap loadavg".&lt;br /&gt;&lt;br /&gt;Machine A runs fine, machine B sometimes sits there. Analyzing this some more, by connecting over telnet to machine B:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I send a command&lt;/li&gt;&lt;li&gt;It replies straightaway&lt;/li&gt;&lt;li&gt;After 60 seconds or so it gives an error message&lt;/li&gt;&lt;li&gt;If I do nothing this error message appears again every 60 seconds.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;On machine A, or if I run the php script directly on machine B, it behaves like this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I send a command&lt;/li&gt;&lt;li&gt;It replies straightaway&lt;/li&gt;&lt;li&gt;It sits there forever waiting for input&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Out of desperation I ran strace (using -p you can attach it to a running process; use ps -a | grep php to find the PID), and I was pleasantly surprised to see it was not too verbose.&lt;br /&gt;&lt;br /&gt;Over xinetd on machine B (the problem configuration) the interesting snippet is:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;...&lt;br /&gt;write(1, "mydata\n", 7)                 = 7&lt;br /&gt;fcntl(0, F_GETFL)                       = 0x2 (flags O_RDWR)&lt;br /&gt;fcntl(0, F_SETFL, O_RDWR)               = 0&lt;br /&gt;poll([{fd=0, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 0 (Timeout)&lt;br /&gt;write(3, "\n", 1)                       = 1&lt;br /&gt;write(5, "\n", 1) &lt;br /&gt;...&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;On machine A over xinetd it instead looks like this:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;...&lt;br /&gt;write(1, "mydata\n", 7)                 = 7&lt;br /&gt;fcntl64(0, F_GETFL)                     = 0x8002 (flags O_RDWR|O_LARGEFILE)&lt;br /&gt;fcntl64(0, F_SETFL, O_RDWR|O_LARGEFILE) = 0&lt;br /&gt;read(0,&lt;/pre&gt;&lt;/blockquote&gt;(Yes nothing comes after the "0," until I send some input.)&lt;br /&gt;&lt;br /&gt;And direct access to the php script on machine B it practically the same:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;...&lt;br /&gt;write(1, "mydata\n", 7)                 = 7&lt;br /&gt;fcntl(0, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)&lt;br /&gt;fcntl(0, F_SETFL, O_RDWR|O_LARGEFILE)   = 0&lt;br /&gt;read(0,&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;So, the cause is clear: fgets() blocks for only 60 seconds instead of blocking forever, but only on this machine and only over xinetd. And the bug in my script then becomes clear: I'm not prepared to handle blank input!&lt;br /&gt;&lt;br /&gt;Here is my fix:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;while(1){&lt;br /&gt;stream_set_blocking(STDIN,true);&lt;br /&gt;$s=trim(fgets(STDIN));&lt;br /&gt;&lt;span style="color: red;"&gt;if($s=='')continue;&lt;/span&gt;&lt;br /&gt;if($GLOBALS['log_fp'])fwrite($GLOBALS['log_fp'],$s."\n");&lt;br /&gt;$params=explode(" ",$s);&lt;br /&gt;$command=array_shift($params);&lt;br /&gt;...&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;As an ironic postscript, the &lt;i&gt;real&lt;/i&gt; problem was elsewhere (a mismatch in another program version and the configuration being used for it!), and I also used strace to track that mistake down. But, what was so important about fixing the above was that it stopped distracting me with an error message every 60 seconds on machine B, when the configuration error was actually on machine A!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3784943146686221696?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3784943146686221696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3784943146686221696' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3784943146686221696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3784943146686221696'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/11/troubleshooting-php-and-xinetd-with.html' title='Troubleshooting php and xinetd with strace'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6841606999049615042</id><published>2010-11-27T18:22:00.000-08:00</published><updated>2010-11-27T18:23:30.507-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>php 5.3, ticks, pcntl_signal, pcntl_signal_dispatch</title><content type='html'>I'm trying to track down a problem with a script that works on php 5.2 but behaves strangely on php 5.3 (there are lots of differences between the environments, and I suspect php version will actually turn out to be completely unrelated). php 5.3 introduced &lt;a href="http://www.php.net/manual/en/function.pcntl-signal-dispatch.php"&gt;pcntl_signal_dispatch()&lt;/a&gt; which processes outstanding signals and I've been investigating if that could somehow explain the behaviour differences I see.&lt;br /&gt;&lt;br /&gt;The confusing part is that I've seen people saying that the old way of "declare(ticks=1)" is now deprecated in 5.3, and you must use pcntl_signal_dispatch(). This seemed very silly as you'd have to litter your code with calls to pcntl_signal_dispatch(), as well as have very different code for php 5.2 and 5.3.&lt;br /&gt;&lt;br /&gt;If you're confused, like I was, here is what you need to know:&lt;br /&gt;1. declare(ticks=1) still works: no deprecated message (with E_ALL|E_STRICT error reporting);&lt;br /&gt;&lt;br /&gt;2. My script, using ticks, behaves identically under 5.2 and 5.3 when I send it a ctrl-C or a kill signal;&lt;br /&gt;&lt;br /&gt;3. The docs don't mention it being deprecated; I realized everywhere saying this was user-contributed comments or blogs!&lt;br /&gt;&lt;br /&gt;There is a performance aspect with using declare(ticks=1). I believe it is minor, but I think pcntl_signal_dispatch() has been introduced so you can use it instead of ticks if you want to take fine-control over when signals get considered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6841606999049615042?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6841606999049615042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6841606999049615042' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6841606999049615042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6841606999049615042'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/11/php-53-ticks-pcntlsignal.html' title='php 5.3, ticks, pcntl_signal, pcntl_signal_dispatch'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-7230189062212680919</id><published>2010-11-25T19:57:00.000-08:00</published><updated>2010-11-25T19:58:46.583-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='regexes'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>bash tips: meld on program output</title><content type='html'>Here is the challenge: I want to run diff on two large log files, but I'm only interested in the entries at a certain time in each log file.&lt;br /&gt;&lt;br /&gt;This used to require four commands:&lt;pre&gt;grep "ABC" a.txt &gt;tmp1.txt&lt;br /&gt;  grep "ABC" b.txt &gt;tmp2.txt&lt;br /&gt;  diff tmp1.txt tmp2.txt&lt;br /&gt;  rm tmp1.txt tmp2.txt&lt;/pre&gt;(Imagine "ABC" is a datestamp, but it could be any other way to filter your log file.)&lt;br /&gt;&lt;br /&gt;Thanks to the gurus on &lt;a href="http://tlug.jp"&gt;TLUG's&lt;/a&gt; mailing list I can now do this as a one-liner:&lt;pre&gt;diff &lt;(grep ABC a.txt) &lt;(grep ABC b.txt)&lt;/pre&gt;It works perfectly for meld (a wonderful visual diff program) too!Here is another way to use it, to compare the output of a program on two different machines (here I'm comparing the php configuration):&lt;pre&gt;diff &lt;(php -i) &lt;(ssh someserver 'php -i')&lt;/pre&gt;We're using the form of ssh that runs a program on the remote server.The command in the brackets can get quite complex. Here is an example where I needed to compare datestamps in two csv files, but the first field was an id number, arbitrary, and therefore different for all records.&lt;pre&gt;diff &lt;(egrep -o '[0-9]{8} [0-9]{2}:[0-9]{2}:[0-9]{2}' dir1/abc.csv)&lt;br /&gt;  &lt;(egrep -o '[0-9]{8} [0-9]{2}:[0-9]{2}:[0-9]{2}' dir2/abc.csv)&lt;/pre&gt;The -o flag to egrep tells it to only output the matching part, not the whole line. This next version shows the complete rest of the line starting with my datestamp field; i.e. this version just excludes the csv field(s) before the datestamp field:&lt;pre&gt;diff &lt;(egrep -o '[0-9]{8} [0-9]{2}:[0-9]{2}:[0-9]{2}.+' dir1/abc.csv)&lt;br /&gt;  &lt;(egrep -o '[0-9]{8} [0-9]{2}:[0-9]{2}:[0-9]{2}.+' dir2/abc.csv)&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7230189062212680919?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7230189062212680919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7230189062212680919' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7230189062212680919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7230189062212680919'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/11/bash-tips-meld-on-program-output.html' title='bash tips: meld on program output'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7678453537803973106</id><published>2010-10-18T19:54:00.000-07:00</published><updated>2010-11-22T22:03:52.014-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>Bash loops</title><content type='html'>Faced with having to write 160 commands by hand (to split 5000 files into subdirectories, based on the first 3 digits of their 6 digit filenames) I quickly learnt bash loops instead:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;for i in `seq 102 189`;&lt;br /&gt;  do&lt;br /&gt;    mkdir $i&lt;br /&gt;    mv $i???.sgf $i/&lt;br /&gt;  done  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I.e. it does commands like this:&lt;br /&gt;&lt;pre&gt;mkdir 102&lt;br /&gt;  mv 102???.sgf 102/&lt;br /&gt;  mkdir 103&lt;br /&gt;  mv 103???.sgf 103/&lt;br /&gt;  ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;UPDATE: In a comment, traxplayer kindly explained how I would make, for instance a filename like 120abc.txt; obviously writing $iabc.txt won't work. The trick is to write $i as ${i}. E.g.&lt;pre&gt;mv ${i}abc.sgf $i/&lt;/pre&gt;&lt;br /&gt;I just needed to use it, and it worked! This bash script tries to find the last reference to each of XXX06..XXX20 in two logfiles.&lt;br /&gt;&lt;pre&gt;rm lastorder.log&lt;br /&gt;touch lastorder.log&lt;br /&gt;&lt;br /&gt;for i in `seq 6 9`;&lt;br /&gt;    do&lt;br /&gt;        grep -h XXX0${i} order_verbose.log.old order_verbose.log | tail -1 &gt;&gt; lastorder.log&lt;br /&gt;    done&lt;br /&gt;&lt;br /&gt;for i in `seq 10 20`;&lt;br /&gt;    do&lt;br /&gt;        grep -h XXX${i} order_verbose.log.old order_verbose.log | tail -1 &gt;&gt; lastorder.log&lt;br /&gt;    done&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By the way, traxplayer also said he prefers to use $(...) instead of `...` for readability:&lt;pre&gt;  for i in $(seq 102 189);&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7678453537803973106?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7678453537803973106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7678453537803973106' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7678453537803973106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7678453537803973106'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/10/bash-loops.html' title='Bash loops'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6725918658402658549</id><published>2010-09-13T20:09:00.000-07:00</published><updated>2010-09-13T20:09:26.567-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>When bash history no longer works</title><content type='html'>I've been pulling my hair out: I have all the HISTORY variables set correctly, but history never survives to the next session. I thought it must be an Ubuntu 10.04 bug, as only that machine, but couldn't track down anyone reporting the same problem.&lt;br /&gt;&lt;br /&gt;The breakthrough came when I stopped typing "history" to view the history, and decided to "cat ~/.bash_history". I got permission denied... and discovered the file was owned by root. Suddenly it all made sense.&lt;br /&gt;&lt;br /&gt;(By the way, I've a bunch of blog articles about setting up a new Ubuntu dual boot, RAIDed, encrypted machine; coming soon to an RSS feed near you...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6725918658402658549?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6725918658402658549/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6725918658402658549' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6725918658402658549'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6725918658402658549'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/09/when-bash-history-no-longer-works.html' title='When bash history no longer works'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2617979607157755276</id><published>2010-08-22T18:34:00.000-07:00</published><updated>2010-08-22T18:34:46.830-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Doctrine ORM'/><title type='text'>Putting parentheses into complex doctrine sql queries</title><content type='html'>Consider this query:&lt;br /&gt;&lt;blockquote&gt;SELECT * FROM mytable WHERE&lt;br /&gt;&amp;nbsp; ( status IN ('A','B') OR access IN ('X','Y') )&lt;br /&gt;&amp;nbsp; AND created_at &amp;gt; '2010-01-01';&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Doctrine offers no way to do this! The following code makes a query without the parentheses, meaning it will return all records with status of A or B, whatever their creation date.&lt;br /&gt;&lt;blockquote&gt;$q=Doctrine_Query::create() -&amp;gt;from('mytable');&lt;br /&gt;$q-&amp;gt;whereIn('status',$statusList);&lt;br /&gt;$q-&amp;gt;orWhereIn('access',$accessList);&lt;br /&gt;$q-&amp;gt;andWhere('created&amp;gt;?','2010-01-01');&lt;br /&gt;&lt;/blockquote&gt;If your IN lists are of fixed length you could do it like this (untested):&lt;br /&gt;&lt;blockquote&gt;$q=Doctrine_Query::create() -&amp;gt;from('mytable');&lt;br /&gt;$q-&amp;gt;where('( status IN (?,?) OR access in (?,?) )',array('A','B','X','Y'));&lt;br /&gt;$q-&amp;gt;andWhere('created&amp;gt;?','2010-01-01');&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;But a far better solution is suggested on &lt;a href="http://danielfamily.com/techblog/?p=37"&gt;Scott Daniel's blog&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I changed his code in two ways. First I put it straight into Doctrine/Query.php, which saves me having to change the class name in my source code. I'm comfortable doing this as I have the Doctrine source in SVN, meaning I won't lose my change when updating Doctrine. Secondly I used an explicit reference to save having to reassign the variable. I doubt there is any difference in speed, but this way looks clearer to me. So the full code I put at the end of Query.php looks like this:&lt;br /&gt;&lt;blockquote&gt;/**&lt;br /&gt;* Custom addition to Doctrine, to allow wrapping a set of OR clauses&lt;br /&gt;* in parentheses, so that they can be combined with AND clauses.&lt;br /&gt;*&lt;br /&gt;* @see http://danielfamily.com/techblog/?p=37&lt;br /&gt;*    I modified it slightly to use an explicit reference.&lt;br /&gt;*&lt;br /&gt;* @return Doctrine_Query   this object&lt;br /&gt;*/&lt;br /&gt;public function whereParenWrap()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; $where = &amp;amp;$this-&amp;gt;_dqlParts['where'];&lt;br /&gt;&amp;nbsp; if (count($where) &amp;gt; 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; array_unshift($where, '(');&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; array_push($where, ')');&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp; return $this;&lt;br /&gt;}&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;To use it simply add a call to whereParenWrap() just after your OR clauses. For instance, here is how my original example is modified:&lt;br /&gt;&lt;blockquote&gt;$q=Doctrine_Query::create() -&amp;gt;from('mytable');&lt;br /&gt;$q-&amp;gt;whereIn('status',$statusList);&lt;br /&gt;$q-&amp;gt;orWhereIn('access',$accessList);&lt;br /&gt;&lt;div style="color: red;"&gt;$q-&amp;gt;whereParenWrap();&lt;/div&gt;$q-&amp;gt;andWhere('created&amp;gt;?','2010-01-01');&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;In my actual code this required me to shuffle things around so the OR clauses were defined first, then the straightforward AND clauses came last. But that was no hardship.&lt;br /&gt;&lt;br /&gt;However there is one flaw in this approach: if you need to have two such sets of OR clauses, which are themselves joined by an OR or AND. I thought a more generic approach might work: openParenthesis() and closeParenthesis() functions which you call exactly where you want them. But openParenthesis goes wrong (it ends up making "WHERE ((AND status IN..."; i.e. the "(" looks like an existing clause to Doctrine). I'm sure this could be made to work, but it will be more intrusive. So I'm going to be pragmatic and worry about this should I ever actually need it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2617979607157755276?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2617979607157755276/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2617979607157755276' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2617979607157755276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2617979607157755276'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/08/putting-parentheses-into-complex.html' title='Putting parentheses into complex doctrine sql queries'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7478974176978488132</id><published>2010-08-03T00:57:00.000-07:00</published><updated>2010-08-03T00:57:33.075-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Doctrine ORM'/><title type='text'>Doctrine many-to-many relationships with onDelete</title><content type='html'>In a &lt;a href="http://darrendev.blogspot.com/2010/08/be-explicit-about-ondelete-in-doctrine.html"&gt;previous article&lt;/a&gt; I explained why I now try to put an explicit onDelete on every relation. The problem comes with many-to-many relationships. If you put onDelete: CASCADE in the relationship it gets ignored and the entries survive in your refclass table. I found some page on this but none that really answers the question fully:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;a href="http://www.blogger.com/%20http://stackoverflow.com/questions/1488171/in-symfony-doctrines-schema-yml-where-should-i-put-ondelete-cascade-for-a-many"&gt;http://stackoverflow.com/questions/1488171/in-symfony-doctrines-schema-yml-where-should-i-put-ondelete-cascade-for-a-many&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;a href="http://www.blogger.com/%20http://groups.google.com/group/doctrine-user/browse_thread/thread/2f924ee3b8cd2689?pli=1"&gt;http://groups.google.com/group/doctrine-user/browse_thread/thread/2f924ee3b8cd2689?pli=1&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;a href="http://trac.doctrine-project.org/ticket/1123"&gt;http://trac.doctrine-project.org/ticket/1123&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The common theme is if you want an onDelete on a many-to-many relationship then you have to put it on the reference table. But not a single example anywhere. So, let's start with &lt;a href="http://www.doctrine-project.org/projects/orm/1.2/docs/manual/defining-models/en#relationships:join-table-associations:self-referencing-nest-relations:equal-nest-relatio" target="_blank"&gt;the "friends" nest example in the manual&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here is what does not work (no error, simply leaves the records behind in FriendReference)&lt;br /&gt;&lt;br /&gt;User:&lt;br /&gt;# ...&lt;br /&gt;&amp;nbsp; relations:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; # ...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Friends:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; class: User&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; local: user1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreign: user2&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; refClass: FriendReference&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; equal: true&lt;br /&gt;&lt;span style="color: red;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; onDelete: CASCADE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;FriendReference:&lt;br /&gt;&amp;nbsp; columns:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; user1:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type: integer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; primary: true&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; user2:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type: integer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; primary: true&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This one won't even compile (well, I didn't think it would):&lt;br /&gt;&lt;br /&gt;User:&lt;br /&gt;# ...&lt;br /&gt;&amp;nbsp; relations:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; # ...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Friends:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; class: User&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; local: user1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreign: user2&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; refClass: FriendReference&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; equal: true&lt;br /&gt;&lt;br /&gt;FriendReference:&lt;br /&gt;&amp;nbsp; columns:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; user1:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type: integer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; primary: true&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; user2:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type: integer&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; primary: true&lt;br /&gt;&lt;div style="color: red;"&gt;&amp;nbsp; relations:&lt;/div&gt;&lt;span style="color: red;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; onDelete: CASCADE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, how do we change it so it will work? I'll update this if and when I work it out; if you know please let me know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7478974176978488132?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7478974176978488132/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7478974176978488132' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7478974176978488132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7478974176978488132'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/08/doctrine-many-to-many-relationships.html' title='Doctrine many-to-many relationships with onDelete'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6614249321805215438</id><published>2010-08-03T00:31:00.000-07:00</published><updated>2010-08-03T00:31:35.800-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Doctrine ORM'/><title type='text'>Be explicit about OnDelete in doctrine schemas</title><content type='html'>In doctrine, I tried $obj-&amp;gt;delete(); and got an SQL exception from another table. But &lt;i style="color: #888888;"&gt;you're supposed to have deleted that too, that's the whole point of me telling you about all the relations between the tables&lt;/i&gt;, screamed I.&lt;br /&gt;&lt;br /&gt;So I went through my schema inserting "onDelete: CASCADE" in every relation. But as I did it realized sometimes I wanted "onDelete: SET NULL", and sometimes I wanted "onDelete: RESTRICT". &lt;a href="http://symforc.com/2008/07/10/foreign-keys-ondelete-for-dummies/"&gt;This&lt;/a&gt; is a helpful article on the onDelete choices (though for null you actually have to write "SET NULL" not just "NULL". There is also another choice: "OnDelete: SET DEFAULT" (e.g. when a user is deleted, you may want his comments to become owned by the admin user, rather than become orphaned.)&lt;br /&gt;&lt;br /&gt;So, now my advice is to explicitly put onDelete on every relation; treat it as required. I also find I need a line of documentation to explain every time I choose something other than cascade. It is good to be forced to think of these things.&lt;br /&gt;&lt;br /&gt;All is smooth until you come to many-to-many relationships, especially self-referential (nest) relationships, such as two users being friends (symmetrical) or one user blocking another (one-way). I will cover it in a separate article.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6614249321805215438?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6614249321805215438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6614249321805215438' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6614249321805215438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6614249321805215438'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/08/be-explicit-about-ondelete-in-doctrine.html' title='Be explicit about OnDelete in doctrine schemas'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-4122558795691489755</id><published>2010-07-25T23:01:00.000-07:00</published><updated>2010-11-17T23:28:15.863-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Wine broke, or PHP, or xinetd??</title><content type='html'>Sometimes it can be really educational to sit on the shoulder of brilliant developers and watch how they troubleshoot and debug. So, here is your chance to sit on my shoulder,... and snigger as failure reduces me to tears.&lt;br /&gt;[2010-11-18 UPDATE: a mere 5 months after originally posting, I think I have the answer; see the bottom of this article.]&lt;br /&gt;&lt;br /&gt;A program worked fine Saturday. This is on my Ubuntu 8.04 (hardy heron) machine.&lt;br /&gt;&lt;br /&gt;Sunday I updated firefox and ghostscript only. I then rebooted, which means kernel 2.6.24-28 finally became active (I updated it July 11th, but hadn't had chance to reboot until yesterday).&lt;br /&gt;&lt;br /&gt;Today that program doesn't run. It is one particular wine program: a go program. Another wine program (also a go program) runs fine. And here is the real killer: if I start that problem wine program from gogui (gogui.sf.net) it works. Exactly the same commandline, but it works from gogui, and doesn't work when started from my php script. That PHP script was last changed last Wednesday.&lt;br /&gt;&lt;br /&gt;I rebooted into the previous kernel (2.6.24-27), and the problem is exactly the same.&lt;br /&gt;&lt;br /&gt;Have you ever done those logic problems where you get a list of clues and have to work out who did what? Applied to the above we discover the only logical explanation is that... reality is warped. It can't be: wine, the go program, php, my php script or the kernel.&lt;br /&gt;&lt;br /&gt;Here is some more evidence. The first time I run it after a reboot the program (called valhallgtp.exe) fails to start with a stack dump and backtrace. Here are the first few and last few lines of that:&lt;br /&gt;=&gt;1 0x7bc3b23c __regs_RtlRaiseException+0x4c() in ntdll (0x014ded68)&lt;br /&gt;2 0x7bc76de3 in ntdll (+0x66de3) (0x014df0cc)&lt;br /&gt;3 0x7bc3a936 RtlRaiseException+0x6() in ntdll (0x014df144)&lt;br /&gt;4 0x00415833 in valhallgtp (+0x15833) (0x014df2ac)&lt;br /&gt;5 0x004159dd in valhallgtp (+0x159dd) (0x014dfa24)&lt;br /&gt;...  (6..20 are all in valhallgtp)&lt;br /&gt;21 0x0049b02c in valhallgtp (+0x9b02c) (0x014dff08)&lt;br /&gt;22 0x7b8773a7 in kernel32 (+0x573a7) (0x014dffe8)&lt;br /&gt;&lt;br /&gt;I also see: "err:seh:raise_exception Exception frame is not in stack limits =&gt; unable to dispatch exception."&lt;br /&gt;&lt;br /&gt;The 2nd and subsequent times I try to start it I get:&lt;br /&gt;Failed to start wine ValhallGTP.exe ...&lt;br /&gt;(this is an error from my php script; i.e. proc_open() is failing.)&lt;br /&gt;&lt;br /&gt;The CWD (current working directory) is correct, the parameters are correct; both are exactly what gogui is using. And running it from a bash shell is fine too.&lt;br /&gt;&lt;br /&gt;The fingers are pointing at PHP. But no PHP upgrades either yesterday or in the July 11th batch. And that same PHP is still successfully starting 4 other programs, including another one that uses wine. (BTW, go to synaptic package manager, file menu, history; this is where you can see exactly what Ubuntu updated and when.)&lt;br /&gt;&lt;br /&gt;Disk space is fine ("df"), memory is fine ("cat /proc/meminfo"). Machine load is low ("w"). Running it as root has same problem.&lt;br /&gt;&lt;br /&gt;When I ran from the -28 kernel I also got errors about no X DISPLAY. And then when I started the other wine program it opened but had no display. And running wine config was the same. I've not seen that again. Let's pretend it never happened and concentrate on what we can reproduce.&lt;br /&gt;(UPDATE: it happened again (this time with the -27 kernel). I had the gtpmfgo.exe running fine under wine; then starting mfgo.exe it came up but with no display. Close and retry showed it was repeatable. After closing gtpmfgo.exe it worked, and then I could open gtpmfgo.exe fine. Note: not repeatable; I haven't managed to get it to happen again.)&lt;br /&gt;&lt;br /&gt;Okay, where to next? What I don't understand is how come I cannot reproduce the problem I get when I start it the first time. That seems like a small enough challenge to be achievable: how to get it to crash consistently.&lt;br /&gt;&lt;br /&gt;...time passes...&lt;br /&gt;&lt;br /&gt;Aha! It seems it is crashing in the same way; it was just crashing and dying before I got chance to read the crash message. So the first time after a reboot it must take longer than a second to start up (perhaps wine initialization).&lt;br /&gt;&lt;br /&gt;...aha, aha! Narrowed down further. To explain this I need to give more background: I have a script called frontend.php that calls backend.php over sockets. backend.php uses stdin/stdout and uses xinetd to turn it into a socket server. When I run backend.php directly it works!&lt;br /&gt;&lt;br /&gt;Could the updates/reboot have affected xinetd somehow? ...probably not. The xinetd files are all dated Dec 2007 except my own config file which is June 30th. And, the other 4 programs are started in exactly the same way and work fine.&lt;br /&gt;&lt;br /&gt;Starting backend.php in exactly the same way as xinetd does works fine. (I've tried it both from bash and from sh.)&lt;br /&gt;&lt;br /&gt;...time passes...&lt;br /&gt;&lt;br /&gt;I seem to have a solution. (I had started working on a xinetd replacement php script, but that doesn't seem needed now.) My solution was to set xinetd to listen on another port, and use that port for just the problem program. This is working. I cannot explain it however: I had already tried connecting to only that program, so xinetd wouldn't have been doing anything different to now.&lt;br /&gt;&lt;br /&gt;UPDATE: 7 weeks later and I do another reboot and it broke in exactly the same way!!&lt;br /&gt;And again the only solution I could find was to give xinetd a new port for just this particular problem.&lt;br /&gt;Weird, very, very weird, ...&lt;br /&gt;&lt;br /&gt;2010-11-18 UPDATE: It was bothering me again, but on a different machine, and the trick with setting up xinetd on a different port didn't work. But adding this code to the top of backend.php seems to have solved it:&lt;br /&gt; if(trim(getenv('DISPLAY'))=='')putenv('DISPLAY=:0.0');&lt;br /&gt;&lt;br /&gt;The problem program has a GUI you see. I switch the GUI off with a commandline option ("hidewindows") but I think it actually creates windows and just hides them. And yes, none of the other programs uses a GUI... sigh, the clues were there.&lt;br /&gt;&lt;br /&gt;Curiously the "hidewindows" option no longer works! That is probably going to annoy me, but not as annoying as not working at all!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4122558795691489755?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4122558795691489755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4122558795691489755' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4122558795691489755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4122558795691489755'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/07/wine-broke-or-php-or-xinetd.html' title='Wine broke, or PHP, or xinetd??'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-5131087956433568512</id><published>2010-06-24T19:10:00.000-07:00</published><updated>2010-09-08T00:24:23.748-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Zend Framework'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Zend Form: display group and custom decorator</title><content type='html'>I keep meaning to write a proper review of Zend Framework, but I am waiting to finish a big project that uses it. It is running late, and I think ZF can take some of the blame. So don't hold your breath waiting for a positive review.&lt;br /&gt;&lt;br /&gt;Today's topic: I want to have part of my form hidden initially, and instead show a button saying: "toggle more questions".&lt;br /&gt;&lt;br /&gt;So, we make it a display group (where ex1 and ex2 are the form elements to hide):&lt;br /&gt;&lt;pre&gt;$form-&amp;gt;addDisplayGroup(array('ex1','ex2'),'extras');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By default this wraps it in a fieldset, with no place I could see to slip in my CSS, javascript and the toggle link. What I need is a decorator, I said to myself.&lt;br /&gt;&lt;br /&gt;Oh, gasp, does Zend make this difficult or what! In another part of this project custom decorators had been used for a minor layout change. 7 classes in 3 directories, almost all of it boilerplate. The real work was being done in CSS; those 7 classes were just to give a way to name the items as far as I could tell.&lt;br /&gt;&lt;br /&gt;The problem is the Zend Framework Philosophy of making things complex. Did you realize there is no addDecorator($myclass) function! You have to keep decorators in a special directory and then tell Zend where to get them. Then addDecorator('part_of_my_class_name').&lt;br /&gt;&lt;br /&gt;ZF's saving grace is that it is open source. So I poked around, and here is my solution. First, I'll define an alias for readability (optional):&lt;br /&gt;&lt;pre&gt;$displayGroup=$form-&amp;gt;getDisplayGroup('extras');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Remove all the default stuff I don't need (this step is optional too):&lt;br /&gt;&lt;pre&gt;$displayGroup-&amp;gt;removeDecorator('Fieldset');&lt;br /&gt;  $displayGroup-&amp;gt;removeDecorator('HtmlTag');&lt;br /&gt;  $displayGroup-&amp;gt;removeDecorator('DtDdWrapper');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now insert my decorator (these 4 lines are in lieu of addDecorator($myclass)):&lt;br /&gt;&lt;pre&gt;$decorators=$displayGroup-&amp;gt;getDecorators();&lt;br /&gt;  $decorators['MyTest']=new MyTestDecorator();&lt;br /&gt;  $displayGroup-&amp;gt;clearDecorators();&lt;br /&gt;  $displayGroup-&amp;gt;addDecorators($decorators);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(I.e. get the current decorators, add mine, then replace the existing decorators with the new set.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Finally we get to the meat. All you need is to define a render() function that takes a string (the existing content) and returns that string, optionally modified. Here is the minimal version that does nothing.&lt;br /&gt;&lt;pre&gt;class MyTestDecorator extends Zend_Form_Decorator_Abstract&lt;br /&gt;{&lt;br /&gt;  public function render($content){ return $content; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And here is the &lt;i&gt;full&lt;/i&gt; version: it hides the elements in the group and uses JQuery to show/hide it. The CSS is inline.&lt;br /&gt;&lt;pre&gt;class MyTestDecorator extends Zend_Form_Decorator_Abstract&lt;br /&gt;{&lt;br /&gt;  public function render($content)&lt;br /&gt;  {&lt;br /&gt;    $js="$('#extra_questions').toggle();return false;";&lt;br /&gt;    return '&amp;lt;a href="" onclick="'.$js.'"&gt;Toggle Tags Visibility&amp;lt;/a&gt;'.&lt;br /&gt;      '&amp;lt;div id="extra_questions" style="display: none;"&gt;'.$content.'&amp;lt;/div&gt;';&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yep, it's that simple. Oooh, the architects of ZF must be turning red with rage ;-)&lt;br /&gt;&lt;br /&gt;UPDATE: The &lt;a href="http://devzone.zend.com/article/3450"&gt;best article&lt;/a&gt; I've found so far on Zend Form Decorators. As many of the comments say, the length of the article is also a very good argument against using Zend Form. And it still didn't answer my questions, so it really needed to be 2-3 times as long. But if you need to format a form, it is a far more useful resource than the utterly inadequate Zend Form manual.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-5131087956433568512?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/5131087956433568512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=5131087956433568512' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5131087956433568512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5131087956433568512'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/06/zend-form-display-group-and-custom.html' title='Zend Form: display group and custom decorator'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-8802110076815216600</id><published>2010-06-22T22:00:00.000-07:00</published><updated>2010-06-22T22:00:01.293-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>fclib 0.4.20 release</title><content type='html'>I just put up another &lt;a href="http://dcook.org/software/fclib/"&gt;fclib&lt;/a&gt; release. Fclib is an ad hoc collection of php libraries, started about 10 years ago; the i18n-related functions are perhaps of most interest to people (with functions for Japanese, Chinese and Arabic language processnig).&lt;br /&gt;&lt;br /&gt;This new release has some minor bug fixes, a new utf8 function (for truncating a string), and a new file, modify_images.inc, that is a high-level interface to use gd functions to make doing thumbnails, cropping, resizing and basic drawing edits. It can operate on one or a batch of images.&lt;br /&gt;&lt;br /&gt;(The previous release was 11 months ago, described &lt;a href="http://darrendev.blogspot.com/2009/07/new-fclib-and-dcflash-releases.html"&gt;here&lt;/a&gt;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8802110076815216600?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8802110076815216600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8802110076815216600' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8802110076815216600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8802110076815216600'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/06/fclib-0420-release.html' title='fclib 0.4.20 release'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-1954961555658271913</id><published>2010-06-21T20:53:00.000-07:00</published><updated>2010-06-21T20:53:50.074-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='boost'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C++ socket library: asio</title><content type='html'>It is when it comes to writing a simple socket client in C++ that I really feel how much PHP has spoilt me.&lt;br /&gt;&lt;br /&gt;I previously have used SmartNetwork, part of the SmartWin library ( &lt;a href="http://smartwin.sourceforge.net/"&gt;http://smartwin.sourceforge.net/&lt;/a&gt; ) but it is fatally flawed: when the remote server dies there is no error reporting, so it happily carries on sending data to oblivion.&lt;br /&gt;&lt;br /&gt;Therefore when an application I am currently working on needed to write to a socket I decided to try out a new library. I looked at 4 or 5 libraries, and after that first pass rejected &lt;i&gt;all&lt;/i&gt; of them. Mainly based on their lack of clear documentation; sometimes based on their license. Faced with a choice of zero, I lowed my criteria, and chose to try &lt;a href="http://www.boost.org/doc/libs/release/libs/asio/index.html"&gt;boost::asio&lt;/a&gt; (also available in a &lt;a href="http://think-async.com/"&gt;non-boost version&lt;/a&gt;). &lt;a href="http://www.boost.org/"&gt;Boost&lt;/a&gt; is a major project, already installed on my machines, portable for at least Linux/Windows, and the libraries are peer-reviewed by some very clever people. Boost libraries are also usually flexible (to the point of making them hard to use), and definitely able to give me the error reporting I need.&lt;br /&gt;&lt;br /&gt;Here is the one line review: I've been banging my head against ASIO for 2-3 weeks, still do not have working code, but every time I consider running away I decide to stick with it.&lt;br /&gt;&lt;br /&gt;Or in other words, it is terrible, but the alternatives are worse.&lt;br /&gt;&lt;br /&gt;It is terrible in three ways: no high-level functions, it is hard to use &lt;i&gt;properly&lt;/i&gt; and it is basically undocumented. Yeah, yeah, there is API documentation for all the functions, but it doesn't say when to use which function. And, yeah, yeah, there are a dozen or so tutorials. But they are all toy examples, and don't explain why the code has been written the seemingly-complex way it has.&lt;br /&gt;&lt;br /&gt;Let me explain a bit more. Asio offers sync operations, but they are no use for any real-world code. For something you intend to use in production you will be forced to use the async functions. That means you'll need to use boost::threads, boost::bind (for the callbacks), boost::smart_pointer (as you have to wait for all callbacks to finish before you can delete an object, and it turns out your async callbacks can get called even after you've closed the socket) and &lt;i&gt;understand&lt;/i&gt; how async programs work. All of that is hard.&lt;br /&gt;&lt;br /&gt;I keep thinking I've got the code working, then I discover a timing problem that causes a crash only once in 15 runs, or it works on Linux but crashes on Windows, or is fine connecting to localhost but crashes when connecting to a remote server (due to different timing).&lt;br /&gt;&lt;br /&gt;Going back to the first of my reasons for describing it as terrible, an example of the lack of high-level assistance is that there is no timeout parameter on any async operations. To do an async_connect that I want to give up after 3 seconds I have to write code for both the connect and the timer, which means two callbacks, and coordinating those two callbacks. Time-outs are part of the low-level BSD sockets but the asio code is doing something that deliberately cripples that, so trying to use them won't work either.&lt;br /&gt;&lt;br /&gt;But I don't like to moan without being constructive. So I've been working on two things. First a high-level class called SocketFeeder (and SocketFeederSet) that you can just call write() on and not have to worry about all these callbacks. Second, a tutorial explaining how SocketFeeder has been written, and the thinking behind the design decisions.&lt;br /&gt;&lt;br /&gt;The class is for a client's production environment but has been written on my own time, so I'll be able to release it as open source. I'll edit this blog to link to it when it is finally ready; if you are keen to see it and the tutorial then leave a comment or send me a mail. Constructive nagging usually works! (And if you want to sponsor me, or are a magazine that pays for articles, let me know; it will get released eventually, but financial incentive means I can give it priority.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1954961555658271913?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1954961555658271913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1954961555658271913' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1954961555658271913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1954961555658271913'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/06/c-socket-library-asio.html' title='C++ socket library: asio'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-3361634514446970267</id><published>2010-06-15T03:06:00.000-07:00</published><updated>2010-06-15T03:06:07.811-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='Doctrine ORM'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>doctrine: delete object but leave it in database</title><content type='html'>I've a website where I make a page from a doctrine object which is taken from a database. Nothing special there. For adding the data to the database I have a form that goes to a preview page then a confirm button that actually writes it. For the preview page I share the same display logic, so I construct a doctrine object and simply don't save it. For editing, I do the same: I load the doctrine object from the database, make the changes specified on the form, and then either show the preview page or save to the database (depending on if preview or confirm was clicked).&lt;br /&gt;&lt;br /&gt;The problem came with editing a one-to-many relation. As a concrete example let's say the user is registering multiple email addresses. I was doing this:&lt;br /&gt;&amp;nbsp;&amp;nbsp; $User-&amp;gt;Emails-&amp;gt;delete();&lt;br /&gt;&lt;br /&gt;Then doing this for each address the user gave: &lt;br /&gt;&amp;nbsp;&amp;nbsp; $User-&amp;gt;Emails[]=new Email(...);&lt;br /&gt;&lt;br /&gt;It nicely handles when the user has removed an email address, changed one, or added an extra one. But when I realized the flaw I slapped my forehead so hard I gave myself whiplash. Do you see it? Have a moment...&lt;br /&gt;&lt;br /&gt;Yep, if they click cancel (or leave before clicking confirm) they're expecting nothing has changed, whereas in fact all the email addresses they'd previously input have vanished.&lt;br /&gt;&lt;br /&gt;The problem is that delete() happens immediately, rather than waiting for me to call save(). I hunted high and low for a way to stop that. My final solution was:&lt;br /&gt;&amp;nbsp;&amp;nbsp; if(confirmed)$User-&amp;gt;Emails-&amp;gt;delete();&lt;br /&gt;&amp;nbsp;&amp;nbsp; else $User-&amp;gt;unlink('Emails');&lt;br /&gt;&lt;br /&gt;I.e. unlink() appears to be the delete-that-does-not-touch-database I was searching for. (Of course, don't do something silly like go and call save() now, or the user will lose their email addresses &lt;i&gt;and&lt;/i&gt; you'll have some orphaned records in your Emails table.)&lt;br /&gt;&lt;br /&gt;Incidentally this did not work:&lt;br /&gt;&amp;nbsp;$user-&amp;gt;clearRelated('Email');&lt;br /&gt;&lt;br /&gt;I'd hoped it would, after reading the description in the manual. But in fact it did nothing at all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3361634514446970267?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3361634514446970267/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3361634514446970267' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3361634514446970267'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3361634514446970267'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/06/doctrine-delete-object-but-leave-it-in.html' title='doctrine: delete object but leave it in database'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3629717097320469447</id><published>2010-06-11T02:37:00.000-07:00</published><updated>2010-06-11T02:37:53.946-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><title type='text'>jquery: dcookorg_annotator V0.3 released</title><content type='html'>I know, I'm behind in &lt;a href="http://dcook.org/software/jquery/"&gt;my jquery plugin&lt;/a&gt; announcements, so I'm going to do three in one.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_pxlJfbZTzYg/TBH-Gs_LUuI/AAAAAAAAABM/doTTpVlILpo/s1600/annotator_screenshot.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="261" src="http://1.bp.blogspot.com/_pxlJfbZTzYg/TBH-Gs_LUuI/AAAAAAAAABM/doTTpVlILpo/s320/annotator_screenshot.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;First up is annotator, for annotating an image (or anything):&lt;br /&gt;&lt;a href="http://dcook.org/software/jquery/annotator/"&gt;http://dcook.org/software/jquery/annotator/&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;You can have any number of annotations, can drag them anywhere, and can resize them. In the default mode each annotation has a text box appear underneath it for adding a comment.&lt;br /&gt;&lt;br /&gt;New in version 0.3, and shown in the screenshot to the left, are some hooks for attaching a form to each annotation, so you can create a custom form for each one (or any other idea you have)!&lt;br /&gt;&lt;br /&gt;MIT-license open-source, and tested in all of IE6, IE7, IE8, Safari, Firefox 3 and Firefox 3.5.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Next up is selector_aspect, which is for selecting part of an image while maintaining a fixed aspect ratio. E.g. useful for cropping an image.&lt;br /&gt;&lt;a href="http://dcook.org/software/jquery/selector_aspect/"&gt;http://dcook.org/software/jquery/selector_aspect/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A simple straightforward plug-in, with not many options.&lt;br /&gt;&lt;br /&gt;Third is get_percentage_position, which is used to get the size of one div (or any DOM position) in terms of another div (or any DOM object), and also to get the relative position in the same terms. Not very glamorous, but useful in conjunction with the selector_aspect plugin, for instance. It is available here:&lt;br /&gt;&lt;a href="http://dcook.org/software/jquery/get_percentage_position/"&gt;http://dcook.org/software/jquery/get_percentage_position/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Finally, a reminder that my first jquery plugin, to run a magnifier over an image is introduced here:&lt;br /&gt;&lt;a href="http://darrendev.blogspot.com/2010/04/jquery-plugin-image-magnifier.html"&gt;http://darrendev.blogspot.com/2010/04/jquery-plugin-image-magnifier.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;and available here:&lt;br /&gt;&lt;a href="http://dcook.org/software/jquery/magnifier/"&gt;http://dcook.org/software/jquery/magnifier/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;and all my jquery plugins are being kept here:&lt;br /&gt;&lt;a href="http://dcook.org/software/jquery/"&gt;http://dcook.org/software/jquery/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3629717097320469447?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3629717097320469447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3629717097320469447' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3629717097320469447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3629717097320469447'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/06/jquery-dcookorgannotator-v03-released.html' title='jquery: dcookorg_annotator V0.3 released'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_pxlJfbZTzYg/TBH-Gs_LUuI/AAAAAAAAABM/doTTpVlILpo/s72-c/annotator_screenshot.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-3427173404705515952</id><published>2010-06-06T17:31:00.000-07:00</published><updated>2010-06-06T17:31:34.636-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>C++: incomplete type and cannot be defined</title><content type='html'>A very confusing error just now:&lt;br /&gt; error: aggregate ‘std::ofstream out’ has incomplete type and cannot be defined&lt;br /&gt;&lt;br /&gt;from simple code:&lt;br /&gt;  std::ofstream out;&lt;br /&gt;&lt;br /&gt;And the similar one:&lt;br /&gt;  error: variable ‘std::ofstream out’ has initialiser but incomplete type&lt;br /&gt;&lt;br /&gt;from this code:&lt;br /&gt;  std::ofstream out(fname.c_str(),std::ios_base::app);&lt;br /&gt;&lt;br /&gt;Using this didn't help:&lt;br /&gt; #include &amp;lt;iostream&gt;&lt;br /&gt;&lt;br /&gt;Using this also didn't help:&lt;br /&gt; #include &amp;lt;ofstream&gt;&lt;br /&gt;&lt;br /&gt;That was confusing me most, but now I see I've been getting "error: ofstream: No such file or directory" and I was missing it in the noise of other warnings and errors.&lt;br /&gt;&lt;br /&gt;The solution was simple:&lt;br /&gt; #include &amp;lt;fstream&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Yes, a very simple solution, but google wasn't helping. If it had been saying "ofstream is not a member of std" I'd have known I was missing a header file; a strange error message has you looking in other places. (I guess another std header file is doing a forward declaration for ofstream, which is why we get "incomplete type".)&lt;br /&gt;&lt;br /&gt;Mumble, grumble, back to work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3427173404705515952?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3427173404705515952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3427173404705515952' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3427173404705515952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3427173404705515952'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/06/c-incomplete-type-and-cannot-be-defined.html' title='C++: incomplete type and cannot be defined'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7900256225116852255</id><published>2010-05-31T20:43:00.000-07:00</published><updated>2010-05-31T20:43:39.454-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>Rotating in jquery (firefox problems)</title><content type='html'>In &lt;a href="http://darrendev.blogspot.com/2010/05/rotating-in-jquery-and-ie8-problems.html"&gt;my entry on rotating images in jquery/javascript&lt;/a&gt; I mentioned I'd not had the problem that the unofficial patch was supposed to fix. Well, now I've seen it &lt;i&gt;even using that patch&lt;/i&gt;. The problem is this: if you try to rotate an image (in Firefox, at least) that hasn't fully loaded it all goes wrong.&lt;br /&gt;&lt;br /&gt;When rotating in Firefox/Safari you are making a copy of the image; the jquery-rotate patch is to make sure your copy has been properly initialized before doing anything with it. My problem was similar: I was trying to rotate an image that hadn't loaded. (Curiously it didn't happen on a 106Kb image, but did &lt;i&gt;consistently&lt;/i&gt; on a 69Kb image; the smaller image was in landscape, while the larger image was portrait, but I have no idea if that is related.)&lt;br /&gt;&lt;br /&gt;My rotate call is in a function called init(). I first tried calling init() from the image's onLoad(), which worked most of the time. I also tried a more complex solution that involved not calling init() until both the image's onLoad() and JQuery's $(function(){...});  (what I personally like to call &lt;i&gt;onDomLoaded&lt;/i&gt;) had both run. But still it happened on certain images, and I now feel that that level of complexity is not needed (also, onDomLoaded always seems to run before the image's onLoad triggers).&lt;br /&gt;&lt;br /&gt;So, my solution is this:&lt;pre&gt;  &amp;lt;img src="test.jpg" id="img"&lt;br /&gt;  onLoad="window.setTimeout('init()',40)"&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using a time-out of 25ms was not enough, but 40ms seems reliable. The downside is that the image flashes on screen briefly in the original orientation before rotate can kick in. We can fix that by having it invisible initially:&lt;pre&gt;  &amp;lt;img src="test.jpg" id="img" &lt;span style="color:red"&gt;style="visibility:hidden"&lt;/span&gt;&lt;br /&gt;  onLoad="window.setTimeout('init()',40)"&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;script&gt;&lt;br /&gt;  function init(){&lt;br /&gt;    $('#img').rotate(90);&lt;br /&gt;    &lt;span style="color:red"&gt;$('#img').css('visibility','visible');&lt;/span&gt;&lt;br /&gt;    }&lt;br /&gt;  &amp;lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7900256225116852255?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7900256225116852255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7900256225116852255' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7900256225116852255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7900256225116852255'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/05/rotating-in-jquery-firefox-problems.html' title='Rotating in jquery (firefox problems)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-7432978286799230405</id><published>2010-05-24T18:30:00.000-07:00</published><updated>2010-05-24T18:30:41.288-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>Rotating in jquery (and IE8 problems)</title><content type='html'>Rotating an image in jquery is harder than you might think. Harder than I had imagined it would be, at least. The first problem is that it is not part of either JQuery or JQueryUI, so you are out there in the wilderness where the 3rd party plugins live. And, trust me, it can get wild out there.&lt;br /&gt;&lt;br /&gt;I first tried &lt;a href="http://code.google.com/p/jqueryrotate/"&gt;jqueryrotate&lt;/a&gt;. It is quite heavy, at around 10KB, but the main reason I abandoned it is that it didn't work properly for me (sorry, I cannot even remember why now).&lt;br /&gt;&lt;br /&gt;Next I tried &lt;a href="http://code.google.com/p/jquery-rotate/"&gt;jquery-rotate&lt;/a&gt; and I soon got this working in Firefox 3. My code also worked in Safari and Firefox 3.6 first time. IE6/7/8 were the problem. Under the hood all these plugins use DXImageTransform.Microsoft.Matrix for the IE browsers (which works back to IE5 apparently), and Canvas for all other browsers (which works from at least Firefox 3). They even allow rotation of any angle, though I only needed to rotate in steps of 90 degrees.&lt;br /&gt;&lt;br /&gt;(By the way jquery-rotate seems to be unmaintained, so I'm using &lt;a href="http://code.google.com/p/jquery-rotate/issues/detail?id=11"&gt;this unofficial patch&lt;/a&gt; even though my testing didn't see the problems it fixes.)&lt;br /&gt;&lt;br /&gt;Here is my code:&lt;pre&gt;  $('#img').rotate(rotation,true);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then:&lt;pre&gt;  if(rotation==90 || rotation==270){&lt;br /&gt;    $('#img').width(300);&lt;br /&gt;    $('#img').height(225);&lt;br /&gt;    }&lt;br /&gt;  else{&lt;br /&gt;    $('#img').width(300);&lt;br /&gt;    $('#img').height(400);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(To keep that code sample clearer I've hard-coded the sizes, to assume an image that has a 3:4 aspect ratio in its original position). The point of this code is to scale the image down to fit in the layout. This was where the first cross-browser problem appears. When Firefox/Safari rotate it they are creating a new object of the new dimensions. As far as I can tell when IE rotate it they are creating an optical illusion. It appears to have been rotated but when you read its width/height you get the numbers for the original position; and the same when you try to set them. (As an aside I tried a number of ideas to get around this underlying issue, but all ended in failure, so I guess it is just the way IE works.)&lt;br /&gt;&lt;br /&gt;Tinaysh... Tenacish... Tenaciousness is my middle name, even if it is hard to spell. This code does the job:&lt;pre&gt;  if(rotation==90 || rotation==270){&lt;br /&gt;    &lt;font color="red"&gt;if(document.all &amp;&amp; !window.opera){  //IE-specific&lt;br /&gt;        $('#img').width(225);&lt;br /&gt;        $('#img').height(300);&lt;br /&gt;        }&lt;br /&gt;    else{&lt;/font&gt;&lt;br /&gt;        $('#img').width(300);&lt;br /&gt;        $('#img').height(225);&lt;br /&gt;        &lt;font color="red"&gt;}&lt;/font&gt;&lt;br /&gt;    }&lt;br /&gt;  else{&lt;br /&gt;    $('#img').width(300);&lt;br /&gt;    $('#img').height(400);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK, images rotate nicely in all browsers, on to the Next Problem.&lt;br /&gt;&lt;br /&gt;I attach a draggable and resizable div (see the demos at &lt;a href="http://dcook.org/software/jquery/magnifier/"&gt;http://dcook.org/software/jquery/magnifier/&lt;/a&gt; to get an idea) to the image. I don't want it to leave the image so I use this:&lt;br /&gt;   mydiv.draggable({containment:img});&lt;br /&gt;&lt;br /&gt;When rotation is 0 or 180 everything is fine, but rotations of 90 and 270 go wrong in IE6/IE7/IE8; it seems the different coordinate system confuse it and I can move mydiv outside the image.  (Incidentally jquery's resizable containment is broken, so I had to hand-code that; my resizable containment code is not affected by this problem!)&lt;br /&gt;&lt;br /&gt;But things get worse. In IE8 *only*, the rotated image overlaps the following page items; IE6/IE7 correctly push the page down when a landscape image gets rotated to become a portrait image. I could have lived with containment not working, but this one is a showstopper.&lt;br /&gt;&lt;br /&gt;We can put the image in a named div, then alter the width/height of that div each time we rotate. So the code ends up as this:&lt;pre&gt;  if(rotation==90 || rotation==270){&lt;br /&gt;    if(document.all &amp;&amp; !window.opera){  //IE-specific&lt;br /&gt;        $('#img').width(225);&lt;br /&gt;        $('#img').height(300);&lt;br /&gt;        &lt;font color="red"&gt;$('#img_outer').width(300);&lt;br /&gt;        $('#img_outer').height(225);&lt;/font&gt;&lt;br /&gt;        }&lt;br /&gt;    else{&lt;br /&gt;        $('#img').width(300);&lt;br /&gt;        $('#img').height(225);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;  else{&lt;br /&gt;    $('#img').width(300);&lt;br /&gt;    $('#img').height(400);&lt;br /&gt;    &lt;font color="red"&gt;if(document.all &amp;&amp; !window.opera){  //IE-specific&lt;br /&gt;        $('#img_outer').width(300);&lt;br /&gt;        $('#img_outer').height(400);&lt;br /&gt;        }&lt;/font&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;You can also fix the other problem by using #img_outer as the containment div, instead of #img. That fixes all problems in IE6/IE7 (though be careful with margins, padding and borders; i.e. make sure #img_outer is exactly the same size as #img).&lt;br /&gt;&lt;br /&gt;IE8 is still less than ideal. Using #img_outer stops it overlapping with the following content, but it puts hard white space in the area where the image would be if height/width were reversed (and the draggable div can still be dragged into that area). For a landscape image that has been rotated that means whitespace to the right of the image, messing up multi-column table layouts. For a portrait image that has been rotated that means a lump of whitespace between the bottom of the image and the start of the next page content. Using overlap:hidden did not help.&lt;br /&gt;&lt;br /&gt;This is very novel: a bug in IE8 that isn't in any other browser, not even IE6! I've not solved this, and welcome advice. Yes, okay, I could use absolute positioning to put a "Download Firefox" icon in that white area, but that isn't quite the advice I'm looking for...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7432978286799230405?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7432978286799230405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7432978286799230405' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7432978286799230405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7432978286799230405'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/05/rotating-in-jquery-and-ie8-problems.html' title='Rotating in jquery (and IE8 problems)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-4835532189097129887</id><published>2010-04-29T17:31:00.000-07:00</published><updated>2010-04-29T17:31:39.177-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='gnome'/><title type='text'>Linux keyboard shortcuts</title><content type='html'>Have you ever gone to hit ctrl-tab (to switch tabs) or ctrl-w (to close current tab) in Firefox and suddenly all your Firefox windows (even the one playing radio on another desktop) disappear? (I'm using Ubuntu and Gnome, but I get the impression this problem affects all Linux distros and all window managers.)&lt;br /&gt;&lt;br /&gt;It must be old age making my fingers clumsier but I didn't even know ctrl-q did that until a few weeks ago. Yet I've done it a couple of times by accident recently, and when I did it today I decided &lt;i&gt;Something Has To Be Done&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;The solution is not just joyously simple, but I also learnt another cool function while I was there. First the solution: System|Preferences|Keyboard Shortcuts. Assign ctrl-q to do something; then Firefox never gets to see it. I assigned it to the calculator app which is nicely harmless.&lt;br /&gt;&lt;br /&gt;I learnt this trick &lt;a href="http://ask.metafilter.com/151533/Just-switch-to-the-next-tab-dont-close-all-25"&gt;here&lt;/a&gt;, which also mentions that the same idea works in XCFE (go to keyboard panel). I'm betting KDE has something similar.&lt;br /&gt;&lt;br /&gt;And the cool function? Looking through the other keyboard shortcuts I saw Alt+Print takes a screenshot of just the current window! You have no idea how many times I've clicked Print, then opened up Gimp to crop the screenshot to show just the window of interest. I'm now alternating between feeling very foolish and feeling very empowered.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4835532189097129887?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4835532189097129887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4835532189097129887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4835532189097129887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4835532189097129887'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/04/linux-keyboard-shortcuts.html' title='Linux keyboard shortcuts'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2998716581654813710</id><published>2010-04-22T02:06:00.000-07:00</published><updated>2010-04-22T02:06:49.862-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>jquery, click here to crash IE8</title><content type='html'>IE6/7/8 had me pulling me hair out again, but I've just found the problem!&lt;br /&gt;&lt;br /&gt;Here is the stripped-down code that shows the problem. We have a few divs, one that is active (and can be resized and dragged). The others are inactive but can be clicked to turn them into the active item.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;var currentItem=null;&lt;br /&gt;&lt;br /&gt;function makeActive(item){&lt;br /&gt;if(currentItem){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; currentitem.css({borderWidth:1,zIndex:998})&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .draggable('disable')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .resizable('disable')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .click(function(){makeActive($(this));});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;item.css({borderWidth:3,zIndex:999})&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .draggable('enable')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .resizable('enable')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ;&lt;br /&gt;currentItem=item;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Clicking back and forth between two items was fine in firefox, but IE8 would lock up (IE6/IE7 were the same). I kept stripping it down until it clicked (pun intended, sorry!). Yes, on each loop the click handler is added again. Each click handler is calling this function recursively and I think that is what locking up IE8.&lt;br /&gt;&lt;br /&gt;The solution is simply to change the .click line to look like this:&lt;br /&gt;&amp;nbsp;&amp;nbsp; .&lt;b&gt;one&lt;/b&gt;(&lt;b&gt;'click',&lt;/b&gt;function(){makeActive($(this));});&lt;br /&gt;&lt;br /&gt;That doesn't just make IE8 happy, it is also more clearly describes the click handler we want. In fact now I understand the problem I'm surprised firefox was not crashing too.&lt;br /&gt;&lt;br /&gt;P.S. resizable('disable') does not work in JQuery 1.7; it is apparently fixed in JQuery 1.8 though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2998716581654813710?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2998716581654813710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2998716581654813710' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2998716581654813710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2998716581654813710'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/04/jquery-click-here-to-crash-ie8.html' title='jquery, click here to crash IE8'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3032571522692490495</id><published>2010-04-19T21:36:00.000-07:00</published><updated>2010-04-19T21:38:29.072-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><title type='text'>jquery plugin: image magnifier</title><content type='html'>I've just released my first fully-fledged and useful jquery plugin:&lt;br /&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;a href="http://dcook.org/software/jquery/magnifier/"&gt;http://dcook.org/software/jquery/magnifier/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_pxlJfbZTzYg/S80uJvF_ddI/AAAAAAAAABE/Lwjo6OP5iMc/s1600/jquery_magnifier_screenshot.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="158" src="http://1.bp.blogspot.com/_pxlJfbZTzYg/S80uJvF_ddI/AAAAAAAAABE/Lwjo6OP5iMc/s200/jquery_magnifier_screenshot.jpg" width="200" /&gt;&lt;/a&gt;&lt;/div&gt;It allows you to magnify an image and examine just one part of it.&lt;br /&gt;Handles on the edge of the "magnifying glass" allow resizing it, which alters the degree of magnification.&lt;br /&gt;The plugin is fully documented, with numerous usage examples.&lt;br /&gt;It runs on all major browsers and operating systems.&lt;br /&gt;Naturally it is open source (MIT).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3032571522692490495?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3032571522692490495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3032571522692490495' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3032571522692490495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3032571522692490495'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/04/jquery-plugin-image-magnifier.html' title='jquery plugin: image magnifier'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_pxlJfbZTzYg/S80uJvF_ddI/AAAAAAAAABE/Lwjo6OP5iMc/s72-c/jquery_magnifier_screenshot.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-8852237456313422514</id><published>2010-04-15T02:29:00.000-07:00</published><updated>2010-04-20T15:56:15.781-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>find, grep and tr</title><content type='html'>A practical example of three useful unix commands, and how they can work together.&lt;br /&gt;&lt;br /&gt;I thought I had found another chance to use &lt;i&gt;sed&lt;/i&gt; (see &lt;a href="http://darrendev.blogspot.com/2009/12/right-sed-fred-im-too-sexy-to-search.html"&gt;Right Sed Fred, I'm too sexy to search and replace&lt;/a&gt;), but in the end did not use it; I wanted to remove newline characters but sed works line by line so this is not possible. (There is a way to do this with sed apparently, but it looked awfully hard to understand.) So I used &lt;i&gt;tr&lt;/i&gt; instead.&lt;br /&gt;&lt;br /&gt;Here is my final command:&lt;br /&gt;find /path/to/myfiles/ -mtime -125 -name '*txt' -print | grep -v xxx | tr '\012' ' '&lt;br /&gt;&lt;br /&gt;The first part:&lt;br /&gt;find /path/to/myfiles/ -mtime -125 -name '*txt' -print&lt;br /&gt;&lt;br /&gt;means search all the *.txt files under the given path, that have been created or modified in the past 125 days. It outputs one filename per line.&lt;br /&gt;&lt;br /&gt;However it include some files I didn't want. Luckily they all had the same filename component ('xxx' in my example above), so were trivial to identify. I used "grep -v" which means &lt;span style="font-style: italic;"&gt;exclude anything that matches&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;At this stage I had the list of filenames, but one per line. I wanted to use them as the parameters to a batch file, so needed them all on one line. I couldn't get sed to work, but stumbled on a way to use the "tr" tool. It takes two parameters: the character to replace, and what to replace it by. '\012' is octal for the unix linefeed character. So&lt;br /&gt;tr '\012' ' ' &lt;br /&gt;&lt;br /&gt;means replace LF with a space. I slapped this on to the previous command and got just want I wanted.&lt;br /&gt;&lt;br /&gt;(For unix beginners, the | is called the pipe character, and it means take the output of the command before it and give it as the input to the command after it. Many unix commands are designed with this kind of piping behaviour in mind.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8852237456313422514?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8852237456313422514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8852237456313422514' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8852237456313422514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8852237456313422514'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/04/find-grep-and-tr.html' title='find, grep and tr'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6581734810436177100</id><published>2010-04-08T22:28:00.000-07:00</published><updated>2010-04-08T22:41:43.276-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>Jquery cheat sheets</title><content type='html'>A cheat sheet, printed out, can be invaluable. Here are a few I've looked at:&lt;br /&gt;&lt;br /&gt;My choice:&lt;br /&gt;  &lt;a href="http://www.javascripttoolbox.com/jquery/cheatsheet/"&gt;http://www.javascripttoolbox.com/jquery/cheatsheet/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Nice and compact, going into good details. It comes in colour, but the wonderful thing is it also comes in Excel format, so I could edit the colours.&lt;br /&gt;It doesn't show the new in 1.3 functions, and doesn't point out the deprecated ones ($.browser and $.boxmodel); I annotated that myself using the "Too Colourful" one below.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Too new:&lt;br /&gt;  &lt;a href="http://labs.impulsestudios.ca/jquery-cheat-sheet"&gt;http://labs.impulsestudios.ca/jquery-cheat-sheet&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This one is nice, fits on one page, already monochrome (though using a light grey for optional parameters, which is hard to read). Does not show the parameters like the my first choice above. It is for jquery 1.4, showing what is new for 1.4; but I also wanted to know what to avoid if I wanted to write a plugin that would work back to 1.2.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Too Colourful:&lt;br /&gt; &lt;a href="http://www.artzstudio.com/files/jquery-rules/jquery_1.3_cheatsheet_v1.pdf"&gt;http://www.artzstudio.com/files/jquery-rules/jquery_1.3_cheatsheet_v1.pdf&lt;/a&gt;&lt;br /&gt;  2 pages worth, shows what is new in jquery 1.3, and what is deprecated. Uses colour and looked terrible as a b/w print-out. Also, it lists each version of similar functions (such as event handlers), which is why it needs two pages instead of one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6581734810436177100?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6581734810436177100/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6581734810436177100' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6581734810436177100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6581734810436177100'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/04/jquery-cheat-sheets.html' title='Jquery cheat sheets'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-8123923030044154470</id><published>2010-03-23T18:20:00.000-07:00</published><updated>2010-03-23T18:30:55.511-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>The Dangers Of GroupBy</title><content type='html'>I had a complex join (three tables) with a group by and a where cause with three conditions. It seemed to be working, until I added more data; I eventually narrowed problem down to just a single table. Consider this query:&lt;pre&gt;&lt;br /&gt;SELECT w.user_id, w.some_date, w.id&lt;br /&gt;FROM mytable w&lt;br /&gt;GROUP BY w.user_id&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;id is the primary key. That query returns all the data:&lt;pre&gt;&lt;br /&gt;user_id   some_date   id&lt;br /&gt;   1      2010-02-01   1&lt;br /&gt;   5      2010-02-02   2&lt;br /&gt;   1      NULL         3&lt;br /&gt;   1      2010-03-22   4&lt;br /&gt;   1      2010-03-24   5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'm only interested in the record with the latest date for each user, so I add a MAX on some_date and GROUP BY user_id:&lt;pre&gt;&lt;br /&gt;SELECT w.user_id, MAX(w.some_date), w.id&lt;br /&gt;FROM mytable w&lt;br /&gt;GROUP BY w.user_id&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That gives:&lt;pre&gt;&lt;br /&gt;user_id   MAX( w.some_date )   id&lt;br /&gt;   1       2010-03-24           1&lt;br /&gt;   5       2010-02-02           2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hang on! The dates are right, but 2010-03-24 is from id=5, not id=1. The some_date column has been max-ed in isolation.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This issue is explained in an excellent two-part article: &lt;a href="http://www.sqlteam.com/article/how-to-use-group-by-in-sql-server"&gt;part1&lt;/a&gt; &lt;a href="http://www.sqlteam.com/article/how-to-use-group-by-with-distinct-aggregates-and-derived-tables"&gt;part2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In fact it is so excellent I didn't fully understand it, and am going to re-read it three times a day until I do.  I really need to understand it because, as the author shows, it is very easy to get correct answers from a query on your test data, pass all your unit tests, and deliver something that goes wrong when one more record is added.&lt;br /&gt;&lt;br /&gt;My query is simpler than the one in his example, but after many tries I cannot find a solution. The "join(...)as d" syntax does not seem to work in MySQL or I am not using it correctly. It seems what I'm trying to do is very basic, so I do not understand why I cannot find more advice on the subject. I'm open to suggestions!&lt;br /&gt;&lt;br /&gt;(For the moment I'm going to give up, do a simpler query and have PHP process the results to get the data I actually want.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8123923030044154470?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8123923030044154470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8123923030044154470' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8123923030044154470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8123923030044154470'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/03/dangers-of-groupby.html' title='The Dangers Of GroupBy'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-5113960767256984803</id><published>2010-03-07T21:55:00.000-08:00</published><updated>2010-08-18T18:19:51.909-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Zend Framework'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Image Protection in Zend Framework</title><content type='html'>Sometimes you want to only show certain images (or other media) to certain users (e.g. those that are logged in, or those that have paid for it). This quick tutorial will show how to write the controller for Zend Framework for serving images. This is for ZF 1.10, but as far as I can tell it is not using anything new or unusual.&lt;br /&gt;&lt;br /&gt;This tutorial does not cover the validation part of the code.&lt;br /&gt;&lt;br /&gt;We start with a controller that does not use views, layout and all that stuff:&lt;br /&gt;&lt;pre&gt;class ImgController extends Zend_Controller_Action&lt;br /&gt;{&lt;br /&gt;public function init()&lt;br /&gt;{&lt;br /&gt;$this-&gt;_helper-&gt;viewRenderer-&gt;setNoRender(true);&lt;br /&gt;$this-&gt;_helper-&gt;layout()-&gt;disableLayout();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now add an action that will serve all images:&lt;br /&gt;&lt;pre&gt;public function imgAction(){&lt;br /&gt;$type=$this-&gt;getRequest()-&gt;getParam(1);&lt;br /&gt;$fname=$this-&gt;getRequest()-&gt;getParam(2);&lt;br /&gt;$ext=$this-&gt;getRequest()-&gt;getParam(3);&lt;br /&gt;&lt;br /&gt;echo "type=$type, fname=$fname, ext=$ext";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public function badAction(){&lt;br /&gt;$this-&gt;getResponse()-&gt;setHeader('Content-Type','image/jpeg');&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The idea is that URLs such as http://127.0.0.1/img/folder/abc.jpg will end up at the imgAction() function, and $type will get set to "folder", $fname to "abc" and $ext to "jpg". To do that we need to set up what is called a router, which we will do next. The badAction() will handle any problems; this crude code will send back a 0 byte jpeg.&lt;br /&gt;&lt;br /&gt;To create a router, jump to your bootstrap file and create a function something like this:&lt;br /&gt;&lt;pre&gt;public function _initCustomRouting(){&lt;br /&gt;$frontController=Zend_Controller_Front::getInstance();&lt;br /&gt;$router=$frontController-&gt;getRouter();&lt;br /&gt;$router-&gt;addRoute(  //To catch any that are not formatted correctly&lt;br /&gt;'imgHandlingBad',&lt;br /&gt;new Zend_Controller_Router_Route('img/*',&lt;br /&gt;array('controller'=&gt;'img', 'action'=&gt;'bad')&lt;br /&gt;)&lt;br /&gt;);&lt;br /&gt;$router-&gt;addRoute(  //Formatted as /url/type/fname.ext&lt;br /&gt;'imgHandling',&lt;br /&gt;new Zend_Controller_Router_Route_Regex('img/(.+)/(.+)\.(.+)',&lt;br /&gt;array('controller'=&gt;'img', 'action'=&gt;'img')&lt;br /&gt;)&lt;br /&gt;);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note that order: imgHandlingBad must come before imgHandling. I found this &lt;a href="http://www.zendcasts.com/creating-custom-zend_controller-routes-2/2009/01/"&gt;introduction (7 minute screencast) to using Zend_Controller_Router_Route&lt;/a&gt; very useful; then some trial and error and study of the ZF source code.&lt;br /&gt;&lt;br /&gt;Now you should be able to test http://127.0.0.1/img/folder/abc.jpg and see our debug comment. But without a folder (e.g. http://127.0.0.1/img/abc.jpg) it will get handled by the badAction(), as will those without an extension: http://127.0.0.1/img/folder/abcjpg&lt;br /&gt;&lt;br /&gt;Now the final step is to feed back some images, so in imgAction() replace the echo line with this code:&lt;br /&gt;&lt;pre&gt;$path=$basePath.$type;&lt;br /&gt;$fullPath=$path.$fname.'.'.$ext;&lt;br /&gt;if(!file_exists($fullPath))return $this-&gt;badAction();&lt;br /&gt;&lt;br /&gt;switch($ext){&lt;br /&gt;case 'jpg':$mime='image/jpeg';break;&lt;br /&gt;case 'png':$mime='image/png';break;&lt;br /&gt;case 'gif':$mime='image/gif';break;&lt;br /&gt;default:return $this-&gt;badAction();&lt;br /&gt;}&lt;br /&gt;$this-&gt;getResponse()-&gt;setHeader('Content-Type',$mime);&lt;br /&gt;&lt;br /&gt;readfile($fullPath);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It decides the filename, decides the mime-type based on extension, and then readfile passes on the binary data.&lt;br /&gt;&lt;br /&gt;In the above code we use the $type directly to decide the image path. You could instead map type to different directories. E.g.&lt;br /&gt;&lt;pre&gt;switch($type){&lt;br /&gt;case 'www':$path='/var/www/main_images/';break;&lt;br /&gt;case 'www/articles':$path='/var/www/html/special/articles/';break;&lt;br /&gt;case 'family':$path='/home/user/images/family/';break;&lt;br /&gt;default:return $this-&gt;badAction();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Another way you might use $type is if certain users can only see certain types of images. You could do different validation checks in each case statement above.&lt;br /&gt;&lt;br /&gt;Got any suggestions to make this code better? Let me know!&lt;br /&gt;&lt;br /&gt;P.S. One special note about this technique. In the typical MVC Zend Framework setup, if you create public/img/ then apache will serve images from there; only if the image is missing in that location will Apache ask the Zend Framework to serve it. You might use this to your advantage to speed up delivery of certain common images. But it also opens the way to accidentally serving images that are supposed to be protected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-5113960767256984803?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/5113960767256984803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=5113960767256984803' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5113960767256984803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5113960767256984803'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/03/image-protection-in-zend-framework.html' title='Image Protection in Zend Framework'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-5878580465577738880</id><published>2010-03-02T18:21:00.000-08:00</published><updated>2010-03-02T18:48:43.034-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='Doctrine ORM'/><title type='text'>Creating A Doctrine Custom Behaviour, Part 2</title><content type='html'>In &lt;a href="http://darrendev.blogspot.com/2010/03/creating-doctrine-custom-behaviour-part.html"&gt;part 1&lt;/a&gt; we made a simple custom behaviour and then gave it some options we could customize in our schema. The second half of the power of Doctrine's behaviours though is being able to set values. First, uncomment this line in the last example in part 1:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    $this-&gt;addListener(new DarrenTestableListener($this-&gt;_options));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You should get told class DarrenTestableListener does not exist, so create DarrenTestableListener.php in the same directory as DarrenTestable.php (though, as mentioned before, it can go anywhere in your models directory tree), and fill it with this code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;class DarrenTestableListener extends Doctrine_Record_Listener&lt;br /&gt;{&lt;br /&gt;    protected $_options = array();&lt;br /&gt;&lt;br /&gt;    public function __construct(array $options)&lt;br /&gt;    {&lt;br /&gt;        $this-&gt;_options = $options;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /** Called when a new record is created */&lt;br /&gt;    public function preInsert(Doctrine_Event $event)&lt;br /&gt;    {&lt;br /&gt;        $name = $event-&gt;getInvoker()-&gt;getTable()-&gt;getFieldName($this-&gt;_options['name']);&lt;br /&gt;        $modified = $event-&gt;getInvoker()-&gt;getModified();&lt;br /&gt;        if ( ! isset($modified[$name])) {&lt;br /&gt;            $event-&gt;getInvoker()-&gt;$name = "C";&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /** Called when an existing record is updated  */&lt;br /&gt;    public function preUpdate(Doctrine_Event $event)&lt;br /&gt;    {&lt;br /&gt;        $name = $event-&gt;getInvoker()-&gt;getTable()-&gt;getFieldName($this-&gt;_options['name']);&lt;br /&gt;        $modified = $event-&gt;getInvoker()-&gt;getModified();&lt;br /&gt;        if ( ! isset($modified[$name])) {&lt;br /&gt;            $event-&gt;getInvoker()-&gt;$name .= 'U';&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /** Handle dql update queries */&lt;br /&gt;    public function preDqlUpdate(Doctrine_Event $event)&lt;br /&gt;    {&lt;br /&gt;        $params = $event-&gt;getParams();&lt;br /&gt;        $name = $event-&gt;getInvoker()-&gt;getTable()-&gt;getFieldName($this-&gt;_options['name']);&lt;br /&gt;        $field = $params['alias'] . '.' . $name;&lt;br /&gt;        $query = $event-&gt;getQuery();&lt;br /&gt;        if ( ! $query-&gt;contains($field)) {&lt;br /&gt;            $query-&gt;set($field, '?', 'U');&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It has default options (and a constructor to set the options), then one function to handle INSERTs and two functions to handle UPDATEs. (There are also hooks for DELETE, save and validation.) All three functions follow a similar pattern:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt; &lt;li&gt;Get the real fieldname&lt;br /&gt; &lt;li&gt;See if we need to do anything&lt;br /&gt; &lt;li&gt;If so, set the field&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;What we do is set the field to "C" when it is created. Then each time it is updated we append a "U". E.g. after three updates it will look like "CUUU". (TODO: I'm not so familiar with DQL, and I am not using it, so the DQL version replaces the existing data instead of appending. If you can supply the proper code for it let me know!)&lt;br /&gt;&lt;br /&gt;Incidentally if you set a default in the options that will get precedence over your code (that is what the getModified() call is for, I assume). E.g. if I change my YAML schema to:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ActsAs:&lt;br /&gt;    DarrenTestable:&lt;br /&gt;        name: being_silly&lt;br /&gt;        options:&lt;br /&gt;            default: Hello!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then after a couple of updates being_silly will contains "Hello!UU".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-5878580465577738880?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/5878580465577738880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=5878580465577738880' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5878580465577738880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5878580465577738880'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/03/creating-doctrine-custom-behaviour-part_02.html' title='Creating A Doctrine Custom Behaviour, Part 2'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3253182652257998133</id><published>2010-03-02T17:41:00.001-08:00</published><updated>2010-03-02T18:49:52.798-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='Doctrine ORM'/><title type='text'>Creating A Doctrine Custom Behaviour, Part 1</title><content type='html'>Doctrine is a wonderful ORM library, but the manual is..., well, let's just say I think it was written by the developers, and out of obligation :-)&lt;br /&gt;&lt;br /&gt;Behaviours are a motivating feature for using Doctrine, so here is a two-part tutorial on how to add your own &lt;span style="font-style:italic;"&gt;minimal&lt;/span&gt; behaviour. The first part will show how to add a field to a table. The &lt;a href="http://darrendev.blogspot.com/2010/03/creating-doctrine-custom-behaviour-part_02.html"&gt;second part&lt;/a&gt; will show how to have its contents set automatically. For a more in-depth example see &lt;a href="http://clear-cache.fr/?post/2009/11/26/How-to-create-a-custom-Doctrine-behavior"&gt;here&lt;/a&gt; or study the source code of the core behaviours.&lt;br /&gt;&lt;br /&gt;The first thing you need to know is to put your behaviour classes in your models directory, or a sub-directory of your choice. You don't need a directory structure that matches the "XXX_YYY_" prefixes of the class name. This is assuming you are using Doctrine's auto-loading of table classes; if not you can put them anywhere you like of course. I'm putting them in models/behaviors/ (yes, using American spelling!)&lt;br /&gt;&lt;br /&gt;Make a file called "DarrenTestable.php" with these contents:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;class DarrenTestable extends Doctrine_Template{&lt;br /&gt;    public function setTableDefinition(){&lt;br /&gt;        $this-&gt;hasColumn('darren_testing','string');&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;See, I told you it was minimal! To add it to an existing class, add this to your YAML schema:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ActsAs:&lt;br /&gt;    DarrenTestable&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Recreate the class from the schema and you should see a field called "darren_testing". On MySQL the type is "text" (i.e. this is what "string" maps to if you are using MySQL).&lt;br /&gt;&lt;br /&gt;Even that very small example is doing something useful: if this behaviour is used in 10 classes and we need to change the name or data type (or set a default, add an index, etc.) we only need to edit it one place.&lt;br /&gt;&lt;br /&gt;While still staying within the scope of a minimal example, we can expand it in two directions. We can have our code automatically set the field value (see &lt;a href="http://darrendev.blogspot.com/2010/03/creating-doctrine-custom-behaviour-part_02.html"&gt;part 2 of this article&lt;/a&gt;), and we can allow users to customize it. Here is how we add some options, with defaults:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;?php&lt;br /&gt;class DarrenTestable extends Doctrine_Template&lt;br /&gt;{&lt;br /&gt;    protected $_options = array(&lt;br /&gt;        'name'=&gt;'darren_testing',&lt;br /&gt;        'type'=&gt;'string',&lt;br /&gt;        'options'=&gt;array(),&lt;br /&gt;        );&lt;br /&gt;&lt;br /&gt;    public function setTableDefinition()&lt;br /&gt;    {&lt;br /&gt;        $this-&gt;hasColumn($this-&gt;_options['name'],&lt;br /&gt;            $this-&gt;_options['type'], null, $this-&gt;_options['options']);&lt;br /&gt;        //$this-&gt;addListener(new DarrenTestableListener($this-&gt;_options));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I have added the call to the listener we'll use in &lt;a href="http://darrendev.blogspot.com/2010/03/creating-doctrine-custom-behaviour-part_02.html"&gt;part 2&lt;/a&gt;, but commented it out for the moment. The options contains a sub-array called options which is passed on to Doctrine_Table::setColumn() and can be used to set defaults and so on.&lt;br /&gt;&lt;br /&gt;Usage is exactly like above if we want the defaults. If we want to change the name, and require it to be set (i.e. NOT NULL) you would do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ActsAs:&lt;br /&gt;    DarrenTestable:&lt;br /&gt;        name: being_silly&lt;br /&gt;        options:&lt;br /&gt;            notnull: true&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3253182652257998133?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3253182652257998133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3253182652257998133' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3253182652257998133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3253182652257998133'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/03/creating-doctrine-custom-behaviour-part.html' title='Creating A Doctrine Custom Behaviour, Part 1'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7081069354826917903</id><published>2010-02-27T15:49:00.000-08:00</published><updated>2010-02-27T16:00:52.533-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>When a unix dot means a whole different world...</title><content type='html'>Subtitle: new ways to shoot yourself in the foot.&lt;br /&gt;&lt;br /&gt;A week ago I was changing ownership on a web site subdirectory:&lt;br /&gt;  chown -R darren:www *&lt;br /&gt;&lt;br /&gt;There were some files/directories that start with a dot, such as .htaccess, so I then did:&lt;br /&gt;  chown -R darren:www .*&lt;br /&gt;&lt;br /&gt;If you just screamed out "Nooooo!!!!" you are allowed to polish your Unix Guru badge and wear it with pride. Personally I just sat there wondering why it was taking so long to return and why my disk was suddenly sounding so busy.&lt;br /&gt;&lt;br /&gt;That's right. ".." refers to the parent directory. It had gone up a directory, then was using -R to recurse into every subdirectory on my web site, destroying previous ownership settings with abandon.&lt;br /&gt;&lt;br /&gt;I realized after a few moments and killed it, but the damage was done, and I'm still discovering small parts of web sites that got broken. But a question for the gurus: how should I change all files and directories in a tree including those that start with a dot? Did I need to use some complicated find and chown combination command?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7081069354826917903?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7081069354826917903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7081069354826917903' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7081069354826917903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7081069354826917903'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/02/when-unix-dot-means-whole-different.html' title='When a unix dot means a whole different world...'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-5944927919966742698</id><published>2010-02-16T17:36:00.001-08:00</published><updated>2010-02-16T17:57:24.510-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='Japanese'/><title type='text'>Google Messing Up I18n</title><content type='html'>People often seem to think Google can do no wrong; an image helped no doubt by being surrounded by companies such as Microsoft and Apple. But, even with all those computer science PhD's it snapped up, it sometimes just does not get it.&lt;br /&gt;&lt;br /&gt;Once a month or so, Google resets back to showing me the search results in Japanese, and only searching Japanese pages. My browser is set to say I prefer English pages, but it ignores that and uses my IP address. Japanese people use a Japanese browser that will send a header saying they want to see Japanese. Unless they actually want to see English. Why is Google ignoring this?&lt;br /&gt;&lt;br /&gt;Perhaps Google needs fewer PhD's and more people who know what this header means:&lt;br /&gt;  Accept-Language en-gb,en;q=0.5&lt;br /&gt;&lt;br /&gt;Google (!) tells me Google need to look at section 14.4 of RFC2616.&lt;br /&gt;&lt;br /&gt;But, the user-unfriendliness goes deeper. Here is how I get out of Japanese mode.&lt;br /&gt;Click: 検索オプション&lt;br /&gt;Find: 言語 検索対象にする言語&lt;br /&gt;and scroll down to find and select 英語&lt;br /&gt;&lt;br /&gt;No! No, no, no! It just goes to show, even though I read Japanese I still messed up. Here is the correct way:&lt;br /&gt;&lt;br /&gt;In the top-right of the search results click "設定 ▼" then 検索設定&lt;br /&gt;In the line that says 表示言語の設定 scroll down to select 英語.&lt;br /&gt;Then find the button in top-right that says 保存。When it pops up something that looks like an error message (but is actually a success message), press OK.&lt;br /&gt;&lt;br /&gt;Hopefully I'm good for another month.&lt;br /&gt;&lt;br /&gt;But the point of my rant is the only piece of English on any of those pages was "google". Not a single helpful line such as "switch this page to English". Perhaps they should use the Accept-Language header to decide what language the user would most like to see a "switch this page to XXX" link in?&lt;br /&gt;&lt;br /&gt;Well, rant over. Google have been doing this for 10 years, so I don't expect they're going to change any time soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-5944927919966742698?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/5944927919966742698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=5944927919966742698' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5944927919966742698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5944927919966742698'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/02/google-messing-up-i18n.html' title='Google Messing Up I18n'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-6990567051616688781</id><published>2010-02-04T20:50:00.000-08:00</published><updated>2010-02-04T21:15:07.631-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><category scheme='http://www.blogger.com/atom/ns#' term='computer go'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Facebook Making Me Redundant</title><content type='html'>Back in 2007 I wrote an article on how to port your PHP applications into C++ (in &lt;a href="http://phparch.com/magazine/index/59"&gt;the Oct 2007 issue&lt;/a&gt; of PHP Architect), showing the speed-up and memory-usage benefits. It's a nice idea, though I've only done it in a production project once. The programmer man-hours is very rarely worth it.&lt;br /&gt;&lt;br /&gt;Well, Facebook have &lt;a href="http://developers.facebook.com/news.php?blog=1&amp;story=358"&gt;gone and automated the process&lt;/a&gt;, calling it Hip-Hop. Over 90% of Facebook's (400 billion PHP-based page views every month) traffic is now running on their Hip-Hop system, i.e. on PHP pages that have been compiled into C++.&lt;br /&gt;&lt;br /&gt;This is very interesting to me as I have recently been writing less and less in C++, more and more in PHP. Even a tree searcher for computer go I have been doing in PHP, as an experiment to see how it goes. On reflection that particular experiment is probably a failure - I need to set bits in a 32-bit or 64-bit integer and PHP puts a layer of abstraction in the way.&lt;br /&gt;&lt;br /&gt;The main php script is very slow (its been running non-stop for 5-6 weeks so far, with months left to run) but ironically all that time is being spent in calling other applications (written in C++!) to do the hard calculations. However I've another script that analyzes the data, needing to hold it all in memory at once, and that is showing signs of cracking - I'm already having to set my memory_limit to 1.5G, and that will probably hit 4G when the other script has chomped through all my data.&lt;br /&gt;&lt;br /&gt;Hip Hop might be my saviour.&lt;br /&gt;&lt;br /&gt;P.S. PHP Architect magazine also &lt;a href="http://phparch.com/main/news/view/68/Announcing_our_support_for_Facebook_s_HipHop"&gt;think this is going to be a very important technology&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6990567051616688781?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6990567051616688781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6990567051616688781' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6990567051616688781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6990567051616688781'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/02/facebook-making-me-redundant.html' title='Facebook Making Me Redundant'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-8889084864113805893</id><published>2010-01-16T19:29:00.000-08:00</published><updated>2010-01-17T00:37:29.147-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Zend Framework</title><content type='html'>I've been hearing a lot about Zend Framework over the past year or two, it seemed to have become the de facto standard framework (something else I've been hearing a lot about) and I felt a need to get up to speed on it.&lt;br /&gt;&lt;br /&gt;I put up my review of &lt;a href="http://darren-reviews.blogspot.com/2010/01/easy-php-websites-with-zend-framework.html"&gt;my review of Easy PHP Websites with the Zend Framework&lt;/a&gt;. I bought the book a few months back, as it seemed the best of the choices at the time. Quick summary of the review: &lt;span style="font-style:italic;"&gt;Okay book, not impressed by Zend Framework, last third of the book is useful and interesting.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'll talk about not being impressed by the DB part of Zend below, but I feel the main other aspect that bothered me was the way data is given to and then used in views. In the controller I have to write:&lt;br /&gt;  $this-&gt;view-&gt;something=$mymodel-&gt;get_something();&lt;br /&gt;&lt;br /&gt;Then in the view it is used as:&lt;br /&gt;  Our something is &amp;lt;?php echo $this-&gt;something;?&gt; today!&lt;br /&gt;&lt;br /&gt;All that $this-ing and view-ing makes the code verbose, hiding the real thing I'm trying to say with my code. In particular the view code is ugly - ZF has not helped at all compared to writing plain PHP.&lt;br /&gt;&lt;br /&gt;Why do I care? Try getting a designer to edit a view that looks like that. They may be comfortable with HTML but will go pale and faint at the sight of a dollar sign let alone all the surrounding junk. I can tell you from experience they will refuse to even edit a file that contains text that looks like that, let alone type it themselves.&lt;br /&gt;&lt;br /&gt;I was also not impressed with how Zend Form does input validation. It seems to be just as much work as doing it in plain PHP. If I add a dependency I want value for money!&lt;br /&gt;&lt;br /&gt;Back to databases. I recently wrote about &lt;a href="http://darrendev.blogspot.com/2010/01/doctrine-orm.html"&gt;being impressed with Doctrine ORM&lt;/a&gt;. Zend DB didn't impress me at all; I already use Pear::DB to abstract away the database, and all Zend adds is having to use PHP instead of SQL to write common queries, while still having to use SQL to write the less common ones.&lt;br /&gt;&lt;br /&gt;A web search found I wasn't alone in this opinion, but what was interesting was I found people using Doctrine within Zend Framework, as a replacement for Zend DB.&lt;br /&gt;&lt;br /&gt;To quote &lt;a href="http://ruben.savanne.be/articles/integrating-zend-framework-and-doctrine"&gt;an article by Ruben Vermeersch&lt;/a&gt;: "While Zend_Db isn't a bad technology, it is still quite low-level and close to the underlying database. Using Doctrine, you can manipulate your data like objects, without worrying about the database too much." The article is pro-Zend and pro-Doctrine, and shows how to use the two together. Good clear article.&lt;br /&gt;&lt;br /&gt;Here is &lt;a href="http://activecodeline.com/simple-todo-application-in-zend-framework-and-doctrine-orm"&gt;another example of using Zend and Doctrine together&lt;/a&gt;. It is just example code, not a teaching article, but interesting as it also uses ajax (using jquery).&lt;br /&gt;&lt;br /&gt;Here is &lt;a href="http://www.robertspeer.com/blog/symfony-refactor-of-the-zend-quick-start-tutorial/"&gt;an article comparing Symfony (using Doctrine) with Zend&lt;/a&gt;, with a pro-Symfony slant. Also check out the comments, especially the reply by Matthew Weier O'Phinney describing what is coming up in Zend (including how it may have more formal integration with Doctrine at some point, and hopefully auto-admin pages and auto-forms-from-schema).&lt;br /&gt;  &lt;br /&gt;By the way, the afore-mentioned Ruben Vermeersch article suggests these articles to get up to speed on Zend:&lt;br /&gt;   &lt;a href="http://framework.zend.com/docs/quickstart"&gt;http://framework.zend.com/docs/quickstart&lt;/a&gt;&lt;br /&gt;   &lt;a href="http://akrabat.com/zend-framework-tutorial/"&gt;http://akrabat.com/zend-framework-tutorial/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'd like to stress again that my views of Zend Framework are currently only based on studying it; I'll get back to you once I've tried it out on a full-scale real-world project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8889084864113805893?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8889084864113805893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8889084864113805893' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8889084864113805893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8889084864113805893'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/01/zend-framework.html' title='Zend Framework'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-837155769575512843</id><published>2010-01-14T19:41:00.000-08:00</published><updated>2010-01-20T16:34:15.620-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><title type='text'>ASP, Error-Handling and Microsoft's Latest Catchphrase</title><content type='html'>Microsoft's Latest Catchphrase: &lt;span style="font-style:italic;"&gt;It may be crap, but at least it is ready installed crap!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What inspired today's rant? Using ASP, of course. The question I had was to how to handle SQL errors. (This article is more than just rant; I will also tell you how to do a couple of essential tasks that ASP makes hard.)&lt;br /&gt;&lt;br /&gt;I've been using ASP for a few months, for one particular sub-project. We wanted to minimize the installed components, and as one sub-system already needed IIS, and the required scripting was fairly simple, we decided to go with that instead of installing WAMP (Windows Apache/MySQL/PHP).&lt;br /&gt;&lt;br /&gt;I'm using ADODB COM object for the database connectivity to Microsoft SQL Server. I've used the same COM object from PHP before, and errors get thrown as exceptions. I wrap them in try/catch blocks and everything is good. A bit of searching discovered how it works in ASP/VBScript. First exceptions are thrown just the same. Second, there is no try/catch functionality.&lt;br /&gt;&lt;br /&gt;Read those two key facts again, and have a good long think about them. &lt;br /&gt;&lt;br /&gt;Some more searching discovered things are not quite that bad, and you can handle errors. The first thing you must do is precede your function with "on error resume next". That tells it to ignore errors. Then immediately after any action that might have had an error you do:&lt;br /&gt;&lt;br /&gt;if err.Number &lt;&gt; 0 then&lt;br /&gt;    'Handle errors here&lt;br /&gt;end if&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://www.eggheadcafe.com/forumarchives/scriptingVisualBasicscript/Oct2005/post23966274.asp"&gt;here&lt;/a&gt; and &lt;a href="http://adamv.com/dev/articles/hatevbs/vbscript"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;By the way, to turn off "resume next" mode, you give the almost mystical "On Error Goto 0". And the other thing you need to know is that resume next mode only affects the current function; if you call another function and that function doesn't explicitly "on error resume next" then any error there will terminate the script. Which I suppose is better than the confusion resulting if the flag was global.&lt;br /&gt;&lt;br /&gt;As my next piece of evidence let me show you how to do the equivalent of PHP's "s=file_get_contents(fname);" in ASP (due to the afore-mentioned difficulties in error-reporting it returns a blank string if the file does not exist):&lt;br /&gt;&lt;br /&gt;Function file_get_contents(fname)&lt;br /&gt;fname=Server.MapPath(fname)&lt;br /&gt;mode=1  'Read-only&lt;br /&gt;set FSO = server.createobject("Scripting.FileSystemObject")&lt;br /&gt;if(FSO.FileExists(fname))then&lt;br /&gt;    set fp= FSO.OpenTextFile( fname, mode)&lt;br /&gt;    file_get_contents=fp.ReadAll&lt;br /&gt;    fp.Close&lt;br /&gt;    set fp= nothing&lt;br /&gt;else&lt;br /&gt;    file_get_contents=""&lt;br /&gt;end if&lt;br /&gt;set FSO = nothing&lt;br /&gt;End Function&lt;br /&gt;&lt;br /&gt;Oh, what fun! But I try to be fair, and every language has its good and bad points, so I tried to think up all the good features of ASP. After all, I have actually been writing useful, working code in ASP. The bad points flashed into my mind at every turn, but in the end I did manage to find one good point...&lt;br /&gt;&lt;br /&gt;It is already installed, if you are already using IIS.&lt;br /&gt;&lt;br /&gt;Which brings us back to that catchphrase that Microsoft is hurriedly trade-marking in 120 countries worldwide: &lt;span style="font-style:italic;"&gt;It may be crap, but at least it is ready installed crap!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;(By the way, speaking of ready-installed crap, did you know IE6 still has 15-20% market share? And IE6+IE7+IE8 together have 67% share? It was news to me - I thought the browser wars were over and everyone used Firefox, or at least Safari!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-837155769575512843?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/837155769575512843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=837155769575512843' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/837155769575512843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/837155769575512843'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/01/asp-error-handling-and-microsofts.html' title='ASP, Error-Handling and Microsoft&apos;s Latest Catchphrase'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7960305802602911640</id><published>2010-01-05T23:39:00.000-08:00</published><updated>2010-08-10T17:13:25.888-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gadgets'/><title type='text'>Portable PDF reader</title><content type='html'>More and more books, especially tech books, are only available as PDF. Another advantage is I can get the PDF delivered immediately rather than the "2-4 weeks" amazon.jp likes to tell me. But I don't like sitting at my computer to read - I want to be able to sit in an armchair, on the train, in a cafe, etc. to read (see also my &lt;a href="http://darrendev.blogspot.com/2009/12/php-architect-no-more-print-version.html"&gt;moan about php Architect magazine going PDF only&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I've done some searching, and some asking, and I suspect the device I want does not yet exist, but I'm open to suggestions. I don't want the heavy bulk of a notebook. The Kindle (or similar) seem hard-wired for content being sold by Amazon (or whoever), which is no good at all (Update: PDF supported since version 2.3, and content can be uploaded over USB cable; see &lt;a href="http://en.wikipedia.org/wiki/Amazon_Kindle#cite_ref-PDF_45-2"&gt;wikipedia article&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I'm very interested in how easy it is to read a PDF formatted for A4 on an iPhone (or iPod Touch), as that would have lots of other advantages (e.g. portable video player, as well as portable email and web browsing in the case of the iPhone).&lt;br /&gt;&lt;br /&gt;UPDATE: &lt;a href="http://en.wikipedia.org/wiki/List_of_e-book_readers"&gt;list of e-book readers&lt;/a&gt;&lt;br /&gt;Apparently the Consumer Electronics Show in Las Vegas this month will see some new e-book readers announced. So, sitting on my hands seems best at the moment.&lt;br /&gt;&lt;br /&gt;UPDATE:&lt;br /&gt;Video demo of &lt;a href="http://www.youtube.com/watch?v=I8GX5lzcpsA"&gt;AjiReader viewing a PDF on the iphone&lt;/a&gt; (apparently the app costs $1)&lt;br /&gt;&lt;br /&gt;Another &lt;a href="http://www.youtube.com/watch?v=2cuj1W8QNHs"&gt;video demo showing PDF, DOC, XLS, PPT&lt;/a&gt; but the comments seem to be saying you need a jail-broken phone for this?&lt;br /&gt;&lt;br /&gt;UPDATE: (8 months later!)&lt;br /&gt;The &lt;a href="http://www.amazon.com/kindle"&gt;new Kindle&lt;/a&gt; looks good. I decided to stop Waiting For The Next Big Thing, and went ahead and pre-ordered one. It does PDFs, it does Japanese, everyone I've spoken to says e-ink is great; but, above all, at $139 it is now affordable.&lt;br /&gt;&lt;br /&gt;I also ordered the case with built-in light; it was 1/3rd of the total cost, so I dithered a lot about that. But while I think the case is over-priced it also looks very cool.&lt;br /&gt;&lt;br /&gt;Some people said the 6" screen is still too small to read A4 PDFs in comfort. If I find it unusable for that (my key need) I'm hoping I might be able to resell it at only a small loss. After all, ordering from Japan is a pain.&lt;br /&gt;&lt;br /&gt;I'll put up a review in about a month. Watch This Space :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7960305802602911640?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7960305802602911640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7960305802602911640' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7960305802602911640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7960305802602911640'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/01/portable-pdf-reader.html' title='Portable PDF reader'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-1508997321753059226</id><published>2010-01-04T00:22:00.000-08:00</published><updated>2010-01-04T01:23:00.313-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Doctrine ORM</title><content type='html'>I mentioned &lt;a href="http://darrendev.blogspot.com/2009/10/regex-article-in-phpa.html"&gt;before&lt;/a&gt;, when talking about my Step-By-Step Regex article in php|a magazine, that the same issue (&lt;a href="http://www.phparch.com/magazine/index/104"&gt;August 2009&lt;/a&gt;) had some other interesting articles.&lt;br /&gt;&lt;br /&gt;One is about &lt;a href="http://www.adminer.org/en/"&gt;Adminer&lt;/a&gt;, an alternative to phpMyAdmin which is designed to be very compact and exists in just one file (so it is easy to upload temporarily to a production server for instance); the article is mainly about how Adminer was written, which was interesting.&lt;br /&gt;&lt;br /&gt;But the article - and library - I most want to talk about is &lt;a href="http://www.doctrine-project.org/"&gt;Doctrine ORM&lt;/a&gt;. The main problem this library is solving is this: when you add a field to your database you have to make the change in at least two places, your PHP script and your database. That sucks. The article describes itself as a tour around Doctrine's more advanced features, but I found I didn't need any other introduction, and it sold the features in a very clear and logical way.&lt;br /&gt;&lt;br /&gt;The first thing I liked was you can describe your database schema in any of three ways: SQL, PHP, or the compact YAML files, and utilities are provided to convert between all three. By SQL I mean an existing database, that you could have been creating interactively from phpMyAdmin, or it could be a legacy database that has been around for years and now needs to be connected to a php script.&lt;br /&gt;&lt;br /&gt;What only the Yaml and PHP schema formats can specify are what Doctrine calls behaviours. By adding the Timestampable behaviour it will add created_at and updated_at fields to the database and keep them up to date for you. The Sluggable behaviour is for making URLs and is demonstrated in the php|a article, but others that caught my eye are SoftDelete (records just get the deleted_at field set when you delete, rather than physically being removed), and Versionable (each time a record is changed the previous record is saved, and can be reverted to). NestedSet helps build trees, I18n is for holding translations (see below), Searchable does indexing, Geographical can find nearby places.&lt;br /&gt;&lt;br /&gt;Incidentally I covered I18n in a &lt;a href="http://darrendev.blogspot.com/2009/08/storing-website-translations-in-sql.html"&gt;previous article&lt;/a&gt; and Doctrine is using type 1, but without the language table to store collation. There is no consideration of different collations, and no concept of &lt;span style="font-style:italic;"&gt;default language&lt;/span&gt;. It is sufficient for most purposes though.&lt;br /&gt;&lt;br /&gt;Looking at some behaviours &lt;a href="http://www.doctrine-project.org/extensions"&gt;not in the Doctrine core&lt;/a&gt; distribution, Taggable looks interesting, allowing you to add blog-style tags. Locatable apparently ties in with Google Maps to get latitude and longitude automatically given an address (it could then be used, for instance, with the Geographical behaviour to automatically find all people within 10km of you based on just their address). EventLoggable logs all actions (select, update, delete, etc.) to a disk file. Blameable records (in the table) who last made any change to a record.&lt;br /&gt;&lt;br /&gt;Doctrine's behaviours feature is powerful, portable, the existing ones are customizable (all the above seem to have sensible defaults but also can have their behaviour fine-tuned) and new ones look easy to write.&lt;br /&gt;&lt;br /&gt;The third big feature of Doctrine that I like is the way data is retrieved on demand, and how that ties in with table relations (see listing 6 in the php|a article). And if this becomes inefficient you can optimize without having to change much code (listing 7). I'm not a big fan of using PHP to write SQL queries. Generally to do anything interesting you end up dropping down to writing SQL anyway. But I was impressed with the example in the article, and you only need to start explicitly describing joins if you have profiled and found you need to optimize.&lt;br /&gt;&lt;br /&gt;(By the way, it goes without saying that Doctrine ORM is database-neutral, not tying you into MySQL or any other database.)&lt;br /&gt;&lt;br /&gt;The final cool Doctrine feature is migrations. When you make a change to the schema in either your YAML file or your PHP file you run a script and it will make a migration file. This can then be run to move a database between versions (backwards or forwards). I admit I currently do this with some hand-crafted SQL scripts, which are one way only, and painful. And sometimes I'm not even that organized.&lt;br /&gt;&lt;br /&gt;I've not used Doctrine ORM for a real project, but I expect I will try it out very soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1508997321753059226?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1508997321753059226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1508997321753059226' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1508997321753059226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1508997321753059226'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2010/01/doctrine-orm.html' title='Doctrine ORM'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-9197766213693894799</id><published>2009-12-19T17:18:00.000-08:00</published><updated>2010-10-24T22:13:10.617-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='regexes'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><title type='text'>Right Sed Fred, I'm too sexy to search and replace</title><content type='html'>Last week I hand-edited 22 XML files to change one attribute in each. I had only ten minutes, and I knew that &lt;span style="font-style:italic;"&gt;solution&lt;/span&gt; could be done in that time.&lt;br /&gt;&lt;br /&gt;Today I had exactly the same task, but with less time pressure, I went hunting. Here is the solution I used:&lt;br /&gt;sed -i 's/old/new/g' *.xml&lt;br /&gt;&lt;br /&gt;-i means replace inline. I had struggled with sed years ago and thought it was a horrible monster that made emacs look user-friendly in comparison. But that is so simple. Perhaps I was hurt before by trying to do something that couldn't be described as a simple regex?&lt;br /&gt;&lt;br /&gt;Actually I vaguely remember my need at that time was to modify all html files in a directory tree, which sed cannot do. But with find it can:&lt;br /&gt;find . -iname \*.html -execdir sed -i 's/&amp;lt;html&gt;/&amp;lt;html mytag="test"&gt;/g' {} +&lt;br /&gt;&lt;br /&gt;That inserts an attribute in the html tag of all *.html files in current directory and its subdirectories. (Shamelessly stolen from comments on this page &lt;a href="http://mandrivausers.org/index.php?/topic/41133-sed-on-every-file-in-every-folder/"&gt;here&lt;/a&gt;, and then I did a quick test to confirm I hadn't introduce a typo.)&lt;br /&gt;&lt;br /&gt;Cool. One step closer to unix guruness. (sed is also available in cygwin, which is where I was actually doing the edit that started this article.)&lt;br /&gt;&lt;br /&gt;UPDATE: for an example where tr is more useful than sed see&lt;br /&gt;&lt;a href="http://darrendev.blogspot.com/2010/04/find-grep-and-tr.html"&gt;find, grep and tr&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;UPDATE: Here is an expansion of the find+sed example above. I wanted to alter three types of extensions: html, php, phtml. And I only wanted to alter those files in just certain subdirectories. The -regex parameter of find seemed to do the job:&lt;br /&gt;&lt;br /&gt;find . -regex './\(dir1\|dir2/subdir1\|dir3\|dir4\)/.+\.\(html\|php\|phtml\)' -execdir sed -i 's/ABC/XYZ/g' {} +&lt;br /&gt;&lt;br /&gt;That is the command exactly as you run it at the bash command prompt. Notice that, in the regex, not just (), but also | need to be prefixed with a single backslash.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-9197766213693894799?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/9197766213693894799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=9197766213693894799' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/9197766213693894799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/9197766213693894799'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/12/right-sed-fred-im-too-sexy-to-search.html' title='Right Sed Fred, I&apos;m too sexy to search and replace'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6307578542169114002</id><published>2009-12-01T02:49:00.000-08:00</published><updated>2009-12-01T03:00:05.881-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><title type='text'>php Architect: no more print version</title><content type='html'>The subject says it all: the publishers of the excellent php|a magazine have suddenly stopped the print edition. It has gone PDF only.&lt;br /&gt;(Click php|a on the right to see my other blog articles about php|a magazine.)&lt;br /&gt;&lt;br /&gt;This is such a shame: I would willingly pay much more for a print version than a PDF version. In fact I was doing exactly that. Then a year ago they turned everyone to be Print+PDF subscribers, cut the price dramatically, and gave me free extra 12 issues to make up for the price cut. In fact that only works out as 6 or so free issues, but as they were free I can hardly complain.&lt;br /&gt;&lt;br /&gt;It is just a shame they fiddled with their business model, as obviously the price cut made the print magazine too expensive.&lt;br /&gt;&lt;br /&gt;Why do I prefer the print version? It can be read on the train, read in the mountains, is light and portable. It can be kept on my bookshelf, pulled off and opened on my desk while I'm coding. As an author, a print magazine is also much more impressive. It is something physical I can show a potential client, something I can show my Mum.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6307578542169114002?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6307578542169114002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6307578542169114002' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6307578542169114002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6307578542169114002'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/12/php-architect-no-more-print-version.html' title='php Architect: no more print version'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-7839625083024807573</id><published>2009-11-23T17:56:00.000-08:00</published><updated>2009-11-23T18:34:05.390-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='boost'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Escaping CSV in C++</title><content type='html'>There are two escaping rules for each field in a comma-separated value row:&lt;br /&gt;1. Change each double quote to two double quotes.&lt;br /&gt;2. Surround with double quotes if the field contains a comma or double quote.&lt;br /&gt;&lt;br /&gt;These are the rules used by Excel and all other software that deals with CSV data.&lt;br /&gt;&lt;br /&gt;As an example, if my fields are:&lt;br /&gt;  hello world&lt;br /&gt;  a,b,c&lt;br /&gt;  "CSV" is popular&lt;br /&gt;  ""&lt;br /&gt;&lt;br /&gt;Then it becomes:&lt;br /&gt;  hello world,"a,b,c,","""CSV"" is popular",""""""&lt;br /&gt;&lt;br /&gt;C++ has a justified reputation as a hard language for text manipulation. Boost has &lt;a href="http://www.boost.org/doc/libs/1_41_0/libs/libraries.htm#String"&gt;libraries&lt;/a&gt; to make it a little easier, but I didn't want to add Boost as a dependency for a project I was working on. Fortunately std::string's replace() function turned out to be more powerful than I had realized:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void output_csv(std::ostream &amp;out,std::string s){&lt;br /&gt;if(s.find('"')!=std::string::npos){  //Escape double-quotes&lt;br /&gt;    std::string::size_type pos=0;&lt;br /&gt;    while(1){&lt;br /&gt;        pos=s.find('"',pos);&lt;br /&gt;        if(pos==std::string::npos)break;&lt;br /&gt;        s.replace(pos,1,"\"\"");&lt;br /&gt;        pos+=2; //Need to skip over those two quotes, to avoid an infinite loop!&lt;br /&gt;        }&lt;br /&gt;    out&amp;lt;&amp;lt;'"'&amp;lt;&amp;lt;s&amp;lt;&amp;lt;'"';&lt;br /&gt;    }&lt;br /&gt;else if(s.find(',')!=std::string::npos){ //Need to surround with "..."&lt;br /&gt;    out&amp;lt;&amp;lt;'"'&amp;lt;&amp;lt;s&amp;lt;&amp;lt;'"';&lt;br /&gt;    }&lt;br /&gt;else out&amp;lt;&amp;lt;s;   //No escaping needed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you like compact code then the while loop can be rewritten:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void output_csv(std::ostream &amp;out,std::string s){&lt;br /&gt;if(s.find('"')!=std::string::npos){  //Escape double-quotes&lt;br /&gt;    for(std::string::size_type n=0;(n=s.find('"',n))!=std::string::npos;n+=2)s.replace(n,1,"\"\"");&lt;br /&gt;    out&amp;lt;&amp;lt;'"'&amp;lt;&amp;lt;s&amp;lt;&amp;lt;'"';&lt;br /&gt;    }&lt;br /&gt;else if(s.find(',')!=std::string::npos)out&amp;lt;&amp;lt;'"'&amp;lt;&amp;lt;s&amp;lt;&amp;lt;'"';&lt;br /&gt;else out&amp;lt;&amp;lt;s;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;P.S. If you need to do the same in PHP, PHP 5.1 finally introduced &lt;a href="http://jp.php.net/manual/en/function.fputcsv.php"&gt;fputcsv&lt;/a&gt; for it. The comments on that page show how to do it in older versions of PHP; my &lt;a href="http://dcook.org/software/fclib/"&gt;fclib&lt;/a&gt; library also contains functions for it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7839625083024807573?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7839625083024807573/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7839625083024807573' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7839625083024807573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7839625083024807573'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/11/escaping-csv-in-c.html' title='Escaping CSV in C++'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-8637771862054086737</id><published>2009-11-16T18:54:00.001-08:00</published><updated>2009-11-19T18:26:47.942-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>samba and strange permissions</title><content type='html'>On my linux server I run a samba share, which is used by both Linux and Windows clients on the LAN. I moved it from an old FedoraCore machine to Ubuntu a few months ago, and ever since have been getting strange permissions: text files kept becoming executable (but only for user, not group or other).&lt;br /&gt;&lt;br /&gt;It took me this long to realize what was going on, and when I tried to track it down about a week ago I concluded it was SciTE being strange on just samba partitions. I.e. I'd edit a file with rw-r--r-- permissions, save it, and it would end up with "rwxr--r--". Every time. But not on normal partitions, and gedit didn't do it on the samba partition. I noticed today that files created by a PHP script also got those weird permissions, and the penny dropped: gedit must be explicitly setting permissions when it saves a file. Scite wasn't the cause of the problem at all.&lt;br /&gt;&lt;br /&gt;So, I went hunting again. I referred to &lt;a href="http://ubuntuforums.org/showthread.php?t=288534"&gt;Mount samba shares with utf8 encoding using cifs&lt;/a&gt; a lot, but in fact it didn't give me the answer I wanted: the instructions there gave me the same problem. (It did show me how to set my samba partition to mount from /etc/fstab, replacing my crude entry in rc.local though.)&lt;br /&gt;&lt;br /&gt;Hunting through the troubleshooting section I found the "nounix" flag and tried that. Initially it made things worse, giving all files rwxrwxrwx permissions. Then I changed from "file_mode=0777,dir_mode=0777" to "file_mode=0644,dir_mode=0755" (which was what I had originally), and that combined with nounix works! All text files are rw-r--r-- before and after saving. Oh, the other change I added relative to the above page was including "uid=darren,gid=darren". Otherwise files were owned by "nobody:root" and I didn't have permission to edit anything (even with the suggested 0777 settings).&lt;br /&gt;&lt;br /&gt;My guess is that my old FedoraCore samba server didn't have the unix extensions, Ubuntu 8 does, and somehow those unix extensions are misconfigured in Ubuntu. My samba server configuration is all defaults however... Anyway, it now works the way it has for the previous few years, so I'm happy.&lt;br /&gt;&lt;br /&gt;UPDATE: I just realized this has also fixed another irritation - delete (that moves to the Trash folder) hasn't been working on that partition, but now it does. Trash folder vs. direct delete was only a minor factor; what was really annoying was every time I pressed delete it then popped up a dialog box requiring me to confirm it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-8637771862054086737?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/8637771862054086737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=8637771862054086737' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8637771862054086737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/8637771862054086737'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/11/samba-and-strange-permissions.html' title='samba and strange permissions'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-1858190899377377317</id><published>2009-11-04T21:20:00.000-08:00</published><updated>2009-11-04T21:24:40.120-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud computing'/><title type='text'>How much is that password worth?</title><content type='html'>These people have published details about how they used Amazon EC2 to crack passwords:&lt;br /&gt;&lt;a href="http://news.electricalchemy.net/2009/10/cracking-passwords-in-cloud.html"&gt;http://news.electricalchemy.net/2009/10/cracking-passwords-in-cloud.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Personally, I skipped all the details and went straight to the interesting conclusions page:&lt;br /&gt;&lt;a href="http://news.electricalchemy.net/2009/10/password-cracking-in-cloud-part-5.html"&gt;http://news.electricalchemy.net/2009/10/password-cracking-in-cloud-part-5.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It tells you how much it will cost someone (in EC2 charges) to crack your passwords, based on their lengths and the number of characters you use.&lt;br /&gt;&lt;br /&gt;I used to think 8 characters was a good password. Seems it is worth about $3, or $45 if I've mixed in some numbers. Gulp. And all this is assuming there are no dictionary words in there. Double gulp.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1858190899377377317?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1858190899377377317/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1858190899377377317' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1858190899377377317'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1858190899377377317'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/11/how-much-is-that-password-worth.html' title='How much is that password worth?'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-5509102213485004420</id><published>2009-10-25T17:38:00.000-07:00</published><updated>2009-10-25T18:57:26.677-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='regexes'/><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Regex Article in php|a</title><content type='html'>The &lt;a href="http://www.phparch.com/magazine/index/104"&gt;August edition&lt;/a&gt; of PHP Architect magazine contained my introductory article on regexes. It has a PHP slant but mainly it is about regexes. Well, most of it is introductory but it contains a bonus section at the end using advanced regexes in SQL to repair a database in situ.&lt;br /&gt;&lt;br /&gt;The print magazine arrived by air mail a couple of weeks ago and I finally got to read it. I was pleased (and relieved) that it read well. If you have read it I'd love to get some constructive criticism - especially if you are uncomfortable with regexes.&lt;br /&gt;&lt;br /&gt;It was also my first print article to use colour, and I think that worked well too.&lt;br /&gt;&lt;br /&gt;One correction in the complicated "Repairing With Regexes" section at the end of the article. The text says "So &lt;span style="font-weight:bold;color:red"&gt;\1&lt;/span&gt; has to be written \\\\1 (that is four backslashes)." That seemed a strange thing to write, so I took a look at the unit test source code (included with the PDF download version of php|a magazine) I had written. Four backslashes is correct when in PHP, but when in SQL it should be "&lt;span style="font-weight:bold;color:red"&gt;\\1&lt;/span&gt;" not "\1".&lt;br /&gt;(I just checked my emails from when we were proof-reading, and one of the edits had removed all those backslashes; I caught that at the time but ironically I got it wrong when putting them back in.)&lt;br /&gt;&lt;br /&gt;The other minor correction I only realized after Arne Blanket's column in the same magazine issue! I had written: "...an IP address after that at sign, such as darren@10.0.0.1, which, while unusual, is technically valid". In fact "darren@&lt;span style="font-weight:bold;color:red"&gt;[&lt;/span&gt;10.0.0.1&lt;span style="font-weight:bold;color:red"&gt;]&lt;/span&gt;" is the technically valid form. Which is doubly annoying because my article's regex would reject those square brackets and I hadn't explicitly pointed that out. (No harm done, though, as this email form is highly discouraged.)&lt;br /&gt;&lt;br /&gt;Arne's column also points out that top-level domains are no longer just two or three characters. So my '\.[a-zA-Z]{2,3}$' suggestion should really have been '\.[a-zA-Z]{2,}$'. Luckily, that suggestion was just in a list of other ideas, not part of the article's main regex.&lt;br /&gt;&lt;br /&gt;(By the way I enjoyed, and will blog about real soon, some other articles in this particular issue; if you are not a subscriber, and your work involves data and PHP, then this is &lt;span style="font-style:italic;"&gt;the&lt;/span&gt; back issue you should get!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-5509102213485004420?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/5509102213485004420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=5509102213485004420' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5509102213485004420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5509102213485004420'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/10/regex-article-in-phpa.html' title='Regex Article in php|a'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-7244422186717323207</id><published>2009-10-14T18:51:00.000-07:00</published><updated>2009-12-15T16:09:32.877-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Control firefox from PHP?</title><content type='html'>Internet Explorer can be controlled from a COM object interface, and therefore from PHP (i.e. any scripting language that has COM support).&lt;br /&gt;&lt;br /&gt;But is there a way to get script control of firefox? Ideally I'm looking for a platform-independent solution, and something I can use from PHP. Google is not helping (PHP's dominance as a server-side technology overwhelms the client-side related hits).&lt;br /&gt;&lt;br /&gt;Here is my dream PHP script:&lt;br /&gt;&lt;br /&gt;  $firefox=new FirefoxInstance();&lt;br /&gt;  $firefox-&gt;set_url("http://dcook.org/work/");&lt;br /&gt;  $firefox-&gt;wait_until_fully_loaded();&lt;br /&gt;  $links=$firefox-&gt;get_links();&lt;br /&gt;  $found=false;&lt;br /&gt;  foreach($links as $id=&gt;$info){&lt;br /&gt;    if($info['text']=="MLSN"){$firefox-&gt;click_link($id);$found=true;break;}&lt;br /&gt;    }&lt;br /&gt;  if(!$found)echo "MLSN link is missing...\n";&lt;br /&gt;  &lt;br /&gt;Or:&lt;br /&gt;&lt;br /&gt;  $form=$firefox-&gt;get_form("login");&lt;br /&gt;  $form-&gt;set("username","guest");&lt;br /&gt;  $form-&gt;set("password","guest");&lt;br /&gt;  $form-&gt;submit();&lt;br /&gt;&lt;br /&gt;Et cetera. I.e. I'm talking about operating firefox the same way a user does; I know I can grab the raw HTML, parse it, etc. all from PHP, but that doesn't test a web site the same way clicking links in a browser does. Especially web pages with javascript, iframes, AJAX, etc.&lt;br /&gt;&lt;br /&gt;(I'd heard of &lt;a href="https://developer.mozilla.org/en/XPCOM"&gt;XPCOM&lt;/a&gt; but, if I've understood it correctly, it is a library to build firefox and its extensions, not something to control firefox? It also has no PHP bindings.)&lt;br /&gt;&lt;br /&gt;BTW, going back to controlling IE from the COM interface, I don't suppose anyone has seen a detailed tutorial on how to use it to fill out and submit forms? I only ever see simple examples of how to set the URL, but I believe full control should be possible.&lt;br /&gt;Dec 16th 2009 UPDATE: This came to the top of my to-do list so I read the MSDN docs on the Internet Explorer COM object, and now it is my understanding that I cannot manipulate and submit forms via the COM object. None of the example usage even hinted at doing this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7244422186717323207?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7244422186717323207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7244422186717323207' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7244422186717323207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7244422186717323207'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/10/control-firefox-from-php.html' title='Control firefox from PHP?'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-6909460844412245998</id><published>2009-09-24T17:24:00.000-07:00</published><updated>2009-09-24T17:54:13.611-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>PHP, reliability, ASP and remote desktops</title><content type='html'>I've been working in ASP on an IIS server recently. Not my environment of choice, but the scripts I need to write are relatively simple, so I decided it was easier to go with it than ask for PHP to be installed (which then requires apache). This is quite a helpful page:&lt;br /&gt;  &lt;a href="http://www.design215.com/toolbox/asp.php"&gt;http://www.design215.com/toolbox/asp.php&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We'll see how it goes. In many of the systems I work on, windows or linux, I usually use commandline PHP as the glue. I'm always amazed at how rock solid it is. Especially on Windows it is always the most stable element. E.g. I've a 24/7 PHP script that deals with COM objects. The COM objects crash regularly. But I just catch the exception in PHP, delete the COM object, create another one and off we go. Needing to use 3rd party libraries only available in C are the only reason I write C++ recently, but they cause me no end of trouble (mysterious crashes deep in windows system calls for instance). Windows itself needs to be rebooted every 3 or 4 weeks or it starts to go all wobbly on us, and my 24/7 PHP scripts simply run for those 4 weeks without a single problem or restart.&lt;br /&gt;&lt;br /&gt;Of course saying PHP is more stable than C would be stupid; PHP is itself a C app. The difference is that PHP is a well-crafted piece of code, better than the COM objects, 3rd party C libraries and Microsoft OSes I'm comparing it to.&lt;br /&gt;&lt;br /&gt;Going back to IIS, I don't have it installed locally, so have been developing on a remote server. "rdesktop" wins this month's "application I wish I'd discovered months ago" award. Previously I would have to boot up my windows notebook just to use remote desktop. rdesktop gives me this functionality on linux, works perfectly (touch wood) and copy and paste works too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6909460844412245998?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6909460844412245998/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6909460844412245998' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6909460844412245998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6909460844412245998'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/09/php-reliability-asp-and-remote-desktops.html' title='PHP, reliability, ASP and remote desktops'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2427780408926406334</id><published>2009-09-03T02:14:00.000-07:00</published><updated>2009-09-03T16:10:03.272-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Windows SEH in C++</title><content type='html'>A windows app was mysteriously dying, roughly once a day; no clues in any of the various log files.&lt;br /&gt;So I enabled Windows SEH:&lt;br /&gt;  _set_se_translator(win32_exception_handler);&lt;br /&gt;&lt;br /&gt;(See &lt;a href="http://msdn.microsoft.com/en-us/library/5z4bw5h5(VS.80).aspx"&gt;MSDN article about _set_se_translator&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;And here is my implementation of that function:&lt;br /&gt;&lt;br /&gt;void win32_exception_handler(unsigned int code, EXCEPTION_POINTERS *ep){&lt;br /&gt;throw new win32_exception(code,ep);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;And the class (cut down to basics) is:&lt;br /&gt;&lt;br /&gt;class win32_exception{&lt;br /&gt;std::string info;&lt;br /&gt;public:&lt;br /&gt;win32_exception(unsigned int code,EXCEPTION_POINTERS *ep){&lt;br /&gt;//Describe it in info&lt;br /&gt;}&lt;br /&gt;const char *what()const{return info.c_str();}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Then, I have this code (which was already here before I enabled SEH):&lt;br /&gt;&lt;br /&gt;try{&lt;br /&gt;    e=main_loop(stuff);&lt;br /&gt;catch(std::exception &amp;e){&lt;br /&gt;    ErrorLog()&amp;lt;&amp;lt;"Got a std::exception thrown by main_loop:"&amp;lt;&amp;lt;e.what()&amp;lt;&amp;lt;"  (will treat as fatal)\n";&lt;br /&gt;    return -9;&lt;br /&gt;    }&lt;br /&gt;catch(win32_exception &amp;e){&lt;br /&gt;    ErrorLog()&amp;lt;&amp;lt;"Caught win32_exception thrown by main_loop:"&amp;lt;&amp;lt;e.what()&amp;lt;&amp;lt;" (will treat as fatal)\n";&lt;br /&gt;    return -9;&lt;br /&gt;    }&lt;br /&gt;catch(...){&lt;br /&gt;    ErrorLog()&amp;lt;&amp;lt;"Got an unexpected exception thrown by main_loop (will treat as fatal)\n";&lt;br /&gt;    return -9;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, what I don't get is today the program died with this message:&lt;br /&gt;  Got an unexpected exception thrown by main_loop (will treat as fatal)&lt;br /&gt;&lt;br /&gt;I'm staring and staring at the code and don't get why the win32_exception code didn't catch and instead some other exception type got caught. Enabling SEH was the only change, all such exceptions should go through my function, and that only throws win32_exception instances.&lt;br /&gt;&lt;br /&gt;The other mystery to me is what is the type is of this "unexpected" exception.&lt;br /&gt;&lt;br /&gt;(I'm hoping to come back and add to this blog entry when I work out what is going on!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2427780408926406334?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2427780408926406334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2427780408926406334' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2427780408926406334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2427780408926406334'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/09/windows-seh.html' title='Windows SEH in C++'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-254835836809815490</id><published>2009-08-23T04:27:00.000-07:00</published><updated>2009-08-24T04:05:51.601-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>php6 - maybe not so painful?</title><content type='html'>I long ago learned that if you want to get development work done then always use the distro's own version of PHP, Apache, etc, rather than compiling your own version. Otherwise every upgrade due to a security or bug fix requires you to first realize the need (costs time keeping up with the study) and then do something about it (costs time). Time is money; not spending the time is insecure. So I outsource all of that to the experts at my distro or ISP.&lt;br /&gt;&lt;br /&gt;Consequences are that my code avoids using the latest feature, and tries to work on as wide a set of versions as possible. PHP 5.0.0 was released in July 2004 yet at the start of this year two of my clients were still using php 4. One of them was still using php 4.2. (They've both upgraded this year; one because they moved to a new ISP, the other because we were troubleshooting code and running out of ideas, and wondered if moving to php 5 might magically fix it.)&lt;br /&gt;&lt;br /&gt;Which is a long-winded introduction to say that PHP 6 will be out before long, and I'm very &lt;span style="font-style:italic;"&gt;un&lt;/span&gt;excited. It brings native unicode; but the mbstring extension already did the job fine. It removes bad stuff that was already deprecated, but I didn't use it anyway.&lt;br /&gt;&lt;br /&gt;This article made me concerned it may turn out to be a negative experience overall, rather than a neutral one:&lt;br /&gt;  &lt;a href="http://www.linux-mag.com/id/7433/2/"&gt;http://www.linux-mag.com/id/7433/2/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It says PHP4-style classes are disappearing. Yet they work fine in PHP 5, and E_STRICT has not been complaining if I've not explicitly written "public". Of even more concern is php 5.3 apparently brings in this change too?&lt;br /&gt;&lt;br /&gt;However, it turns out this was just a poor Linux Magazine article. What is being removed in php 6 is "ZE1 compatability mode" (see the &lt;a href="http://php.net/manual/en/ini.core.php"&gt;php manual description&lt;/a&gt;); it had defaulted to off, and only affected the way objects were copied/compared. The "php 4" example Linux Magazine show is valid php 5 code, and (as far as I can tell) will be valid php 6 code too.&lt;br /&gt;&lt;br /&gt;So that was a red herring. But I'm also bothered how &lt;span style="font-style:italic;"&gt;(string)&lt;/span&gt; works for binary strings in php 5, but will need to be changed to &lt;span style="font-style:italic;"&gt;(binary)&lt;/span&gt; for php 6 - that is a very annoying change for anyone storing binary data in strings (disabling unicode is not an option if you also need to store UTF-8 in other strings)!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-254835836809815490?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/254835836809815490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=254835836809815490' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/254835836809815490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/254835836809815490'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/08/php6-maybe-not-so-painful.html' title='php6 - maybe not so painful?'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-4035590028823742431</id><published>2009-08-20T10:14:00.000-07:00</published><updated>2009-08-20T13:22:47.343-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><title type='text'>Storing website translations in SQL</title><content type='html'>In &lt;a href="http://phparch.com/magazine/index/95"&gt;php|a magazine, April 2009&lt;/a&gt;, there was an article called "Storing Multilingual Records in the MySQL Database". As the title says, it has some mysql-specific elements, but the concepts are quite general. The author introduced and compared three alternatives; as I have used a fourth way I thought I'd write about it here.&lt;br /&gt;&lt;br /&gt;The situation was a product database, where the name, url and description have to be localized. But some entries will be untranslated and should fall back to using the default language entry.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;1. Translations in a separate table&lt;/h3&gt;&lt;br /&gt;Here is the database schema:&lt;br /&gt;&lt;br /&gt;product: product_id, group_id, price.&lt;br /&gt;product_translation: product_id (links to product), language_id (links to language), name, url, description.&lt;br /&gt;language: language_id, collation.&lt;br /&gt;&lt;br /&gt;This requires an SQL join which is hard for mysql to execute efficiently. Fulltext indexes are also a problem, as the name and description fields contain text from different languages. The only good point of this approach is that it is easy to add a new language.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;2. Data Copy&lt;/h3&gt;&lt;br /&gt;Here the group_id and price fields have been moved into the product_translation table. That makes the SQL queries a bit cleaner, as it saves one join, but doesn't really solve the other problems. And the data redundancy is an accident waiting to happen (I'm having a physical reaction just thinking about it).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;3. Translation Directly In The Database&lt;/h3&gt;&lt;br /&gt;This is like 2 above, but each language gets its own field. For instance if we have three languages, English (en), German (de) and Japanese (ja) then it looks like:&lt;br /&gt;&lt;br /&gt;product: product_id, language_id (links to language), name_en, url_en, description_en, name_de, url_de, description_de, name_ja, url_ja, description_ja, price, group_id.&lt;br /&gt;&lt;br /&gt;The advantage this brings is using the default language is easy; you can either just always select the default language field, or use &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html#function_ifnull"&gt;mysql's IFNULL&lt;/a&gt; function. E.g. either:&lt;br /&gt; SELECT name_de,name_en WHERE product_id=123;&lt;br /&gt; (and then check in your PHP script to see if name_de is blank, and if so use name_en instead)&lt;br /&gt;Or:&lt;br /&gt; SELECT IFNULL(name_de,name_en) as name WHERE product_id=123;&lt;br /&gt;&lt;br /&gt;The other advantage is FULLTEXT indices work well now, as only one language is kept in each text column. Disadvantages are the space-wasting if actual translations are sparse, the work required to add a new language, and possibly hitting the mysql maximum row size (64K).&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;4. One table per language&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;This is the approach I've used in MLSN, among others:&lt;br /&gt;&lt;br /&gt;product: product_id, group_id, price.&lt;br /&gt;product_translation_en: product_id, name, url, description.&lt;br /&gt;product_translation_de: product_id, name, url, description.&lt;br /&gt;product_translation_ja: product_id, name, url, description.&lt;br /&gt;&lt;br /&gt;Fulltext indices work well, joins are relatively easy, no data duplication. Adding a language feels cleaner than solution three above, as it doesn't require modifying an existing table, just adding a new one. If the site takes off then different languages can be split to different servers easily. And the language table can disappear as collation will now be part of the field definition when the table is created. (I think the same can be said of solution three, but the php|a article kept the language table in its schema - was that an oversight, or am I missing something?)&lt;br /&gt;&lt;br /&gt;The dark side? Getting the default language requires two queries. Or would something like this (untested) SQL work?&lt;br /&gt; SELECT ISNULL(de.name,en.name) FROM product_translation_de de,product_translation_en en WHERE de.product_id=123 AND en.product_id=123&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Thoughts?&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The 4th option works well for me, but I'd be interested to here arguments against it. Perhaps you use something different again?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4035590028823742431?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4035590028823742431/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4035590028823742431' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4035590028823742431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4035590028823742431'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/08/storing-website-translations-in-sql.html' title='Storing website translations in SQL'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3787368121857976765</id><published>2009-07-27T20:39:00.000-07:00</published><updated>2009-07-27T20:50:23.191-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='animation'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='data presentation'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='AS3'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><category scheme='http://www.blogger.com/atom/ns#' term='Arabic'/><title type='text'>New fclib and dcflash releases</title><content type='html'>A few days ago I updated my dcflash project on SourceForge. This is an open source (MIT license) actionscript library, with a data visualization emphasis:&lt;br /&gt;  &lt;a href="http://sourceforge.net/projects/dcflash/"&gt;http://sourceforge.net/projects/dcflash/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In particular there is now an AS3 version. In fact the AS2 and AS3 releases currently have only a couple of classes in common. The AS3 classes are mostly for loading images, movies, and setting up slideshows; none of the chart classes from the AS2 version (or the only partially ported and unreleased AS1 library) are there yet. As I say in the release notes, I'm open to offers to port them to AS3. (A look at &lt;a href="http://dcook.org/work/charts/"&gt;http://dcook.org/work/charts/&lt;/a&gt; will show you the kind of things in the AS1 and AS2 libraries.)&lt;br /&gt;&lt;br /&gt;And now today I've put up a &lt;a href="http://dcook.org/software/fclib/"&gt;new release of fclib&lt;/a&gt;. This is an open source (MIT license again) PHP library. Fairly ad hoc, but naturally lots of internationalization-related classes. The new 0.4.19 release contains some arabic classes, but other than that it is mostly just tweaks and small improvements:&lt;br /&gt;  &lt;a href="http://dcook.org/software/fclib/"&gt;http://dcook.org/software/fclib/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3787368121857976765?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3787368121857976765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3787368121857976765' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3787368121857976765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3787368121857976765'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/07/new-fclib-and-dcflash-releases.html' title='New fclib and dcflash releases'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-189657068515053016</id><published>2009-07-22T17:03:00.000-07:00</published><updated>2009-07-22T17:16:32.487-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Date comparisons in C++</title><content type='html'>Just been scratching my head over a code snippet that looks like this:&lt;br /&gt;&lt;br /&gt;time_t now=...;&lt;br /&gt;time_t last_tick=...;&lt;br /&gt;&lt;br /&gt;struct tm *timeinfo=localtime(&amp;now);&lt;br /&gt;...&lt;br /&gt;struct tm *tick_timeinfo=localtime(&amp;last_tick);&lt;br /&gt;...&lt;br /&gt;if(tick_timeinfo-&gt;tm_mday==timeinfo-&gt;tm_mday &amp;&amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;tick_timeinfo-&gt;tm_mon==timeinfo-&gt;tm_mon &amp;&amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;tick_timeinfo-&gt;tm_year==timeinfo-&gt;tm_year)return OK;&lt;br /&gt;else return BAD;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I'd realized this was code was always returning OK, even when last_tick was yesterday, a month ago, or even from last year.&lt;br /&gt;Can you see the bug? The above is all you need to know. Answers in a comment. No stamp required.&lt;br /&gt;&lt;br /&gt;(To be fair to my bruised ego, the problem can really only be one thing in the way I've presented it above.)&lt;br /&gt;&lt;br /&gt;Kudos to the first reply. And I'd be interested to hear how people do date comparisons in C++ more safely.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-189657068515053016?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/189657068515053016/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=189657068515053016' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/189657068515053016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/189657068515053016'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/07/date-comparisons-in-c.html' title='Date comparisons in C++'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-740448476488487574</id><published>2009-07-21T20:40:00.000-07:00</published><updated>2009-07-27T20:54:25.259-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Screen still going blank! (/proc/mtrr)</title><content type='html'>I wrote before how &lt;a href="http://darrendev.blogspot.com/2009/04/screen-keeps-going-blank.html"&gt;my screen keeps going blank&lt;/a&gt;, and how it had seemed to get worse after upgrading to software RAID. It has still been doing it, and the past week or so I've been systematically disabling things to see if I can fix it. It still happens, but less frequently, so some combination of things I've disabled may have helped?&lt;br /&gt;&lt;br /&gt;But, it does still happen, so I went googling again. I think I may have found something:&lt;br /&gt;  https://bugzilla.redhat.com/show_bug.cgi?id=446620&lt;br /&gt;  https://bugs.freedesktop.org/show_bug.cgi?id=15360&lt;br /&gt;&lt;br /&gt;It seems Intel video driver and/or the linux kernel cope badly with unusual memory configurations. I moved to software RAID at the same time as &lt;a href="http://darrendev.blogspot.com/2009/03/6gb-on-32-bit-linux.html"&gt;I moved from 2G to 6G&lt;/a&gt; main memory! Here is my /proc/mtrr:&lt;br /&gt;&lt;br /&gt;reg00: base=0xd0000000 (3328MB), size= 256MB: uncachable, count=1&lt;br /&gt;reg01: base=0xe0000000 (3584MB), size= 512MB: uncachable, count=1&lt;br /&gt;reg02: base=0x00000000 (   0MB), size=4096MB: write-back, count=1&lt;br /&gt;reg03: base=0x100000000 (4096MB), size=2048MB: write-back, count=1&lt;br /&gt;reg04: base=0x180000000 (6144MB), size= 512MB: write-back, count=1&lt;br /&gt;reg05: base=0x1a0000000 (6656MB), size= 256MB: write-back, count=1&lt;br /&gt;reg06: base=0xcf700000 (3319MB), size=   1MB: uncachable, count=1&lt;br /&gt;&lt;br /&gt;This looks like one of the problem ones from the above bug report. Unfortunately I'm at a bit of a loss what to do with it now. My understanding of the problem is something like main memory and video memory overlap, so when some program uses that memory it kills my video and X dies. My ctrl-alt-F1, breath in, breath out, ctrl-alt-F7 "fix" must reset X?? (Hhhmm, how come I never see problems with any programs having their memory overwritten by the graphics card though?)&lt;br /&gt;&lt;br /&gt;Here are my dmesg entries either side of the only mtrr complaint:&lt;br /&gt;[  142.691737] [drm] Initialized drm 1.1.0 20060810&lt;br /&gt;[  142.700818] ACPI: PCI Interrupt 0000:00:02.0[A] -&gt; GSI 16 (level, low) -&gt; IRQ 17&lt;br /&gt;[  142.700827] PCI: Setting latency timer of device 0000:00:02.0 to 64&lt;br /&gt;[  142.700880] [drm] Initialized i915 1.6.0 20060119 on minor 0&lt;br /&gt;[  142.715171] mtrr: type mismatch for d0000000,10000000 old: write-back new: write-combining&lt;br /&gt;[  143.311587] set status page addr 0x00033000&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;These are the best explanations I've found so far:&lt;br /&gt;  http://www.rage3d.com/board/showthread.php?t=33821469&lt;br /&gt;  http://www.rage3d.com/board/showthread.php?threadid=33736241&lt;br /&gt;&lt;br /&gt;Both the solutions in the 2nd link talk about modifying the video=... parameter given to the kernel. But I don't have one of them. I just tried throwing it on the end of the kernel commands but the /proc/mtrr output is unchanged, so I don't think it had any effect:&lt;br /&gt;   kernel /vmlinuz-2.6.24-24-server root=/dev/md3 ro quiet splash nomttr&lt;br /&gt;&lt;br /&gt;I've just tried changing Advanced|Chipset|Northbridge|Memory Remap from Enabled to Disabled, in my bios. Main memory has dropped from 6G to about 5.3G, and /proc/mtrr has changed to:&lt;br /&gt;reg00: base=0xd0000000 (3328MB), size= 256MB: uncachable, count=1&lt;br /&gt;reg01: base=0xe0000000 (3584MB), size= 512MB: uncachable, count=1&lt;br /&gt;reg02: base=0x00000000 (   0MB), size=4096MB: write-back, count=1&lt;br /&gt;reg03: base=0x100000000 (4096MB), size=2048MB: write-back, count=1&lt;br /&gt;reg04: base=0xcf700000 (3319MB), size=   1MB: uncachable, count=1&lt;br /&gt;reg05: base=0xcf800000 (3320MB), size=   8MB: uncachable, count=1&lt;br /&gt;&lt;br /&gt;Well, it looks just as dubious to my untrained eye (for starters it still seems to think there is 6G of memory), but at least it is different. Let's see what happens... no screen still goes blank.&lt;br /&gt;&lt;br /&gt;What about DVMT mode? http://www.techarp.com/showfreebog.aspx?lang=0&amp;bogno=322&lt;br /&gt;I currently am in "DVMT" mode, with 256M. I've plenty of memory, so let's try fixed mode, with 256M. ...it made no difference to the /proc/mtrr output, and the problem still happens.&lt;br /&gt;&lt;br /&gt;(There is also an "ASMT resolution" option in the bios, which is "enabled". Google isn't helping me much here, but some explanation is here and it doesn't seem to be to do with video memory: http://www.avsforum.com/avs-vb/showthread.php?t=938473&amp;page=12)&lt;br /&gt;&lt;br /&gt;(I've been holding off on posting this for the past week, in the hope I'd resolve the problem. But unfortunately I haven't yet. I guess fiddling with /proc/mtrr directly is needed, but I don't have time to investigate that currently.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-740448476488487574?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/740448476488487574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=740448476488487574' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/740448476488487574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/740448476488487574'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/07/screen-still-going-blank-procmtrr.html' title='Screen still going blank! (/proc/mtrr)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-429005694126271120</id><published>2009-07-17T19:28:00.000-07:00</published><updated>2009-07-17T19:55:10.454-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Email on ubuntu (exim)</title><content type='html'>When I installed real player a few months back I noticed it had installed exim. I thought that highly suspicious - what was real player planning to do behind my back? Well, I thought I'd try removing it today, and discovered realplay depends on lsb-core (linux standards), and lsb-core depends on exim. realplay was innocent.&lt;br /&gt;&lt;br /&gt;The magic command to remove exim is:&lt;br /&gt;dpkg --purge --ignore-depends=lsb-core --ignore-depends=mailx exim4 exim4-base exim4-config exim4-daemon-light&lt;br /&gt;&lt;br /&gt;But after doing that I changed my mind. As I discovered I couldn't send any email from commandline. Did somebody just say "Duh!". Well I was hoping I could then configure something to tell it to use SMTP directly to my ISP. (Why do I want to send email from commandline? Well, yes to test programs, but much more importantly that that I realized my system has been sending me warning emails and they've been undeliverable, so for instance the daily mail telling me about my RAID problem has not been reaching me...)&lt;br /&gt;&lt;br /&gt;Anyway, lsb-core and mailx were screaming at me that their dependencies were missing, so re-installing exim was just a matter of telling them to re-install. Then the key step I had been missing was this:&lt;br /&gt;  sudo dpkg-reconfigure exim4-config&lt;br /&gt;&lt;br /&gt;Still not easy, but it becomes much clearer if you know that what exim calls a "smarthost" is what the rest of the world calls "my ISP"! I.e. in my case I told it to use a smarthost for outgoing email, no incoming email, and for the smarthost I just looked in my thunderbird configuration for the SMTP server and gave it that address. It also kept asking me for a default domain and I chose "dcook.org" for everything.&lt;br /&gt;&lt;br /&gt;Seems to have done the job!&lt;br /&gt;&lt;br /&gt;Oh, the raid problem? It is just complaining I didn't set up swap as RAID. This was deliberate, see &lt;a href="http://darrendev.blogspot.com/2009/04/software-raid-on-existing-linux-system.html"&gt;my original post on raid setup&lt;/a&gt;. At the time I wrote that I didn't understand the issues involved regarding RAID and swap. But if the system is complaining I will set it up as raid to make the message go away.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-429005694126271120?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/429005694126271120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=429005694126271120' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/429005694126271120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/429005694126271120'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/07/email-on-ubuntu-exim.html' title='Email on ubuntu (exim)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-701997146707413102</id><published>2009-07-14T18:37:00.000-07:00</published><updated>2009-07-14T19:11:17.468-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Alternative http logins from firefox?</title><content type='html'>Open source works on the "scratch-an-itch" principle, and so there should be solutions for the itches that developers have. So I must be using the wrong keywords as I just cannot track down the firefox plugin I need.&lt;br /&gt;&lt;br /&gt;When I build a web site I need to login as different users. E.g. a normal permissions users, and an admin-level user. I've input both in, firefox is storing both of them, but it only ever offers me one. Ironically way back when Firefox was called the Mozilla Application Suite it did have this - when two or more usernames had been saved it would pop-up a dialog letting you choose. Firefox 1.5 was a two steps forward, one step back kind of release in that respect. (Of course Firefox 2 and Firefox 3 were both "0.6 steps forward, 0.55 step backward" releases, with the kicker that you need to waste time finding replacement plugins, but that is a rant for another time.)&lt;br /&gt;&lt;br /&gt;Help! Please? There &lt;span style="font-style: italic;"&gt;must&lt;/span&gt; be some firefox plugins that does this simple task.&lt;br /&gt;&lt;br /&gt;P.S. I'm also keen to have some function that will allow me to selectively remove certain http authentications, but not the blanket "Miscellaneous|Clear Private Data|HTTP Authentication" option of the Web Developer plugin.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-701997146707413102?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/701997146707413102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=701997146707413102' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/701997146707413102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/701997146707413102'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/07/alternative-http-logins-from-firefox.html' title='Alternative http logins from firefox?'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-6678425417604598830</id><published>2009-06-04T00:03:00.000-07:00</published><updated>2009-06-04T00:06:36.607-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer go'/><category scheme='http://www.blogger.com/atom/ns#' term='artificial intelligence'/><title type='text'>The Shodan Go Bet</title><content type='html'>I am a natural optimist. A hopeless optimist. I spend hours battling against myself with carefully constructed cynicism, but things still get past my guard. And one day, back in 1997, I made the mistake of putting my money where my mouth is. Let me tell you were it all started...&lt;br /&gt;     &lt;br /&gt;&lt;a href="http://dcook.org/gobet/"&gt;http://dcook.org/gobet/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(About a bet I made with John Tromp, in 1997, that a computer could beat him at the game of go before the year 2011; the above URL is to publicize this bet and also has places where you can vote with your opinion and leave comments. If you enjoy it please do link to it, blog about it, tell your friends, etc.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6678425417604598830?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6678425417604598830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6678425417604598830' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6678425417604598830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6678425417604598830'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/06/shodan-go-bet.html' title='The Shodan Go Bet'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-1869019716530578099</id><published>2009-05-28T19:38:00.000-07:00</published><updated>2009-05-28T20:00:20.233-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Linux partition advice</title><content type='html'>When I first partioned my disk I put /boot on the first partition and gave it 96M,  following some advice found online I assume. This is not enough. I suggest giving it 256M. It is still a fraction of your hard disk. And because /boot is special it is impossible, or at least complex, to move some files to other partitions and link to them.&lt;br /&gt;&lt;br /&gt;About a week ago ubuntu upgrade started complaining about a kernel upgrade problem. I ignored it for a few days assuming it would sort itself out. But it kept happening. Then when I viewed details of the upgrade I noticed (just briefly before it vanished off-screen) it was saying not enough diskspace on /boot.&lt;br /&gt;&lt;br /&gt;Not again! When I tried to upgrade from Ubuntu 7 to Ubuntu 8 lack of space on /boot caused problems then too.&lt;br /&gt;&lt;br /&gt;Poking around I also found I still had linux-generic packages installed, even though I'd switched to the linux-server kernel (see &lt;a href="http://darrendev.blogspot.com/2009/03/6gb-on-32-bit-linux.html"&gt;6Gb on 32-bit linux&lt;/a&gt;). I deleted all packages that had the word "generic" in their name. After a reboot I still had one "generic" file left in /boot which I then just deleted. There was also a *.bak file for my current kernel. Datestamp was for a week ago, so I deleted that too.&lt;br /&gt;&lt;br /&gt;It doesn't look like I've broken anything, and I'm now down to using 40M on /boot with 48M free (and I still have Ubuntu 7's linux-server kernel in there, which I think is now pointless, so I could reduce it even more).&lt;br /&gt;&lt;br /&gt;Therefore, you &lt;span style="font-style:italic;"&gt;can&lt;/span&gt; get away with a mere 96M /boot partition, it just requires more time and effort.&lt;br /&gt;&lt;br /&gt;Conversely, I think a /boot partition above a certain size (1G?) causes problem at boot time, which is the whole reason for have a separate /boot partition. But I'm no expert, and that may be old-fashioned advice, and every BIOS on every motherboard made in the last 10 years may in fact be fine.&lt;br /&gt;&lt;br /&gt;I dunno, and am too busy with more interesting stuff to study up on it, which is why I'll go for a 256M partition on my next computer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1869019716530578099?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1869019716530578099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1869019716530578099' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1869019716530578099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1869019716530578099'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/05/linux-partition-advice.html' title='Linux partition advice'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-4991490348767555901</id><published>2009-05-25T19:20:00.000-07:00</published><updated>2009-05-25T19:42:30.052-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='Japanese'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Japanese NLP mailing list</title><content type='html'>A new mailing list for discussing Japanese NLP (natural language processing) in &lt;span style="font-style:italic;"&gt;English&lt;/span&gt; has been set up, by Jim Breen (of JMDict fame):&lt;br /&gt; &lt;a href="http://groups.google.com/group/nlp-japanese?hl=en"&gt;http://groups.google.com/group/nlp-japanese?hl=en&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There is a lot of software for processing Japanese text which is only documented in Japanese, and even then only minimally documented. So the new list is an ideal place (for those of us more comfortable in English than Japanese) for asking about how to use chasen, cabocha, namazu, etc. Or to describe what you are trying to do and get program and data suggestions. Hopefully people will also post about new software and data releases, related conferences, new academic papers, and so on.&lt;br /&gt;&lt;br /&gt;Also, if the above interests you then you will also want to know about this Ubuntu repository for all kinds of NLP software:&lt;br /&gt;  &lt;a href="http://cl.aist-nara.ac.jp/~eric-n/ubuntu-nlp/dists/hardy/japanese/"&gt;http://cl.aist-nara.ac.jp/~eric-n/ubuntu-nlp/dists/hardy/japanese/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Much of the Japanese stuff is UTF-8 ready (as opposed to the EUC-JP that academic Japanese software still likes to default to).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4991490348767555901?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4991490348767555901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4991490348767555901' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4991490348767555901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4991490348767555901'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/05/japanese-nlp-mailing-list.html' title='Japanese NLP mailing list'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-945950288276215793</id><published>2009-05-21T18:36:00.001-07:00</published><updated>2009-05-21T18:53:00.362-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Adobe PDF reader and Japanese fonts</title><content type='html'>Another casualty of my recent enforced ubuntu upgrade was Japanese fonts in pdf files. Adobe acroread has also moved from version 8 to version 9. When you meet Japanese in a PDF file it tells you the URL to go to to get asian font pack. Unfortunately that page only has asian fonts for acroread 8 and earlier! &lt;br /&gt;&lt;br /&gt;The link should be:&lt;br /&gt;&lt;a href="http://www.adobe.com/support/downloads/product.jsp?product=10&amp;platform=unix"&gt;http://www.adobe.com/support/downloads/product.jsp?product=10&amp;platform=unix&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(I'm mentioning this as it took a bit of work to discover it.)&lt;br /&gt;&lt;br /&gt;Scroll down to the add-ons section, and it seems each language is now its own file, and the files are much bigger. (I don't know the difference between a "Font Pack" and a "Font Packs"; the files I are identical so I chose the latter.)&lt;br /&gt;&lt;br /&gt;Unzip the bz2 file with "tar xjf FontPack910_jpn_i486-linux.tar.bz2"&lt;br /&gt;Then move into the JPNKIT directory and type "./INSTALL"&lt;br /&gt;&lt;br /&gt;The install process asks:&lt;br /&gt; "Enter the location where you installed the Adobe Reader? /opt"&lt;br /&gt;&lt;br /&gt;I didn't install it, Ubuntu did. However it seems Ubuntu is putting it in /opt!&lt;br /&gt;Strange for a package-based distro to put anything there, but I accepted the /opt default and it worked.  (This was different in Ubuntu 7, as I remember having to try lots of paths until I guessed the one it was after.)&lt;br /&gt;&lt;br /&gt;Incidentally I have already got medibuntu.org as an extra repository, but there is no acroread-fonts or similar package. Perhaps there is some legal issue (though I thought medibuntu.org's raison d'etre was packages with legal issues).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-945950288276215793?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/945950288276215793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=945950288276215793' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/945950288276215793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/945950288276215793'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/05/adobe-pdf-reader-and-japanese-fonts.html' title='Adobe PDF reader and Japanese fonts'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-7780557658018453701</id><published>2009-05-09T19:41:00.000-07:00</published><updated>2009-05-10T17:46:38.452-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Moving encrypted partition to software RAID</title><content type='html'>I &lt;a href="http://darrendev.blogspot.com/2009/04/software-raid-on-existing-linux-system.html"&gt;moved most of my partitions to software RAID&lt;/a&gt; a few weeks ago. But I left /home/darren/ because it was encrypted. However a few days ago I moved it too. Here is how.&lt;br /&gt;&lt;br /&gt;The quick overview: it is exactly like moving any other type of existing partition to software RAID, except where you would format /dev/md7, prior to copying the existing data over to it, you would set up crypt on /dev/md7 instead.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Detailed Steps&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;These instructions assume that you have moved other partitions to software RAID, or are at least familiar with the process (see &lt;a href="http://darrendev.blogspot.com/2009/04/software-raid-on-existing-linux-system.html"&gt;previous article&lt;/a&gt;). All commands are run as root.&lt;br /&gt;&lt;br /&gt;If you have not got an existing crypt partition you need to prepare for it:&lt;br /&gt; * Install cryptsetup&lt;br /&gt; * modprobe dm-crypt&lt;br /&gt; * Add dm-crypt to /etc/modules&lt;br /&gt;&lt;br /&gt;I will be setting up /dev/md7 for software RAID 1, but with just /dev/sdb7 in the raid array initially, i.e.&lt;br /&gt;  mdadm --create /dev/md7 --level=1 --raid-disks=2 missing /dev/sdb7&lt;br /&gt;&lt;br /&gt;Next I setup /dev/md7 for crypt with these commands:&lt;br /&gt;  cryptsetup luksFormat /dev/md7 -c aes -s 256 -h sha256&lt;br /&gt;  cryptsetup luksOpen /dev/md7 somecryptraid&lt;br /&gt;  mke2fs -j /dev/mapper/somecryptraid -L somecryptraid&lt;br /&gt;&lt;br /&gt;Each command only takes a few seconds to run. You will be prompted for a password. Forget that password and the data on your partition is lost forever; there is no recovery ability. So choose carefully.&lt;br /&gt;&lt;br /&gt;The next command sets chkdsk to run every 100 days, however many times you boot. This is just personal preference, and completely optional (the default is to run chkdsk more frequently):&lt;br /&gt;  tune2fs -c 0 -i 100 /dev/mapper/somecryptraid&lt;br /&gt;&lt;br /&gt;Run "mkdir /mnt/md7" then edit /etc/fstab and add this line:&lt;br /&gt; /dev/mapper/somecryptraid   /mnt/md7    ext3    defaults,noatime        0       0&lt;br /&gt;&lt;br /&gt;What we are doing here is saying we want our new encrypted software raid partition to be mounted somewhere temporary, so we can copy our existing data over to it.&lt;br /&gt;(the "noatime" flag is optional, and nothing to do with software RAID or crypt)&lt;br /&gt;&lt;br /&gt;And edit /etc/crypttab to add this line:&lt;br /&gt;    somecryptraid  /dev/md7       none    luks&lt;br /&gt;&lt;br /&gt;This is the command that will cause it to prompt for password on boot time. If you already have an encrypted partition then you are adding the above in addition to your existing entry: you will have two encrypted partitions for the next boot.&lt;br /&gt;&lt;br /&gt;Now reboot into single-user mode (the recovery kernel). You should be prompted for the password for your new crypt partition (in addition to any existing crypt partition). Now I move /home/darren to the new crypt partition with:&lt;br /&gt; cd /home/darren/&lt;br /&gt; cd -dpRx . /mnt/md7&lt;br /&gt;&lt;br /&gt;This took over an hour to run for me.&lt;br /&gt;&lt;br /&gt;Once it finishes edit /etc/fstab to change the /dev/mapper/somecryptraid entry from /mnt/md7 to be /home/darren. And comment out the previous entry for /home/darren. I.e. the new entry looks like:&lt;br /&gt; /dev/mapper/somecryptraid  /home/darren    ext3    defaults,noatime   0   0&lt;br /&gt;&lt;br /&gt;Reboot. When running df you should see a line like:&lt;br /&gt;  /dev/mapper/somecryptraid  ...  ...   ...  ... /home/darren&lt;br /&gt;&lt;br /&gt;You can now remove (or comment out) the old "somecrypt" entry from crypttab. Also use fdisk to change the /dev/sda7 entry from "83 Linux" to "fd Linux RAID autodetect" (use the fdisk "t" command to do this). Reboot again.&lt;br /&gt;&lt;br /&gt;Now you should be able to run:&lt;br /&gt; mdadm --add /dev/md7 /dev/sda7&lt;br /&gt;&lt;br /&gt;This will take a while to run, as it mirrors the data from /dev/sdb7 to /dev/sda7. "watch cat /proc/mdstat" will show its progress.&lt;br /&gt;&lt;br /&gt;"rmdir /mnt/md7" as a tidyup step at the end. You might also want to delete commented out lines in /etc/cryptab and /etc/fstab if you like to keep those files lean and clean.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Troubleshooting&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When I ran "mdadm --add /dev/md7 /dev/sda7" I got this error:&lt;br /&gt; "mdadm: add new device failed for /dev/sda7 as 2: No space left on device"&lt;br /&gt;&lt;br /&gt;I tracked this down to slightly different sizes as reported by fdisk:&lt;br /&gt;  /dev/sda7           16988       23361    51199092   fd  Linux RAID autodetect&lt;br /&gt;  /dev/sdb7           16988       23361    51199123+  fd  Linux RAID autodetect&lt;br /&gt;&lt;br /&gt;Even though start and end cylinders are the same, the number of sectors is different! When I change the view ("u" command in fdisk), to show start and end sector, the problem is clearer:&lt;br /&gt;/dev/sda7       272896281   375294464    51199092   fd  Linux RAID autodetect&lt;br /&gt;/dev/sdb7       272896218   375294464    51199123+  fd  Linux RAID autodetect&lt;br /&gt;&lt;br /&gt;In other words /dev/sda7 starts a few sectors later. My fix was to delete /dev/sda7 from the partition table, and then recreate it. Then /dev/sda7 showed the same start/end sector as /dev/sdb7. Weird and spooky, but now the "mdadm --add" command worked (after another reboot of course).&lt;br /&gt;&lt;br /&gt;Tip: use fdisk to check your partition sizes are exactly the same before starting! The other way to fix this would have been to make /dev/sdb7 smaller but, by the time I realized, it was too late to do that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7780557658018453701?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7780557658018453701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7780557658018453701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7780557658018453701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7780557658018453701'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/05/moving-encrypted-partition-to-software.html' title='Moving encrypted partition to software RAID'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6315161683358180495</id><published>2009-04-29T22:50:00.000-07:00</published><updated>2009-04-29T23:39:22.597-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Ubuntu Hardy: Hard(y) Work</title><content type='html'>After last week's upgrade on my notebook, I did the forced  upgrade from Ubuntu 7 to Ubuntu 8.10 on my main machine yesterday. Numerous breakages.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Easy Fixes&lt;/span&gt;&lt;br /&gt; * Skype disappeared. I still had the deb package and installing it again cured that. All my settings were still around.&lt;br /&gt;&lt;br /&gt; * SciTE: It overwrote my old settings. Luckily I keep a backup copy and merged in my differences.&lt;br /&gt;&lt;br /&gt; * Evolution was back. So I deleted again all packages it would let me delete (some parts are entwined around the heart of either Ubuntu or Gnome).&lt;br /&gt;&lt;br /&gt; * The Permit Cookies firefox plugin didn't like firefox 3. You have to go to developer's own site to get a version that will install.&lt;br /&gt;&lt;br /&gt; * SCIM (Japanese input) hot key had stopped working. This fixed itself when I fixed something else (my guess is the xorg.conf change for the Alt-F1/F7 problem - see below).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hard Fixes&lt;/span&gt;&lt;br /&gt; * pcmanfm. This is better than nautilus. Full stop. But after the hardy upgrade trying to launch anything fails - it tries to launch, but after 20s or so gives up. This even includes opening the terminal. I tried reinstalling, and tried getting the latest version, but same problems.&lt;br /&gt;  Solution: &lt;span style="font-style:italic;"&gt;I gave up&lt;/span&gt;. &lt;a href="http://darrendev.blogspot.com/2009/01/gnome-file-extensions-are-screwing-up.html"&gt;My nautilus problems&lt;/a&gt; seem to have gone, and I installed nautilus-terminal package which gives me the "open a terminal in this directory" feature that I'd become addicted to. (Cannot get F4 to open terminal though: the closest I can manage is "Alt-F, t")&lt;br /&gt;&lt;br /&gt; * Ctrl-Alt-F1/F7 no longer worked. See my &lt;a href="http://darrendev.blogspot.com/2009/04/screen-keeps-going-blank.html"&gt;screen blanking blog entry&lt;/a&gt; for the xorg.conf fix for this.&lt;br /&gt;&lt;br /&gt; * Real Player Plugin. Firefox 3 decided to move the goal posts and keep plugins in a new directory; so you have to move the firefox plugins to the correct place (/usr/lib/firefox-addons/plugins/) yourself.&lt;br /&gt;  That still wasn't enough. I've been disabling various other plugins, reinstalling real player, and lots of swearing. I finally have it working, but I cannot tell you exactly which steps were needed.&lt;br /&gt;  In the end I used the Real Player's deb, which is highly suspicious as it has dependencies such as "exim" (an email client)?!! Many years ago Real got a bad reputation for malware, and I'm wondering if they are still up to their bad tricks?&lt;br /&gt;  But even just that deb didn't work. So, I think that the thing that finally worked was deleting the totem-mozilla package. (I'd already disabled all plugins with the word totem in them however, which was no help, so I'm not entirely sure... the real Real Player plugin calls itself "Helix DNA Plugin", and I think I'd also disabled that... which with hindsight was probably the cause of all my problems?)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Other Fixes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A few months back the red icon to shutdown gnome stopped working. Hardy didn't fix that. But I did find the cause today: Preference | Sessions | Enable Power Manager. A tad confusing - you'd think it was something only needed on notebooks. But, anyway, that was the key.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Things That Did Work Okay:&lt;/span&gt;&lt;br /&gt; * Samba (aka windows) shares&lt;br /&gt; * Software RAID&lt;br /&gt; * Encrypted Partitions&lt;br /&gt; * Email/Browser/sound/video card/etc.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Thankfully Ubuntu 8 is a LTS (Long Term Support) release, so I shouldn't need to go through this for another couple of years.&lt;br /&gt;And while I now &lt;span style="font-style:italic;"&gt;hate&lt;/span&gt; Ubuntu I have been reminded about its most important feature: a huge user base. Which means typing "Ubuntu 8 some problem" into Google will &lt;span style="font-style:italic;"&gt;always&lt;/span&gt; find someone having the same problem, usually with some kind of solution.&lt;br /&gt;&lt;br /&gt;Thanks to all the hundreds of Ubuntu users who answer questions, and blog about their fixes!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6315161683358180495?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6315161683358180495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6315161683358180495' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6315161683358180495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6315161683358180495'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/04/ubuntu-hardy-hardy-work.html' title='Ubuntu Hardy: Hard(y) Work'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7661668088631391100</id><published>2009-04-24T02:53:00.000-07:00</published><updated>2009-04-25T16:51:25.640-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Ubuntu Expired On Me!</title><content type='html'>So, I boot up my notebook into linux (Ubuntu 7) for first time in a while, maybe 2-3 months, to prepare it for a meeting. I'm a good boy and first go to install the updates I know will be there. It says it cannot find some. Strange.&lt;br /&gt;&lt;br /&gt;On a hunch I wonder if Ubuntu 7 has reached end of life. But, no, this page tells me Gutsy Gibbon (7.10) is still current:&lt;br /&gt; https://help.ubuntu.com/community/UpgradeNotes&lt;br /&gt;&lt;br /&gt;It is wrong. Ubuntu obviously haven't read their own press releases. It EOL-ed a week ago:&lt;br /&gt; http://www.ubuntu.com/news/ubuntu-7.10-eol&lt;br /&gt;&lt;br /&gt;What is annoying is how EOL breaks everything. Packages won't validate. Worse, I cannot install some new software (such as mysql; but I could install lame). This is not simply "we're not going to do updates any more". This is "We've pulled the plug. Should've upgraded earlier. Loser."&lt;br /&gt;&lt;br /&gt;Two forehead slaps for Ubuntu in the space of a week. I'll be back to Fedora at this rate!&lt;br /&gt;&lt;br /&gt;Anyway, I started the upgrade to 8.04, but after running for 20 minutes it finally told me it was going to take 19hrs to run. Not good timing for my meeting!&lt;br /&gt;&lt;br /&gt;By the way, 19hrs was a bad estimate. It takes 10-20 minutes to get it started, then 3-4 hours downloading, then 1+hrs installing. It took 6 hrs in total for me. You can leave it alone for the downloading, but need to be around to answer questions during the install stage.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7661668088631391100?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7661668088631391100/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7661668088631391100' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7661668088631391100'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7661668088631391100'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/04/ubuntu-expired-on-me.html' title='Ubuntu Expired On Me!'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-4113508675924662897</id><published>2009-04-22T04:25:00.000-07:00</published><updated>2009-05-05T20:37:16.942-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Screen keeps going blank</title><content type='html'>I mentioned this in previous entry (on software RAID). My screen keeps going blank. Ctrl-Alt-F1, breathe in, breathe out, Ctrl-Alt-F7 fixes it. I.e. that must reset the video card or X.&lt;br /&gt;&lt;br /&gt;It used to happen once a day or less. Since switching to RAID it is happening once an hour roughly. But 9 times in past hour, which was past my threshold, so I went hunting...&lt;br /&gt;&lt;br /&gt;...and came up empty-handed. Xorg.0.log has entries but they match what happens if I do the Ctrl-Alt-F1/F7 sequence. /var/log/acpid also has an entry that I think goes along with Ctrl-Alt-F7. But nothing else. No errors in either. Nothing in messages, syslog et al that could be associated.&lt;br /&gt;&lt;br /&gt;/var/log/gdm/:0.log also gets an entry when I do ctrl-alt-F7, but nothing beyond that.&lt;br /&gt;&lt;br /&gt;Nothing else in /var/log at all.&lt;br /&gt;&lt;br /&gt;I'm stumped. I doubt over-heating, and I doubt CPU load (as all I've been doing the past hour is typing in a text file). w tells me "0.00 0.09 0.07".&lt;br /&gt;&lt;br /&gt;Not a very informative blog, sorry. Anyone got any suggestions? I don't want to mess around trying different video cards, monitors, etc.; it is still just a minor irritation.&lt;br /&gt;&lt;br /&gt;UPDATE: After updating to Ubuntu 8 I still had this problem, but worse my Ctrl-Alt-F1/F7 fix no longer worked! A bit of googling tracked down a solution:&lt;br /&gt;In /etc/X11/xorg.conf, in "InputDevice" section, I commented out these three lines:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  #Option  "XkbLayout" "jp,jp"&lt;br /&gt;  #Option  "XkbVariant" "latin,"&lt;br /&gt;  #Option  "XkbOptions" "grp:alt_shift_toggle,grp_led:scroll"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and replaced with just:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   Option  "XkbLayout" "jp"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Even better, Ctrl-Alt-F1..F6 now give me a terminal! They never have in Ubuntu, instead they just gave me a blank screen. The above xorg.conf has been there since the start - Ubuntu 8 upgrade didn't alter it.&lt;br /&gt;(Oh, and the above change has not fixed the screen going blank problem, so I'm still no closer to fixing that...)&lt;br /&gt;&lt;br /&gt;Update: I've noticed if I move the mouse, especially the mouse wheel, just after the screen goes blank, it comes back. It doesn't always work - not sure if there are two problems here, or if I'm too slow reaching for the mouse sometimes.&lt;br /&gt;Anyway, if that suggests a potential cause for someone let me know!&lt;br /&gt;(By the way, I have screensaver set to blank, after 30 minutes of inactivity. Just to see if that is related I've changed screensaver to some animation, and I'll let you know!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4113508675924662897?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4113508675924662897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4113508675924662897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4113508675924662897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4113508675924662897'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/04/screen-keeps-going-blank.html' title='Screen keeps going blank'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-1909482404554145535</id><published>2009-04-20T02:31:00.000-07:00</published><updated>2009-05-10T17:49:51.584-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Software RAID on an existing linux system</title><content type='html'>So, as mentioned in a &lt;a href="http://darrendev.blogspot.com/2009/03/6gb-on-32-bit-linux.html"&gt;previous article&lt;/a&gt; I bought a terabyte hard disk, mainly to impress the ladies. It doesn't seem to have been working too well in that respect. I considered sending it back, claiming it needs more testerone. But I decided to instead set up software RAID 1.&lt;br /&gt;&lt;br /&gt;My main guide for this task was this article: &lt;a href="http://www.howtoforge.com/software-raid1-grub-boot-debian-etch"&gt;http://www.howtoforge.com/software-raid1-grub-boot-debian-etch&lt;/a&gt;&lt;br /&gt;Rather than repeat everything I will just comment on what I did differently. Note that despite the name of that article, it applied fine to Ubuntu 7, and I suspect it will apply equally well to any linux distro of the past two or three years.&lt;br /&gt;&lt;br /&gt;First, my disks were different size: 320G vs. 1T. So I opened fdisk -l /dev/sda in one terminal, then ran fdisk /dev/sdb in a second terminal and set up the first six partitions (plus one extended partition) to be the same as /dev/sda. I then set up /dev/sdb8 to be one big partition with the rest of the disk, which I mounted as /backup.&lt;br /&gt;&lt;br /&gt;In all software RAID guides they number the partitions /dev/md0, /dev/md1, etc. That is confusing so I decided to keep the same numbering schemes as my hard disks; it turns out this is fine. So I used commands like this:&lt;br /&gt;  mdadm --create /dev/md3 --level=1 --raid-disks=2 missing /dev/sdb3&lt;br /&gt;&lt;br /&gt;compared to this from the HowToForge article:&lt;br /&gt;  mdadm --create /dev/md2 --level=1 --raid-disks=2 missing /dev/sdb3&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The above article did all partitions in one go. I wanted to take baby steps. So I started by mirroring one partition, choosing a non-critical one, and ignoring all that stuff about grub configuration. It worked well. I then did /var and /, leaving /boot unmirrored and therefore again ignoring all that grub configuration stuff. /var went smoothly but / did not. It kept saying /dev/sda3 was in use: "mdadm: Cannot open /dev/sda3: Device or resource busy" But df didn't list it. Trawling /var/log/* didn't list it. "Eh?" thought I.&lt;br /&gt;&lt;br /&gt;Eventually I realized that grub was set to boot from /dev/sda3, not from /. I.e. grub works at the device level not the partition level.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Key Advice:&lt;/span&gt; mirroring one partition at a time is fine, but then do "/", "/boot" and all that grub stuff in one go.&lt;br /&gt;&lt;br /&gt;Now, something the HowToForge article didn't mention at all was doing file copies in single-user mode. I know just enough unix administration to scare myself and the idea of taking a copy of /var and / while all the daemons were still running made me nervous. So I tried to boot to single-user mode.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;font-weight:bold;"&gt;Discovery #1:&lt;/span&gt; Ubuntu is weird. They don't use run levels like the rest of the linux world. Sure, their numbering scheme may make more sense, but it also causes much surprise. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;font-weight:bold;"&gt;Discovery #2:&lt;/span&gt; "telinit 1" doesn't work. The GUI shuts down and then nothing. I can see the system is still running but nothing on screen. Nothing on Ctrl-Alt-F1 through to Ctrl-Alt-F12. I think ctrl-alt-del caused a reboot.&lt;br /&gt;&lt;br /&gt;So, I booted, and choose the "recovery kernel" from the grub menu. Recovery being another name for single-user mode. It asks for the root password. Ah. Ubuntu doesn't use one. I have to continue into a normal GUI boot.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Key Advice:&lt;/span&gt; Set a root password. This has nothing to do with software RAID: you never know when you are going to want to boot into a recovery kernel. I think Ubuntu deserves a forehead slap for this design decision? To set a root password, do "sudo passwd": it will first ask for your user password (that is how sudo works), then it will say "Enter new UNIX password:". I chose the same password as my normal user; easy to remember. (If that makes you nervous, remember that this is equal security to being able to run "sudo passwd"; more importantly, it saves me having to write down my root password somewhere.)&lt;br /&gt;&lt;br /&gt;Other comments: the "cp -dpRx" command is the most time-consuming step for those partitions when you have a lot of data. The system will be sluggish while doing this (and if you are in single-user mode you cannot be browsing or doing anything else anyway). But also when you do the  "mdadm --add /dev/md3 /dev/sda3" to actually create a genuine RAID partition it will be copying a lot of data, and your system will be sluggish for the time this takes (about 10 minutes per 30G). Bear this in mind.&lt;br /&gt;&lt;br /&gt;Swap. I've created /dev/md2 as a swap RAID, but haven't used it. So /dev/md2 just contains /dev/sdb2. And "cat /proc/swaps" tells me only /dev/sda2. Apparently if my sda disk dies my swap will disappear and my system might crash. On the other hand, mirroring swap has a performance downside (apparently). I don't understand the trade-offs well, but this is a workstation, not a server and a crash should /dev/sda ever die is acceptable. I've also got more memory than I need so I suspect I could switch swap off completely. Anyway, so far Inertia born from Igorance means I'm doing nothing about this. Expert opinions are welcome. &lt;br /&gt;&lt;br /&gt;Everything works. Just two things I've noticed. Sometimes the system doesn't boot. It comes up in an "ash" shell, which is one of those recursive acronyms standing for "Ash is Satanic Hell". So far the only command I've mastered is "reboot", which works well - I've not had two boot failures in a row. After 10-20 reboots I think this boot failure is happening about 20-25% of the time. I've no idea how to troubleshoot it.&lt;br /&gt;UPDATE: this boot problem doesn't seem to be happening since upgrading to Ubuntu 8. Perhaps the upgrade fixed some mistake in my grub.conf? Maybe related is that I no longer have an (hd1,0) section in grub.conf; they are all (hd0,0). I think I'll leave it like that:  if "hd0" dies I can edit the grub boot string that once, and then edit grub.conf.&lt;br /&gt;&lt;br /&gt;The other thing is my screen sometimes goes blank. The system is fine, it is just like the video card has died. My solution has always been: Ctrl-Alt-F1, pause for half a second, then Ctrl-Alt-F7. I guess this process resets the video card. It used to happen maybe once a day or so. Now it seems to have happening once an hour on average, and is becoming almost irritating enough that I'll have to look into it. The fact that running RAID could affect the display feels like an important clue.&lt;br /&gt;&lt;br /&gt;Finally, my home partition is encrypted, using crypt. I have not moved it to software RAID yet, for that reason. I will really soon (as all my data that could really benefit from the security of software RAID is currently the only data not protected by it)! I will run crypt on top of software RAID, rather than the other way around; I'm just not sure the best way to do the file copy.&lt;br /&gt;UPDATE: now done, it was straightforward, see &lt;a href="http://darrendev.blogspot.com/2009/05/moving-encrypted-partition-to-software.html"&gt;Moving encrypted partition to software RAID&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-1909482404554145535?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/1909482404554145535/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=1909482404554145535' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1909482404554145535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/1909482404554145535'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/04/software-raid-on-existing-linux-system.html' title='Software RAID on an existing linux system'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-7367369236604427183</id><published>2009-04-14T23:47:00.000-07:00</published><updated>2009-04-15T00:16:18.836-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><category scheme='http://www.blogger.com/atom/ns#' term='data presentation'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Excel and PDF from PHP</title><content type='html'>Been meaning to post this for ages, but PHP Architect magazine for &lt;a href="http://www.phparch.com/c/magazine/issue/86"&gt;November 2008&lt;/a&gt; had a useful article on libraries and code for alternatives to HTML.&lt;br /&gt;&lt;br /&gt;For PDF &lt;a href="http://code.google.com/p/html2pdf/"&gt;html2pdf&lt;/a&gt; was introduced. Unlike other PHP PDF libraries (e.g. PDFLib, FPDF) this one has you make your content in HTML then it gets converted. For those of us comfortable with HTML, that means no learning curve.&lt;br /&gt;&lt;br /&gt;For Excel, &lt;a href="http://www.phpexcel.net/"&gt;PHPExcel&lt;/a&gt; is introduced. This has classes to read and write excel files in a variety of formats. I get this request a lot but so far we have always decided outputting good old CSV is best (simple to make, compact, easy to import into Excel). But if complex layout, or built-in functions, are required this looks worth a look. It can also output into PDF.&lt;br /&gt;&lt;br /&gt;Also very interesting is in-memory spreadsheets, with lots of &lt;a href="http://progress-lab.com/jobtrack/phpexcel/Documentation/API//PHPExcel_Calculation/PHPExcel_Calculation_Functions.html"&gt;mathematical functions&lt;/a&gt; supported. I've not tried it, and neither the article or the website mention explicitly if this will work on Linux (or a Windows machine without Excel installed), but it is described as &lt;span style="font-style:italic;"&gt;standalone&lt;/span&gt; so it should. It might be an interesting way to port someones spreadsheet to a PHP app!&lt;br /&gt;&lt;br /&gt;The article also briefly introduces Google charts, and how to include the produced image in your excel file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7367369236604427183?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7367369236604427183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7367369236604427183' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7367369236604427183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7367369236604427183'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/04/excel-and-pdf-from-php.html' title='Excel and PDF from PHP'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-3107429575091340961</id><published>2009-03-30T20:03:00.000-07:00</published><updated>2009-03-30T20:47:30.158-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>6GB on 32-bit linux</title><content type='html'>Well, it all started when a friend said their SMT (statistical machine translation) system was ready to download and install. He then casually mentioned it is a bit of a memory hog, "4Gb minimum, 8Gb preferred".&lt;br /&gt;&lt;br /&gt;Wow. I looked at my up-until-then-perfectly-adequate-some-might-even-say-overkill 2 gigabytes and felt like a salesman who had just been told he ought to upgrade his sensible car for a sports car in order to look more successful!&lt;br /&gt;&lt;br /&gt;I have dual-channel, and two slots free. Another 4Gb was under 5000 yen at &lt;a href="http://www.dospara.co.jp/top/"&gt;Dospara&lt;/a&gt; (Japanese only), where I had bought this computer, so I emailed them to confirm it would work. After a few emails, and a bit of research I found out:&lt;br /&gt; * Windows 32-bit will only go up to 3Gb.&lt;br /&gt; * 64-bit Windows, 64-bit Linux are both fine well beyond 8Gb.&lt;br /&gt; * 32-bit linux will allow up to 4Gb per process, and can use more than 4Gb altogether.&lt;br /&gt;&lt;br /&gt;(I wanted to stick with 32-bit linux, as Flash is critical to my work and has no 64-bit version.)&lt;br /&gt;&lt;br /&gt;Dospara were rather cautious, saying they don't support linux, but I went for it. When I plugged in the extra 4Gb, the bios correctly recognized 6Gb. Then Ubuntu said I had 3Gb. But that was okay, as I'd been expecting it. I went to the package manager and selected the "linux-server" meta-package, then rebooted.&lt;br /&gt;&lt;br /&gt;Drum roll please: "free" reports I have 6Gb available. I'm using 475 Mb, and have 5,745 Mb free. See, I told you I didn't need it. But this is city driving. You wait until I take this beast down the local Formula One track, otherwise known as Difficult AI Problems.&lt;br /&gt;&lt;br /&gt;Oh, while I was there I also bought a 1Tb SATA drive. Yes, that is a "T". A whole &lt;span style="font-style:italic;"&gt;terabyte&lt;/span&gt; in a little, diddy box. It was only 10,000 yen (you could get one for 7,880 yen if you go for Seagate, but a quick bit of research showed lots of unhappy people, so I went for Western Digital which seemed to be the reliability brand).&lt;br /&gt;&lt;br /&gt;What do you mean: "I bet he doesn't need that much storage either" ? Just because my current 250Gb drive still has 143G of free space after 18 months, doesn't mean I suddenly won't need more capacity...&lt;br /&gt;&lt;br /&gt;And you can bet that when the ladies hear I am part of the Terabyte Generation I'm going to be fighting them off with a stick. Oooh, yeah! I am &lt;span style="font-style:italic;"&gt;so&lt;/span&gt; sexy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3107429575091340961?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3107429575091340961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3107429575091340961' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3107429575091340961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3107429575091340961'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/03/6gb-on-32-bit-linux.html' title='6GB on 32-bit linux'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-6808942146728525078</id><published>2009-03-16T17:53:00.000-07:00</published><updated>2009-03-16T18:44:00.227-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='regexes'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Periods in regex character classes</title><content type='html'>I'm trying to match Japanese numbers with a regex, and this was originally a long article describing what seemed to be a bug when using the following regex:&lt;br /&gt; $regex='/((0|1|2|3|4|5|6|7|8|9|.)+)(万|億|兆)/u';&lt;br /&gt;&lt;br /&gt;(a simplified version of my actual code, but this demonstrated the problem equally well)&lt;br /&gt;&lt;br /&gt;Now, periods in character classes (a list of alternatives in square brackets) are not special, so don't need escaping. But they do need escaping anywhere else. I knew this, which is why I was scratching my head.&lt;br /&gt;&lt;br /&gt;So, naturally, I felt a bit foolish when I realized I was using a list of alternatives in parentheses. It wasn't a character class at all.&lt;br /&gt;&lt;br /&gt;But, now, this was even more confusing because I had already been down that street. My code (which in hindsight was correct) had started out like this:&lt;br /&gt; $regex='/((0|1|2|3|4|5|6|7|8|9|\.)+)(万|億|兆)/u';&lt;br /&gt;&lt;br /&gt;and that kept saying no match. E.g. for "730.28".&lt;br /&gt;&lt;br /&gt;Did I jump out of my bath and shout "Eureka!" when I realized the problem? No. It was more of a forehead slapper. It wasn't "730.28万". It was just "730.28". The code works by trying to match the above regex, then if it fails passes it to another function. I'd been debugging the wrong function. Once I was looking at the right function it was a trivial fix.&lt;br /&gt;&lt;br /&gt;Moral of the story: don't be stupid.&lt;br /&gt;&lt;br /&gt;(Or more seriously, "Time To Refactor", as the code is now tangled enough that it was too easy for me to get entangled in the wrong place.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6808942146728525078?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6808942146728525078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6808942146728525078' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6808942146728525078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6808942146728525078'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/03/periods-in-regex-character-classes.html' title='Periods in regex character classes'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-3392104376559345368</id><published>2009-03-08T19:20:00.000-07:00</published><updated>2009-03-08T19:52:04.754-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><title type='text'>What is an ontology?</title><content type='html'>I went to &lt;a href="http://abelard.flet.keio.ac.jp/ontology/index.php?InterOntology09"&gt;InterOntology 09&lt;/a&gt;, at Keio University a week ago. Actually I only attended the first couple of talks and the (free!) banquet; I couldn't make the other talks. Ontology is one of those words I have had trouble grasping, and I attended with no more ambition that understanding what it means.&lt;br /&gt;&lt;br /&gt;My ambition was not fulfilled, but I was relieved to know that most attendees were just as confused as me. Okay, relieved is perhaps the wrong word. And by "confused", I don't mean people stood around scratching their heads. I mean people were using it in different ways. Most people who said they were "building ontologies" were actually building databases.&lt;br /&gt;&lt;br /&gt;Someone I met there kindly sent me this link:&lt;br /&gt; &lt;a href="http://www.metamodel.com/article.php?story=20030115211223271"&gt;What are the differences between a vocabulary, a taxonomy, a thesaurus, an ontology, and a meta-model?&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This (in conjunction with its first comment) is an excellent article. A bit heavy, but definitely worth the effort.&lt;br /&gt;&lt;br /&gt;I feel it backs up the opinion I've been forming that ontology is a word that does not really need to exist. People "building ontologies" are generally data modeling, or taxonomy-building, or semantic-network-building. These terms are more specific, and contain more information about exactly what you are doing.&lt;br /&gt;&lt;br /&gt;People using the word ontology may want to emphasize that the grammar allows for validating models using logic. Personally I would rather call that validating the data model, though I do see how a widely used ontology representation language could encourage high quality data validation tools. But they are nowhere near that yet - using SPARQL (pronounced sparkle) appears to be more like writing in assembler than the SQL its name hints at.&lt;br /&gt;&lt;br /&gt;On the other hand, if everyone listened to me there would have been no InterOntology conference, and I'd have missed out on a free dinner. Such things need to be taken into consideration. Perhaps I should add "Professional Ontologist" to my business card after all? ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-3392104376559345368?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/3392104376559345368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=3392104376559345368' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3392104376559345368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/3392104376559345368'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/03/what-is-ontology.html' title='What is an ontology?'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-2248139573977457097</id><published>2009-03-05T17:16:00.000-08:00</published><updated>2009-03-05T17:41:46.802-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Comparing two arrays in PHP when elements can be in any order</title><content type='html'>This comes up so often. Usually I want to compare two CSV strings, where order does not matter.&lt;br /&gt;&lt;br /&gt;My usual strategy is to first explode(',',$s) both strings. Then loop through one array, seeing if that element exists in the second array. If it does not then we instantly fail. If it does match then remove that element from the second array. At the end, if no failures and if the second array is empty, then they are the same.&lt;br /&gt;&lt;br /&gt;When writing the above code for the umpteenth I wondered for the umpteenth time if there was a better way. I checked the PHP manual and still no "compare_arrays_can_be_in_any_order()" function. (Or, am I missing it - please let me know!)&lt;br /&gt;&lt;br /&gt;But maybe I can use a couple of functions?&lt;br /&gt;&lt;br /&gt;First idea: array_intersect(). Returns all elements of array1 that are in array2. So:&lt;br /&gt;&lt;br /&gt;  $intersect1=array_intersect($list1,$list2);&lt;br /&gt;  if($intersect1!==$list1)return false; //list2 is a subset of list1.&lt;br /&gt;  $intersect2=array_intersect($list2,$list1);&lt;br /&gt;  if($intersect2!==$list2)return false; //list1 is a subset of list2.&lt;br /&gt;  return true;  //They are supersets of each other. I.e. equal.&lt;br /&gt;&lt;br /&gt;I've not tried this, as my second idea was better: sort(). The implementation is:&lt;br /&gt;&lt;br /&gt;function compare_arrays_can_be_in_any_order($list1,$list2){&lt;br /&gt;   sort($list1);&lt;br /&gt;   sort($list2);&lt;br /&gt;   return ($list1===$list2);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;Note: this is destructive, as both list1 and list2 are modified. Written as above this is fine as I pass the arrays in by value. But be careful if pasting just that code into another function.&lt;br /&gt;&lt;br /&gt;Here is the function to solve my original csv matching problem:&lt;br /&gt;&lt;br /&gt;function compare_csv_strings_can_be_in_any_order($csv1,$csv2){&lt;br /&gt;   $list1=explode(',',$csv1);&lt;br /&gt;   $list2=explode(',',$csv2);&lt;br /&gt;   sort($list1);&lt;br /&gt;   sort($list2);&lt;br /&gt;   return ($list1===$list2);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;Bear in mind this will only work for simple csv strings: if one uses quotes and the other doesn't, or one uses whitespace around the comma and the other doesn't, you need something more sophisticated.&lt;br /&gt;&lt;br /&gt;Now your homework question to make sure you are paying attention: sort() takes an optional parameter of flags, to specify sorting items numerically, as strings, or as strings using current locale. Do I need to specify any flags to have my code work with any type of array data, and to work on a machine using a different locale?&lt;br /&gt;&lt;br /&gt;What about comparing arrays of arrays?&lt;br /&gt;&lt;br /&gt;And final question, the one of most interest to me: do you have a better way? (Better could mean any of: less code, quicker execution or fewer restrictions/disclaimers!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-2248139573977457097?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/2248139573977457097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=2248139573977457097' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2248139573977457097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/2248139573977457097'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/03/comparing-two-arrays-in-php-when.html' title='Comparing two arrays in PHP when elements can be in any order'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-783031090100817185</id><published>2009-03-03T17:46:00.000-08:00</published><updated>2009-03-03T18:07:44.752-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Specify the default, stupid!</title><content type='html'>I've been struggling all week to connect to a Microsoft SQL server. It was SQL Express. I'm using "ADODB.Connection" COM object from PHP.&lt;br /&gt;&lt;br /&gt;(The reason to use that is you can specify the 3rd param as CP_UTF8 (without quotes) and then it will convert from UTF-8 to the UCS-2 that SQL Server works in, all behind the scenes.)&lt;br /&gt;&lt;br /&gt;First, if it says it cannot connect due to there being no server: you have to enable TCP/IP connections in SQL Server. They are off by default.&lt;br /&gt;&lt;br /&gt;That got us to the next error, which said "接続が正しくありません。" which translates as "The connection is incorrect." Not much to go on. I think this may be the same as "error 26", but I'm not 100% sure on that.&lt;br /&gt;&lt;br /&gt;My code could connect fine to the production DB machine, running SQL workstation (which is the more expensive version I believe). And a client on another machine that could also connect to that production DB, could not connect to our SQL Express machine.&lt;br /&gt;&lt;br /&gt;So, with all fingers pointing at the SQL Express server, we tried things and googled things and swore at things. Wrong barking tree!&lt;br /&gt;&lt;br /&gt;I've given the answer in the subject. SQL Server listens on port 1433 by default. Our SQL server was listening on that port, so you would think no need to specify it. But this DSN fails:&lt;br /&gt;&lt;br /&gt;DRIVER={SQL Server};&lt;br /&gt;SERVER={127.0.0.1};UID={myuser};PWD={mypassword}; DATABASE={mydatabase}&lt;br /&gt;&lt;br /&gt;whereas this one works:&lt;br /&gt;DRIVER={SQL Server};&lt;br /&gt;SERVER={127.0.0.1,1433};UID={myuser};PWD={mypassword}; DATABASE={mydatabase}&lt;br /&gt;&lt;br /&gt;Only for SQL Express it seems. No need to specify the default port when connecting to SQL workstation.&lt;br /&gt;&lt;br /&gt;Nice one Microsoft. Just got to work on quality control and documentation a bit more, oh, and consistency, and then they could probably go professional with this software business of theirs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-783031090100817185?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/783031090100817185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=783031090100817185' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/783031090100817185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/783031090100817185'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/03/specify-default-stupid.html' title='Specify the default, stupid!'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7800490556231188013</id><published>2009-02-15T17:34:00.000-08:00</published><updated>2009-03-16T18:50:48.177-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='documentation'/><category scheme='http://www.blogger.com/atom/ns#' term='contracts'/><category scheme='http://www.blogger.com/atom/ns#' term='php|a'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>PHP comments from PHP!</title><content type='html'>php|architect December 2008 edition's most interesting article was on phpdoc. I'm a fanatic when it comes to the javadoc style of commenting. When I create a new function the first thing I do is write function somename(){}, then the second thing I do is write /** */ above it.&lt;br /&gt;&lt;br /&gt;If I'm refactoring then I might copy and paste in some code straightaway, but usually the third thing I do is document. I describe what the function does, er, will do, and usually define the @return tag (if I've not worked out what data, if any, it should return then I've got ahead of myself - how can I even name a function if I don't know what it produces??). The @param tags I usually leave until after I've written and tested the function, as they can change a lot (i.e. I need an algorithm before I know what inputs my algorithm takes).&lt;br /&gt;&lt;br /&gt;During the writing of the function body I will usually jump up to the comment block and add an @internal comment (describing the implementation), or a @todo comment ("Need to add error-checking on call to somefunc()" is a very common one!).&lt;br /&gt;&lt;br /&gt;To quote from the php|a article: "What's really exciting,though, is that since PHP 5.1, the Reflection classes can also supply the subject's "doc comment".&lt;br /&gt;&lt;br /&gt;See &lt;a href="http://php.net/reflection"&gt;http://php.net/reflection&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The php|a article introduced a base class for automatically creating setters for class vars. My own interest is in somehow using it for contract based programming (also called DBC: Design By Contract).&lt;br /&gt;&lt;br /&gt;I believe the Java implementation of DBC uses docblock entries, but with a pre-compiler. Pre-compilers suck: being able to edit and run the same source code file is a big bonus, and maybe reflection of docblocks enables that?&lt;br /&gt;&lt;br /&gt;Of course using comments is poor cousin of DBC built into the language itself (especially if it can do compile-time analysis). But, for some reason I've not yet grasped, language designers just don't understand how important it is. They limp along with lint and asserts instead.&lt;br /&gt;&lt;br /&gt;Back to the docblocks, I've not thought out the details; does PHP provide some hook that can be called when each function is entered and exited? If not I guess you'd have to write VALIDATE() as the first line of each function, and a RETURN() function everywhere you use return, which would be rather intrusive.&lt;br /&gt;&lt;br /&gt;Either way, it could then look for @paramvalidate tags which could describe valid data to be found in parameters. It could look for @variant tags in the class doc block to check the object is valid on each of entry and exit.&lt;br /&gt;&lt;br /&gt;Did I mention DBC finds loads of bugs, without you having to write a single unit test; all you have to do is document properly. Which, of course, you are going to do anyway if you are a professional programmer.&lt;br /&gt;&lt;br /&gt;But did you realize DBC can also be used for optimization, by prioviding hints to the optimizer? As a concrete example (I'm thinking about C++ here) you have two input variables both described as being in the range 1..100. When they are multiplied together the range is 1..10000. Therefore there is no need to consider overflow. If you then divide by that variable then a divide by zero error is impossible.  I'm not a compiler writer, and haven't written assembly in about 15 years, but I'm sure the above information, or similar, can help.&lt;br /&gt;&lt;br /&gt;As another example, that also applies to PHP, static analysis of possible values can tell you that for an if/elseif/elseif/else block only the second elseif block can ever be reached. This can be both reported as a compiler warning and all the conditional stuff thrown away.&lt;br /&gt;&lt;br /&gt;But, that is all a pipe dream, it requires DBC being implemented in the language itself, and I've drifted from the point of this article. Which was: a PHP script can read its own comments! Cool!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7800490556231188013?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7800490556231188013/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7800490556231188013' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7800490556231188013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7800490556231188013'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/02/php-comments-from-php.html' title='PHP comments from PHP!'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-7363758236449584904</id><published>2009-01-29T18:20:00.000-08:00</published><updated>2009-01-29T18:31:26.325-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Merging PDF files (and more!)</title><content type='html'>My mobile phone company decided, 7 months ago, to save the world by not sending paper invoices any more. Unfortunately they didn't tell me (and registering to see invoices online required jumping through lots of hoops, while holding a PIN number I didn't know).&lt;br /&gt;All sorted now, and I only lost one invoice (they only keep the last six online). So I downloaded six shiny new PDF files. Like my phone company I also want to save the world, so wanted to print them four to a page.&lt;br /&gt;&lt;br /&gt;The solution is pdftk (in Ubuntu's package list; also available for other major distributions I believe). This simple command:&lt;br /&gt;  pdftk 2008*.pdf cat output all.pdf&lt;br /&gt;&lt;br /&gt;It just worked. Nice. It also does lots of other cool stuff, like adding or removing encryption, remove just one page out of the middle of a PDF, rotate, extract text. No mention of i18n, so I don't know (yet) how it will cope with Japanese PDFs. Or PDFs where the text is actually an image. The projects home page is &lt;a href="http://www.accesspdf.com/pdftk/"&gt;http://www.accesspdf.com/pdftk/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It appears there are also Windows and Mac versions.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-7363758236449584904?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/7363758236449584904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=7363758236449584904' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7363758236449584904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/7363758236449584904'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/01/merging-pdf-files-and-more.html' title='Merging PDF files (and more!)'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-5277944032532484137</id><published>2009-01-21T17:10:00.000-08:00</published><updated>2009-01-21T21:52:46.093-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='gnome'/><title type='text'>Gnome file extensions are screwing up my life!</title><content type='html'>Okay, perhaps a touch of frustration crept into that choice of subject.&lt;br /&gt;When you double click a file in gnome file manager a lot of the time it works. When it doesn't you are in deep trouble. I've visited this problem before:&lt;br /&gt;http://darrendev.blogspot.com/2008/06/gnome-file-associations-air-and-zip.html&lt;br /&gt;http://darrendev.blogspot.com/2008/05/gnome-whether-under-redhat-or-ubuntu-is.html&lt;br /&gt;&lt;br /&gt;I never did fix the AIR problem - I had to uninstall AIR completely.&lt;br /&gt;Now, since yesterday, all my inc files refuse to open in SciTE. They say "this claims to be an inc file, but the contents look like PHP. Even though SciTE is your editor of choice for both inc and php files I'm not going to let you open it. Please also check behind the sofa for terrorists."&lt;br /&gt;&lt;br /&gt;I suspect it was because I was editing an inc file in jedit a few days ago and manually chose PHP for the syntax highlighting. But if so, how and why is jedit allowed to alter my system configuration??&lt;br /&gt;(I've also noticed that when clicking links within zip files gedit is now associated with txt files; it used to be open office writer. Something weird is going on, but at least this one is an improvement: application associations in zip files are a world of their own, and I've never tracked down any method of configuring them.)&lt;br /&gt;&lt;br /&gt;Following my advice in the second of my above blog entries I tried making a php.xml file for *.inc files. But no luck there.&lt;br /&gt;&lt;br /&gt;Somebody help! How do I restore this? Or, even better, how do I switch off Gnome's stupid wrong-extension-is-a-security-risk check? In many years of using gnome it has &lt;span style="font-style: italic;"&gt;never once&lt;/span&gt; complained in a useful way, or detected a genuine problem. Every single time it has just got in the way.&lt;br /&gt;&lt;br /&gt;(If you type "gnome-control-center" there is supposed to be an icon in there for file types. It is missing in ubuntu 7! Or perhaps it was only in there in gnome 1 and got removed in gnome 2.)&lt;br /&gt;&lt;br /&gt;UPDATE: Problem half-solved. I've installed PC File Manager, and replaced Nautilus everywhere. See the instructions here:&lt;br /&gt;http://linuxondesktop.blogspot.com/2008/05/replacing-nautilus-with-quicker-and.html&lt;br /&gt;(they say Ubuntu 8 but everything seems to apply to Ubuntu 7 fine.)&lt;br /&gt;I had to go through each text file extension and assign it to /usr/bin/scite (it was defaulting to jedit for most of them, and defaulting to nothing for the troublesome *.inc), but after I did that it is working nicely. File associations appear to be much more transparent than in Nautilus.&lt;br /&gt;However following instructions in 2nd post here: http://ubuntuforums.org/showthread.php?t=692238  for replacing the desktop didn't go so well. The icons cannot be moved around on the desktop, and launchers were not working. So I undid those changes, and will now try and change my directory launchers on my desktop to use pcmanfm. It seems I'll need to go through and replace each "location launcher" with an application launcher that calls pcmanfm. A bit of a pain, but looks like it will work.&lt;br /&gt;&lt;br /&gt;Perhaps I'll investigate kubuntu... Surely KDE cannot be worse than Gnome/Nautilus?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-5277944032532484137?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/5277944032532484137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=5277944032532484137' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5277944032532484137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/5277944032532484137'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/01/gnome-file-extensions-are-screwing-up.html' title='Gnome file extensions are screwing up my life!'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-4954611155797533701</id><published>2009-01-13T17:02:00.000-08:00</published><updated>2009-01-13T17:12:31.865-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='commandline'/><title type='text'>linux shell: two directories quick switch</title><content type='html'>Sometimes I have to work in two directories from the commandline. For instance to check a log file got created, then back to the directory where the command I am testing is run from. The quick tip is simply "cd -". This takes you back to the previous directory. If you type it again it takes you back to where you were, acting as a nice toggle.&lt;br /&gt;&lt;br /&gt;"cd" with no parameters takes you to your home directory. "cd ~/somewhere" takes you directly to a directory relative to your home directory (i.e. tilde expands to your home directory). I had a hunt around but couldn't find any other special characters to use with "cd", but if you know one please let me know.&lt;br /&gt;&lt;br /&gt;(This tip was found in Linux Magazine, June 2008)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-4954611155797533701?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/4954611155797533701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=4954611155797533701' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4954611155797533701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/4954611155797533701'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2009/01/linux-shell-two-directories-quick.html' title='linux shell: two directories quick switch'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6728357435846581596</id><published>2008-11-04T16:32:00.000-08:00</published><updated>2008-11-05T17:16:07.666-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='data presentation'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>PHP and outputting floats</title><content type='html'>I was writing a script in PHP to compare two data sources, and in the report I was outputting using code like this:&lt;br /&gt;echo "A={$vA}, B={$vB}\n";&lt;br /&gt;&lt;br /&gt;What was strange is I was getting entries like&lt;br /&gt;A=3.4E+6, B=340000&lt;br /&gt;A=1.4E+6, B=1500000&lt;br /&gt;A=2146666.66667, B=2.3E+6&lt;br /&gt;&lt;br /&gt;My first guess was some data was int, so I added asserts for that, but, no, everything is float.&lt;br /&gt;&lt;br /&gt;As often happens, the answer was to be found in the user contributed notes in the PHP online manual:  &lt;small&gt;&lt;/small&gt;&lt;a href="http://jp.php.net/manual/en/language.types.float.php#83577"&gt;http://jp.php.net/manual/en/language.types.float.php#83577&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I just have to repeat his final sentence:&lt;span style="font-style: italic;"&gt; I have to be honest: this is one of the strangest things I have seen in any language in over 20 years of coding, and it is a colossal pain to work around.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Also in the notes are complaints that output of floating point numbers uses the locale. Using &lt;a href="http://jp.php.net/manual/en/function.number-format.php"&gt;number_format&lt;/a&gt; is one solution for that, or using %F in &lt;a href="http://jp.php.net/manual/en/function.sprintf.php"&gt;sprintf&lt;/a&gt; is also locale-independent apparently. So my code becomes:&lt;br /&gt;echo sprintf("A=%F, B=%F\n",$vA,$vB);&lt;br /&gt;&lt;br /&gt;Unfortunately that now gives me entries like:&lt;br /&gt;A=481.000000,B=1150000.000000&lt;br /&gt;&lt;br /&gt;I have hit this zero-noise issue using sprintf in C++ too.&lt;br /&gt;&lt;br /&gt;So here is my solution (be aware that this adds CPU cycles):&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Format a float, only showing significant decimal places&lt;br /&gt;*&lt;br /&gt;* @param float $v&lt;br /&gt;* @return String&lt;br /&gt;*/&lt;br /&gt;function fmt_float($v){&lt;br /&gt;$s=(string)$v;&lt;br /&gt;if(strpos($s,'E+')===false)return $s;&lt;br /&gt;$s=sprintf("%.9F",$v); //Write with all decimal places&lt;br /&gt;$s=rtrim($s,'0');   //Chop off trailing zeroes&lt;br /&gt;$s=rtrim($s,'.');   //Chop off decimal point if it is left at the end.&lt;br /&gt;return $s;&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;echo "A=".fmt_float($vA).", B=".fmt_float($vB)."\n";&lt;br /&gt;&lt;br /&gt;I'll throw that into the next &lt;a href="http://dcook.org/software/fclib/"&gt;fclib&lt;/a&gt; release. Can anyone make a better version?&lt;br /&gt;&lt;br /&gt;Note: The first two lines mean use PHP's built-in float to string conversion by default and just use our own when it ended up in scientific notation. I found this gave better results (because using %.9F sometimes writes 120.0000000001 instead of simply 120.000000000, whereas the PHP built-in conversion is fine (giving "120") ). However if you also needed a locale-independent solution, then uncomment those first two lines (as PHP's built-in conversion uses locale).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span class="html"&gt;&lt;/span&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6728357435846581596?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6728357435846581596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6728357435846581596' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6728357435846581596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6728357435846581596'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2008/11/php-and-outputting-floats.html' title='PHP and outputting floats'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1625401331703494000.post-664527577284478575</id><published>2008-10-20T03:44:00.000-07:00</published><updated>2009-06-04T00:07:13.091-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogspot'/><title type='text'>Being emailled followups</title><content type='html'>&lt;span style="font-family: webdings;"&gt;A question about Blogspot seems best posted here! I noticed there had been some comments on other entries. Surely I'm not supposed to poll each article to see if there has been a new comment. I'm sure there was a setting to say to email me all follow-ups to all articles, but a recent search turned up nothing.&lt;br /&gt;Anyway, thanks for the replies!! I want to try out the Actionscript suggestions I received and will then update the articles if they work (or even if they don't).&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-664527577284478575?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/664527577284478575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=664527577284478575' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/664527577284478575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/664527577284478575'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2008/10/being-emailled-followups.html' title='Being emailled followups'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-6802653142235605466</id><published>2008-10-15T03:35:00.000-07:00</published><updated>2008-10-15T03:44:57.768-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><title type='text'>Microsoft SQL server and PHP</title><content type='html'>I'm a big fan of Pear::DB. (And, as an aside, I think the Pear decision to deprecate it was a bit strange.) But it didn't work for me for Microsoft SQL server (aka mssql), for one particular project (I'm fairly sure it has worked on other projects, so I think it was something specific to this environment). What did work was using the COM object.&lt;br /&gt;&lt;br /&gt;COM objects from PHP are a bit of a black art, perhaps as most code examples seem to be in Visual Basic, which has a very different syntax. I found the following page useful, as it shows all three ways of running the same query for a mssql database:&lt;br /&gt;  http://www.webcheatsheet.com/PHP/connect_mssql_database.php&lt;br /&gt;&lt;br /&gt;I think I also used the user comments in the online PHP manual.&lt;br /&gt;&lt;br /&gt;One thing the above URL does not show is error reporting. Here is the code I developed, which also takes care of not treating 5701 and 5703 as errors (congrats to Microsoft on another poorly thought out API!):&lt;br /&gt;&lt;br /&gt;//$db is the return from the new COM() call.&lt;br /&gt;//...run some Open or Execute command on $db...&lt;br /&gt;$errors=$db-&gt;Errors;&lt;br /&gt;if($errors-&gt;Count==0)return "";    //It worked&lt;br /&gt;$e='';&lt;br /&gt;foreach($errors as $ix=&gt;$error){&lt;br /&gt;    if($error-&gt;NativeError==5701 || $error-&gt;NativeError==5703)continue; //Information messages, not errors&lt;br /&gt;    $e.="Number={$error-&gt;Number}, Source={$error-&gt;Source}, SQLState={$error-&gt;SQLState}, NativeError={$error-&gt;NativeError}, Description={$error-&gt;Description}\n";&lt;br /&gt;    }&lt;br /&gt;return $e;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-6802653142235605466?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/6802653142235605466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=6802653142235605466' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6802653142235605466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/6802653142235605466'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2008/10/microsoft-sql-server-and-php.html' title='Microsoft SQL server and PHP'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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-1625401331703494000.post-68757004774841103</id><published>2008-07-20T21:01:00.001-07:00</published><updated>2008-07-20T21:32:40.134-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AS3'/><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Centering multiline text fields in AS3?</title><content type='html'>This seems to be a regression-without-cure between AS2 and AS3. In AS2 I could write:&lt;br /&gt;  txt.wordWrap=true;&lt;br /&gt;  txt.align="center";&lt;br /&gt;  txt.htmlText="blah, blah, blah";&lt;br /&gt;&lt;br /&gt;and each line in that text field would be centered.&lt;br /&gt;&lt;br /&gt;In AS3 I am writing:&lt;br /&gt;  txt.wordWrap=true;&lt;br /&gt;  txt.align=TextFieldAutoSize.CENTER;&lt;br /&gt;  txt.htmlText="blah, blah, blah";&lt;br /&gt;&lt;br /&gt;but it ends up left-aligned.&lt;br /&gt;The bigger problem is that this is the documented behaviour: if wordWrap is set then align will not center text.&lt;br /&gt;&lt;br /&gt;So, how do I center a multi-line text field?!!&lt;br /&gt;&lt;br /&gt;(Time passes.)&lt;br /&gt;&lt;br /&gt;I cannot believe there is nothing on the web about this. Have all the flash developers in the world stopped centering their text, or am I missing something obvious?&lt;br /&gt;&lt;br /&gt;(More time passes.)&lt;br /&gt;&lt;br /&gt;OK, after some trial-and-error I have a workaround, which sucks but will get the job done:&lt;br /&gt;&lt;br /&gt;CopyA.htmlText='&amp;lt;body&gt;&amp;lt;p align="center"&gt;blah, blah, blah&amp;lt;/p&gt;&amp;lt;/body&gt;';&lt;br /&gt;&lt;br /&gt;Why do I say this sucks? Because I cannot (easily) apply it afterwards: I will need to get the current htmlText contents, detect the body tag and insert after it; and a corresponding closing-P tag at the end.&lt;br /&gt;&lt;br /&gt;But in this application the text string comes from a DB and is allowed to contain HTML tags. What if it too already has a P tag? Will I get an extra blank line? Are P tags allowed to be nested?&lt;br /&gt;(Well, I just tried, and it seems fine still, and if the inner P tag has an align that is the one that gets applied. So all good there.)&lt;br /&gt;&lt;br /&gt;UPDATE: I discovered I can also write this:&lt;br /&gt;CopyA.htmlText='&amp;lt;p align="center"&gt;&amp;lt;body&gt;blah, blah, blah&amp;lt;/body&gt;&amp;lt;/p&gt;';&lt;br /&gt;That is dubious, and I wonder if it is future-proof, or if I am exploiting a bug? But it is obviously far easier, as it saves me having to do any parsing - I can just wrap whatever the existing htmlText is:&lt;br /&gt;copyA.htmlText='&amp;lt;p align="center"&gt;'+copyA.htmlText+'&amp;lt;/p&gt;';&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1625401331703494000-68757004774841103?l=darrendev.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://darrendev.blogspot.com/feeds/68757004774841103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1625401331703494000&amp;postID=68757004774841103' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/68757004774841103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1625401331703494000/posts/default/68757004774841103'/><link rel='alternate' type='text/html' href='http://darrendev.blogspot.com/2008/07/centering-multiline-text-fields-in-as3.html' title='Centering multiline text fields in AS3?'/><author><name>darren</name><uri>http://www.blogger.com/profile/03151050273122725547</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>3</thr:total></entry></feed>
