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

<channel>
	<title>Jamie's Digital Blog &#187; Development</title>
	<atom:link href="http://www.jamiedigi.com/category/development/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jamiedigi.com</link>
	<description>Digital marketing, search marketing, photography</description>
	<lastBuildDate>Wed, 28 Jul 2010 08:56:00 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Recursively Getting Directory Contents in C#</title>
		<link>http://www.jamiedigi.com/2009/08/recursively-getting-directory-contents-in-csharp/</link>
		<comments>http://www.jamiedigi.com/2009/08/recursively-getting-directory-contents-in-csharp/#comments</comments>
		<pubDate>Mon, 24 Aug 2009 18:38:39 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[recursive]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=612</guid>
		<description><![CDATA[Have you ever wanted an easy way to get all of the contents of a directory into one array, regardless of how many levels of subfolders there are?   This is a task for my favourite kind of programming &#8211; nested loops!
The following function will do the job nicely:
private static void listDirectoryNest(string path, ref [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever wanted an easy way to get all of the contents of a directory into one array, regardless of how many levels of subfolders there are?   This is a task for my favourite kind of programming &#8211; nested loops!</p>
<p>The following function will do the job nicely:</p>
<blockquote><pre>private static void listDirectoryNest(string path, ref List&lt;string&gt; currentList, bool includedirs)
{

    foreach (string dir in Directory.GetDirectories(path))
    {
        listDirectoryNest(dir + "\\", ref currentList, includedirs);
        if (includedirs)
            currentList.Add(dir + "\\");
    }

    foreach (string file in Directory.GetFiles(path))
    {
        currentList.Add(file);
    }

}</pre>
</blockquote>
<p>This function takes the string <code>path</code> which defines where the nesting is to start, for example <code>e:\documents</code>.  It then takes the reference <code>currentList</code> which is a generic string list.  After the function is run this string list will contain all of the files within the original path (including those in subfolders).  The final variable, <code>includedirs</code> defines whether or not to include directories in the <code>currentList</code> output.  If you set this variable to <code>true</code> then you can determine which entries in <code>currentList</code> are directories by checking if they end with the <code>\</code> character.</p>
<p>This is an example of how to call the function above:</p>
<blockquote><pre>List&lt;string&gt; output = new List&lt;string&gt;();

listDirectoryNest("e:\\documents", ref output, true);</pre>
</blockquote>
<p>After running that code, <code>output</code> will contain all of the contents of e:\documents\, including subfolders, in a format like:</p>
<ul>
<li>e:\documents\something.doc</li>
<li>e:\documents\something-else.xls</li>
<li>e:\documents\some subfolder\</li>
<li>e:\documents\some subfolder\subdocument.xls</li>
<li>e:\documents\my pictures\</li>
<li>e:\documents\my pictures\photo.jpg</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/08/recursively-getting-directory-contents-in-csharp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sorting cute girls using IComparable won&#8217;t get you a date</title>
		<link>http://www.jamiedigi.com/2009/08/sorting-cute-girls-using-icomparable-wont-get-you-a-date/</link>
		<comments>http://www.jamiedigi.com/2009/08/sorting-cute-girls-using-icomparable-wont-get-you-a-date/#comments</comments>
		<pubDate>Sat, 08 Aug 2009 14:49:17 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=604</guid>
		<description><![CDATA[Although I found this article on C# sorting algorithms very useful, I think I found one flaw:
[Rolvin] has seen alot of girls around the area, met and chatted with some of them, got their numbers but he never thought about courting or asking some for a date up until now.  Rolvin is smart.. he [...]]]></description>
			<content:encoded><![CDATA[<p>Although I found this article on <a href="http://devpinoy.org/blogs/keithrull/archive/2007/03/23/sorting-a-generic-list-of-object-in-c-using-icomparable-and-anonymous-delegates.aspx">C# sorting algorithms</a> very useful, I think I found one flaw:</p>
<blockquote><p>[Rolvin] has seen alot of girls around the area, met and chatted with some of them, got their numbers but he never thought about courting or asking some for a date up until now.  Rolvin is smart.. he decided to pullout a tissue paper and listed all the names of the girls he met and some basic information about them. There are 3 things that he thought were important. First, the girl should have a name. Second, the girl that he will down on his list should have an age. And lastly, the &#8220;cuteness factor&#8221;. He thinks that he is a very handsome guy and he deserves somebody equal or greater than his looks(talk about ego;)).</p>
<p>Ok, now this is our chance to help Rolvin. We need to create the Girl class first.</p></blockquote>
<p>It was all going so well until the last sentence.  I think Rolvin probably needs to give in now.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/08/sorting-cute-girls-using-icomparable-wont-get-you-a-date/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using PHP to Output RFC3339 Dates from MySQL for Atom Feeds</title>
		<link>http://www.jamiedigi.com/2009/07/using-php-to-output-rfc3339-dates-from-mysql-for-atom-feeds/</link>
		<comments>http://www.jamiedigi.com/2009/07/using-php-to-output-rfc3339-dates-from-mysql-for-atom-feeds/#comments</comments>
		<pubDate>Fri, 17 Jul 2009 13:05:06 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[atom]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rfc3339]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=600</guid>
		<description><![CDATA[Creating an Atom feed manually in PHP is fairly easy &#8211; it is just like generating HTML.  However the Atom specification is quite strict on the date format required:
A Date construct is an element whose content MUST conform to the &#8220;date-time&#8221; production in [RFC3339].  In addition, an uppercase &#8220;T&#8221; character MUST be used [...]]]></description>
			<content:encoded><![CDATA[<p>Creating an Atom feed manually in PHP is fairly easy &#8211; it is just like generating HTML.  However the <a href="http://tools.ietf.org/html/rfc4287">Atom specification</a> is quite strict on the <a href="http://tools.ietf.org/html/rfc4287#section-3.3">date format</a> required:</p>
<blockquote><p>A Date construct is an element whose content MUST conform to the &#8220;date-time&#8221; production in [RFC3339].  In addition, an uppercase &#8220;T&#8221; character MUST be used to separate date and time, and an uppercase &#8220;Z&#8221; character MUST be present in the absence of a numeric time zone offset.</p></blockquote>
<p>Unfortunately this is very different to the default date format that MySQL exports, so you need to convert it.  Thankfully this is very easy.  Assuming <code>$line</code> contains your MySQL record array then this code will work:</p>
<blockquote><pre>echo date('Y-m-d\TH:i:s\Z', strtotime($line["date_added"]));</pre>
</blockquote>
<p>This will output the date in the format required to pass <a href="http://validator.w3.org/feed/">Atom feed validation</a>.</p>
<p>The only other awkward task when exporting an Atom feed is making sure that you send the correct <code>Content-type</code> declaration in the header.  The default header <code>Content-type</code> for PHP output is <code>text/html</code>.  For an Atom feed this should be <code>application/atom+xml</code> instead.  To do this, include the following line at the top of your code, before you have output anything to the client:</p>
<blockquote><pre>header('Content-type: application/atom+xml');</pre>
</blockquote>
<p>Your feed should now return the correct content type, and contain dates that conform to the Atom specification.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/07/using-php-to-output-rfc3339-dates-from-mysql-for-atom-feeds/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript: Creating a Smooth Auto-Hiding Toolbar</title>
		<link>http://www.jamiedigi.com/2009/07/javascript-creating-a-smooth-auto-hiding-toolbar/</link>
		<comments>http://www.jamiedigi.com/2009/07/javascript-creating-a-smooth-auto-hiding-toolbar/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 11:26:46 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=588</guid>
		<description><![CDATA[When displaying photographs or other multimedia, a toolbar is a great way to manage the navigation &#8211; its simple, intuitive and unintrusive.  It&#8217;s even less likely to detract from the experience if it hides itself automatically when not in use, and it looks cool if it does this in a slick, smooth way.  [...]]]></description>
			<content:encoded><![CDATA[<p>When displaying photographs or other multimedia, a toolbar is a great way to manage the navigation &#8211; its simple, intuitive and unintrusive.  It&#8217;s even less likely to detract from the experience if it hides itself automatically when not in use, and it looks cool if it does this in a slick, smooth way.  For a simple example, see my <a href="http://www.jamiembrown.com/street-photography">Street Photography</a> page.</p>
<p>Although you can do this quite easily in Flash, I prefer to work in JavaScript as it gives you much more control over search engine behaviour, and you can use the same components for users without JavaScript enabled &#8211; you don&#8217;t need two versions of your site.  The following scripts work for all of the main browsers &#8211; IE, Firefox, Safari and Chrome.</p>
<p>The first stage is to make sure that you turn the scrollbars off on the page &#8211; the interface for displaying your photos is going to be all on one screen, with no need for the user to scroll up and down.  So in your main CSS file:</p>
<blockquote><pre>body {
	overflow: hidden;
}</pre>
</blockquote>
<p>Now in your HTML you want to define the DIV that will make up the toolbar.  This can be anywhere in your HTML code: </p>
<blockquote><pre>	&lt;div id="topbar"&gt;
	&lt;/div&gt;</pre>
</blockquote>
<p>In your CSS file, position this block as you would like it for users without JavaScript enabled:</p>
<blockquote><pre>div#topbar {
	position: absolute;
	left: 0px;
	top: 0px;
	background-color: #CCCCCC;
	background-image: url('/images/topback.png');
	background-repeat: repeat-x;
	width: 100%;
	height: 34px;
}</pre>
</blockquote>
<p>Notice that I&#8217;m giving it an absolute position of 0px x 0px &#8211; so top left.  I&#8217;m also describing its width as 100% &#8211; all the way across the screen, and its height as a set 34px.  I&#8217;m giving it a background, which is simply a PNG with a basic gradient.</p>
<p>Now create a text file called nav.js &#8211; this is going to be the JavaScript file that controls the showing and hiding of the top navigation bar.</p>
<blockquote><pre>var mode="none";
var toppos = 0;

function getMouse(ev) {

	var ypos = 0;
	var xpos = 0;

	if (!ev) {
		ypos = window.event.clientY;
		xpos = window.event.clientX;
	} else {
		ypos = ev.clientY;
		xpos = ev.clientX;
	}

	if (ypos<100 &#038;&#038; toppos<0 &#038;&#038; mode!="show") {
		mode="show";
		process();
	}
	if (ypos>100 &#038;&#038; toppos>-30 &#038;&#038; mode!="hide") {
		mode="hide";
		process();
	}

}

function process() {

	if (mode=="hide") {

		if (toppos>-30) {
			toppos = toppos - 1;
			document.getElementById("topbar").style.top = toppos + "px";
		} else {
			mode="none";
		}

	} else if (mode=="show") {

		if (toppos<0) {
			toppos = toppos + 1;
			document.getElementById("topbar").style.top = toppos + "px";
		} else {
			mode="none";
		}

	}

	if (mode!="none") {
		setTimeout("process()",20);
	}

}</pre>
</blockquote>
<p>To break this down a little:</p>
<ul>
<li>The first two variables are called <code>mode</code> and <code>toppos</code>.  <code>mode</code> defines what should be happening right now - whether the toolbar should be retracting into the top of the screen (hide) or dropping down into the frame (show).  <code>toppos</code> gives the current position of the toolbar.</li>
<li>The <code>getMouse(ev)</code> function (which we'll come to later) gets the current x and y position of the mouse within the browser window.  If the mouse is under 100 pixels from the top of the window then we'll start dropping the toolbar down.  If it is over 100 pixels then we'll start hiding the toolbar.</li>
<li>The <code>process()></code> function is a self-repeating function that actually manages the movement in a smooth way.  It <a href="http://www.jamiedigi.com/2009/06/doevents-in-javascript/">calls itself every 20 miliseconds</a> while there is work to do, and increments the toolbar by 1px either upwards or downwards, depending on the current <code>mode</code>.  When the toolbar is fully hidden or fully shown, it changes the <code>mode</code> to "none" and stops calling itself.</li>
</ul>
<p>We now need to call this nav.js file from your HTML file.  So between the <code>&lt;/head&gt;</code> and <code>&lt;body&gt;</code> tags put the following script declaration:</p>
<blockquote><pre>&lt;script type="text/javascript" src="nav.js"&gt;&lt;/script&gt;</pre>
</blockquote>
<p>Finally all you need to do is call the <code>getMouse(ev)</code> function from the HTML file.  So in the HTML file change your <code>&lt;body&gt;</code> tag to something like this:</p>
<blockquote><pre>&lt;body onMouseMove="getMouse(event);"&gt;</pre>
</blockquote>
<p>This will cause <code>getMouse</code> to be called everytime the mouse moves within the browser window, along with position information, allowing it to determine whether the toolbar should be shown or hidden.</p>
<p>You should now find that you have a nice toolbar box that moves in and out of the top of your page.  The next stage is to start filling that with content and buttons (just put them with in the <code>DIV</code>).  If you ensure that you use <a href="http://www.jamiedigi.com/2009/07/seo-friendly-javascript-urls/">SEO-friendly JavaScript function calls</a> then you should have a nifty-looking JavaScript powered site that is completely compatible with SEO best practice and users without JavaScript.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/07/javascript-creating-a-smooth-auto-hiding-toolbar/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SEO Friendly JavaScript URLs</title>
		<link>http://www.jamiedigi.com/2009/07/seo-friendly-javascript-urls/</link>
		<comments>http://www.jamiedigi.com/2009/07/seo-friendly-javascript-urls/#comments</comments>
		<pubDate>Sun, 05 Jul 2009 12:03:33 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Search Marketing]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[seo]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=574</guid>
		<description><![CDATA[This is a really simple topic, but something so many developers get wrong.  Lets imagine you have a page showing photographs.  When you click on the next button, you want some nice friendly JavaScript to take the user to the next photo.  You develop a function to do this called &#8220;showNextPicture()&#8221; and [...]]]></description>
			<content:encoded><![CDATA[<p>This is a really simple topic, but something so many developers get wrong.  Lets imagine you have a page <a href="http://www.jamiembrown.com/street-photography">showing photographs</a>.  When you click on the next button, you want some nice friendly JavaScript to take the user to the next photo.  You develop a function to do this called &#8220;showNextPicture()&#8221; and put this on your Next button:</p>
<blockquote><p><code>&lt;a href="javascript:showNextPicture()" title="Show Next Photo"&gt;Next Photo&lt;/a&gt;</code></p></blockquote>
<p>The problem here is that users without JavaScript (and search engines) cannot follow the link to the next photo.  Instead, a link like this works so much better:</p>
<blockquote><p><code>&lt;a href="link-to-next-photo.php" onClick="showNextPicture(); return false;" title="Show Next Photo"&gt;Next Photo&lt;/a&gt;</code></p></blockquote>
<p>In this case when the user has JavaScript enabled then their browser does the following when clicking on that link:</p>
<ul>
<li>See&#8217;s the <code>onClick</code> event, runs the <code>showNextPicture()</code> function.</li>
<li>See&#8217;s <code>return false</code>, which tells it not to follow the <code>href</code></li>
</ul>
<p>If the user has JavaScript disabled (or is a search engine) then it does the following when clicking on that link:</p>
<ul>
<li>It doesn&#8217;t understand <code>onClick</code>, so it ignores the <code>NextPicture()</code> function</li>
<li>Instead it just directly follows the <code>href</code> link</li>
</ul>
<p>All users get a great user experience, and you end up with your pages properly indexed in the search engines.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/07/seo-friendly-javascript-urls/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nice and easy file encryption in Linux using openssl</title>
		<link>http://www.jamiedigi.com/2009/06/nice-and-easy-file-encryption-in-linux-using-openssl/</link>
		<comments>http://www.jamiedigi.com/2009/06/nice-and-easy-file-encryption-in-linux-using-openssl/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 23:08:56 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[encryption]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[openssl]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=551</guid>
		<description><![CDATA[Providing you&#8217;ve got openssl installed then command line file encryption and decryption is a breeze on Linux.   And since almost all web servers have openssl installed this is a more robust solution than relying on bcrypt or gpg.
To encrypt a file:
openssl enc -aes-256-cbc -e -in /file/to/encrypt -out /file/to/save

To decrypt a file:
openssl enc -aes-256-cbc [...]]]></description>
			<content:encoded><![CDATA[<p>Providing you&#8217;ve got <code>openssl</code> installed then command line file encryption and decryption is a breeze on Linux.   And since almost all web servers have <code>openssl</code> installed this is a more robust solution than relying on <code>bcrypt</code> or <code>gpg</code>.</p>
<p>To encrypt a file:</p>
<blockquote><pre>openssl enc -aes-256-cbc -e -in /file/to/encrypt -out /file/to/save</pre>
</blockquote>
<p>To decrypt a file:</p>
<blockquote><pre>openssl enc -aes-256-cbc -d -in ~/file/to/decrypt -out ~/file/to/save</pre>
</blockquote>
<p>It&#8217;s worth noting that openssl does not delete the original file when encrypting or decrypting, so you&#8217;ll need to make sure that you do that afterwards.  It&#8217;s also worth mentioning that you can use <a href="http://www.openssl.org/docs/apps/enc.html">almost any type of encryption</a> you could dream of.  Simply replace <code>-aes-256-cbc</code> with one of the values below:</p>
<blockquote><pre> base64             Base 64

 bf-cbc             Blowfish in CBC mode
 bf                 Alias for bf-cbc
 bf-cfb             Blowfish in CFB mode
 bf-ecb             Blowfish in ECB mode
 bf-ofb             Blowfish in OFB mode

 cast-cbc           CAST in CBC mode
 cast               Alias for cast-cbc
 cast5-cbc          CAST5 in CBC mode
 cast5-cfb          CAST5 in CFB mode
 cast5-ecb          CAST5 in ECB mode
 cast5-ofb          CAST5 in OFB mode

 des-cbc            DES in CBC mode
 des                Alias for des-cbc
 des-cfb            DES in CBC mode
 des-ofb            DES in OFB mode
 des-ecb            DES in ECB mode

 des-ede-cbc        Two key triple DES EDE in CBC mode
 des-ede            Two key triple DES EDE in ECB mode
 des-ede-cfb        Two key triple DES EDE in CFB mode
 des-ede-ofb        Two key triple DES EDE in OFB mode

 des-ede3-cbc       Three key triple DES EDE in CBC mode
 des-ede3           Three key triple DES EDE in ECB mode
 des3               Alias for des-ede3-cbc
 des-ede3-cfb       Three key triple DES EDE CFB mode
 des-ede3-ofb       Three key triple DES EDE in OFB mode

 desx               DESX algorithm.

 gost89             GOST 28147-89 in CFB mode (provided by ccgost engine)
 gost89-cnt        `GOST 28147-89 in CNT mode (provided by ccgost engine)

 idea-cbc           IDEA algorithm in CBC mode
 idea               same as idea-cbc
 idea-cfb           IDEA in CFB mode
 idea-ecb           IDEA in ECB mode
 idea-ofb           IDEA in OFB mode

 rc2-cbc            128 bit RC2 in CBC mode
 rc2                Alias for rc2-cbc
 rc2-cfb            128 bit RC2 in CFB mode
 rc2-ecb            128 bit RC2 in ECB mode
 rc2-ofb            128 bit RC2 in OFB mode
 rc2-64-cbc         64 bit RC2 in CBC mode
 rc2-40-cbc         40 bit RC2 in CBC mode

 rc4                128 bit RC4
 rc4-64             64 bit RC4
 rc4-40             40 bit RC4

 rc5-cbc            RC5 cipher in CBC mode
 rc5                Alias for rc5-cbc
 rc5-cfb            RC5 cipher in CFB mode
 rc5-ecb            RC5 cipher in ECB mode
 rc5-ofb            RC5 cipher in OFB mode

 aes-[128|192|256]-cbc  128/192/256 bit AES in CBC mode
 aes-[128|192|256]      Alias for aes-[128|192|256]-cbc
 aes-[128|192|256]-cfb  128/192/256 bit AES in 128 bit CFB mode
 aes-[128|192|256]-cfb1 128/192/256 bit AES in 1 bit CFB mode
 aes-[128|192|256]-cfb8 128/192/256 bit AES in 8 bit CFB mode
 aes-[128|192|256]-ecb  128/192/256 bit AES in ECB mode
 aes-[128|192|256]-ofb  128/192/256 bit AES in OFB mode</pre>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/06/nice-and-easy-file-encryption-in-linux-using-openssl/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Backing up your website using FTP on Linux</title>
		<link>http://www.jamiedigi.com/2009/06/backing-up-your-website-using-ftp-on-linux/</link>
		<comments>http://www.jamiedigi.com/2009/06/backing-up-your-website-using-ftp-on-linux/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 22:57:17 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[lftp]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=548</guid>
		<description><![CDATA[Linux provides a load of great command line tools for working with FTP, but by far my favourite is the lftp client &#8211; it is immensely powerful and allows you to work in a consistent way across all FTP operations.  It is also very easy to use.  Chances are you&#8217;ve got lftp installed [...]]]></description>
			<content:encoded><![CDATA[<p>Linux provides a load of great command line tools for working with FTP, but by far my favourite is the <code>lftp</code> client &#8211; it is immensely powerful and allows you to work in a consistent way across all FTP operations.  It is also very easy to use.  Chances are you&#8217;ve got <code>lftp</code> installed already, but if not you should be able to install it using your default package manager:</p>
<blockquote><pre>apt-get install lftp</pre>
</blockquote>
<p>The easiest way to get started with <code>lftp</code> is to use it in interactive mode, typing commands into the command prompt.  To connect to the remote FTP server type:</p>
<blockquote><pre>lftp -u ftpusername ftp.ftpserver.com</pre>
</blockquote>
<p>You can then use the <code>help</code> command to give you a breakdown of all of the FTP commands available to you.  You&#8217;ll find many of them are very familiar: <code>ls</code>, <code>cd</code>, <code>rm</code> etc.  To get help on any command type <code>help commandname</code>.</p>
<p>However <code>lftp</code> becomes most powerful when its scripted.  You can create a script like the following to pretty much do whatever you want:</p>
<blockquote><pre>open -u ftpusername,ftpassword ftp.ftpserver.com
... some FTP operations here...
close</pre>
</blockquote>
<p>Save this file as a text file, and then run it manually or from a bash script using:</p>
<blockquote><pre>lftp -f filename</pre>
</blockquote>
<p>One of the most useful (and simple) scripts that you can create is to back up the entire contents of your remote FTP account to your local PC:</p>
<blockquote><pre>open -u ftpusername,ftppassword ftp.ftpserver.com
lcd /local/path/to/copy/to
cd /remote/path/to/start/from
mirror -v
close</pre>
</blockquote>
<p><code>lcd</code> points <code>lftp</code> to the relevant folder on your PC.  <code>cd</code> points to the relevant folder on the remote PC.  The <code>mirror</code> command will then download the contents of all changed files to your local PC.  Once you&#8217;re happy with the way that it works, you can add the <code>--delete</code> parameter to the <code>mirror</code> command to delete any local files that are not found on the remote server.</p>
<p>You can also very easily add exclusions to the task, using the <code>exclude</code> parameter:</p>
<blockquote><pre>mirror -v --exclude somefolder/somesubfolder</pre>
</blockquote>
<p>In this case, nothing in somefolder/somesubfolder will be synchronised.  This is especially useful for ignoring large image directories, or logfile directories. </p>
<p>You can also use the <code>-R</code> parameter to reverse the operation, so if you wanted to work locally and then sync the contents of the local folders back to the remote server, you can use this script:</p>
<blockquote><pre>open -u ftpusername,ftppassword ftp.ftpserver.com
lcd /local/path/to/start/from
cd /remote/path/to/copy/to
mirror -R -v
close</pre>
</blockquote>
<p>In this case all contents from the <code>lcd</code> folder will be copied to the remote directory specified by <code>cd</code>.  Again you can specify exclusions using <code>--exclude</code> or use the <code>--delete</code> parameter to get rid of deleted files.</p>
<p>If you&#8217;re concerned about version control (overwriting changed files when synchronising) you can add the <code>--only-newer</code> parameter to mirror to stop it overwriting any files on the destination that are newer than the source.</p>
<p>You could pretty easily get a nice development workflow going by syncing down to your local PC before you start working, and then syncing up to the remote FTP server when you&#8217;re happy with the result.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/06/backing-up-your-website-using-ftp-on-linux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Checking if a file has changed using Bash scripting</title>
		<link>http://www.jamiedigi.com/2009/06/checking-if-a-file-has-changed-using-bash-scripting/</link>
		<comments>http://www.jamiedigi.com/2009/06/checking-if-a-file-has-changed-using-bash-scripting/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 22:16:48 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[md5]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=544</guid>
		<description><![CDATA[Linux Bash scripting is a powerful way to easily automate or simplify tasks.  However the solutions are not always intuitive unless you are very familiar with the syntax (which I am not).  For one particular script I wanted to launch a file in a text editor and then take an action afterwards only [...]]]></description>
			<content:encoded><![CDATA[<p>Linux Bash scripting is a powerful way to easily automate or simplify tasks.  However the solutions are not always intuitive unless you are very familiar with the syntax (which I am not).  For one particular script I wanted to launch a file in a text editor and then take an action afterwards only if the user had changed the file.</p>
<p>This solution seems to be working brilliantly:</p>
<blockquote><pre>MD5OLD=`md5sum ~/thefile | cut -d " " -f1`
vim ~/thefile
MD5NEW=`md5sum ~/thefile | cut -d " " -f1`
if [ "$MD5OLD" != "$MD5NEW" ]; then
    echo "The file has changed - do something here"
else
    echo "The file has not changed - don't do anything"
fi</blockquote>
</pre>
<p>The trick is to get an MD5 hash before launching the text editor, and then get another MD5 afterwards.  Comparing the two MD5 hash values will tell you if the file has changed or not.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/06/checking-if-a-file-has-changed-using-bash-scripting/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Solved: Cygwin Speed Issue</title>
		<link>http://www.jamiedigi.com/2009/06/solved-cygwin-speed-issue/</link>
		<comments>http://www.jamiedigi.com/2009/06/solved-cygwin-speed-issue/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 22:10:53 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[cygwin]]></category>
		<category><![CDATA[slow]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=536</guid>
		<description><![CDATA[While writing my previous post on Getting Started with Cygwin I was slightly perturbed to discover that my brand-new Cygwin installation was running pretty slowly.  Even simple commands like ls or cd were taking up to 10 seconds to run.
After doing a bit of digging I came across a very simple solution in the [...]]]></description>
			<content:encoded><![CDATA[<p>While writing my previous post on <a href="http://www.jamiedigi.com/2009/06/getting-started-with-cygwin/">Getting Started with Cygwin</a> I was slightly perturbed to discover that my brand-new Cygwin installation was running pretty slowly.  Even simple commands like <code>ls</code> or <code>cd</code> were taking up to 10 seconds to run.</p>
<p>After doing a bit of digging I came across a very simple solution in the <a href="http://cygwin.com/ml/cygwin/2003-11/msg00400.html">Cygwin mailing list archives</a> &#8211; I had a reference in my Windows PATH variable to a network drive that was unavailable.</p>
<p>To check this, right click on My Computer, and go to Properties.  Go to Advanced, and then Environment Variables:</p>
<p><img src="http://www.jamiedigi.com/wp-content/uploads/2009/06/SS-20090622230502.png" alt="Finding Environment Variables" title="Finding Environment Variables" width="418" height="485" class="alignnone size-full wp-image-537" /></p>
<p>In System Variables find PATH:</p>
<p><img src="http://www.jamiedigi.com/wp-content/uploads/2009/06/SS-20090622230559.png" alt="Finding the PATH system variable" title="Finding the PATH system variable" width="384" height="429" class="alignnone size-full wp-image-538" /></p>
<p>Click on Edit, and copy and paste the entire string into your favourite text editor (in my case <a href="http://notepad-plus.sourceforge.net/uk/site.htm">Notepad++</a>, but Notepad will do, or vim):</p>
<p><img src="http://www.jamiedigi.com/wp-content/uploads/2009/06/SS-20090622230941.png" alt="My system PATH variable" title="My system PATH variable" width="538" height="395" class="alignnone size-full wp-image-541" /></p>
<p>In this case the offending entry was <code>U:\scripts</code> &#8211; the U:\ drive was only available part of the time.  Removing this, pasting it all back into the PATH variable and then clicking OK instantly sped up Cygwin.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/06/solved-cygwin-speed-issue/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Getting Started with Cygwin</title>
		<link>http://www.jamiedigi.com/2009/06/getting-started-with-cygwin/</link>
		<comments>http://www.jamiedigi.com/2009/06/getting-started-with-cygwin/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 21:49:45 +0000</pubDate>
		<dc:creator>Jamie</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[cygwin]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[puttycyg]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://www.jamiedigi.com/?p=521</guid>
		<description><![CDATA[Cygwin is a version of the Linux bash shell for Windows.  It provides a great way to automatically script operations, and if you&#8217;re a fan of the command line then its a great way to navigate your system.  However the default Cygwin installation is not very friendly, and many features that you&#8217;d expect [...]]]></description>
			<content:encoded><![CDATA[<p>Cygwin is a version of the Linux bash shell for Windows.  It provides a great way to automatically script operations, and if you&#8217;re a fan of the command line then its a great way to navigate your system.  However the default Cygwin installation is not very friendly, and many features that you&#8217;d expect from the Linux command line are not configured by default.  The aim of this guide is to describe the actions I take when installing Cygwin from scratch.</p>
<p><strong>Part One &#8211; Downloading and Installing Cygwin</strong></p>
<p>Installing Cygwin is very easy &#8211; simply visit <a href="http://www.cygwin.com/">the Cygwin project page</a> and click on the Install Cygwin Now link.  Download the setup.exe file to your computer and run it.  The setup is a simple wizard interface, which will take you step by step through the process, including selecting the packages you want included.  I usually use the defaults, adding only a couple:</p>
<ul>
<li><code>vim</code> &#8211; the UNIX text editor of my choice.  emacs and nano are also available (along with many others)</li>
<li><code>lftp</code> &#8211; a powerfull command line FTP client, used for scripting FTP operations.</li>
<li><code>openssh</code> &#8211; the Linux SSH client</li>
<li><code>openssl</code> &#8211; command line SSL, great for doing simple encryption tasks</li>
</ul>
<p>You can also select to install everything, which will give you a pretty complete Linux system, but there are a lot of packages and they take ages to download.  Most of them you&#8217;re unlikely to use.</p>
<p><img src="http://www.jamiedigi.com/wp-content/uploads/2009/06/SS-20090622225154.png" alt="Selecting packages in Cygwin setup" title="Selecting packages in Cygwin setup" width="504" height="395" class="alignnone size-full wp-image-532" /></p>
<p>You can come back and run setup.exe again later if you find you want to add or remove packages.  It acts as a pretty comprehensive package management system for your Cygwin installation &#8211; it can be used to update your system with the latest binaries too.</p>
<p><strong>Part Two &#8211; Starting Up</strong></p>
<p>When you first start Cygwin, you might get the error:</p>
<blockquote><pre>Your group is currently "mkpasswd".  This indicates that
the /etc/passwd (and possibly /etc/group) files should be rebuilt.
See the man pages for mkpasswd and mkgroup then, for example, run
mkpasswd -l [-d] > /etc/passwd
mkgroup  -l [-d] > /etc/group
Note that the -d switch is necessary for domain users.</pre>
</blockquote>
<p>This means that Cygwin is not aware of your current user setup.  Run the commands specified, using the <code>-d</code> switch if your computer is a member of a Windows domain.  These commands will create <code>/etc/passwd</code> and <code>/etc/group</code> files that tell Cygwin about your user setup.  Exit Cygwin and start it up again.</p>
<p><strong>Part Three &#8211; Setting Your Home Directory</strong></p>
<p>When you restart Cygwin you&#8217;ll probably find that your home directory is now your Windows home directory.  This might be what you want, but I always prefer to move it.  Moving it has the following advantages:</p>
<ul>
<li>Cygwin won&#8217;t get in the way of your Windows setup by adding configuration files (starting with .) to your nicely organised Windows home folders.</li>
<li>You can create symbolic links to My Pictures, My Music, My Videos etc, rather than having to type &#8220;My\ Pictures&#8221; every time.</li>
</ul>
<p>To change your home directory, edit the <code>/etc/passwd</code> file in your favourite text editor.  If you installed Cygwin in <code>C:\Cygwin</code> then this file will be here:</p>
<blockquote><pre>c:\Cygwin\etc\passwd</pre>
</blockquote>
<p>The <code>passwd</code> file is a list of users registered within Cygwin, with the following fields:</p>
<blockquote><pre>username:password:uid:gid:user info:home directory:shell</pre>
</blockquote>
<p>Find your Windows user and change the home directory to your preferred folder.  I usually just use <code>/home</code>.  You&#8217;ll need to create this folder in <code>c:\Cygwin\home</code> too.  You should find that when you restart Cygwin your home directory is the one you specified.</p>
<p><strong>Part Four &#8211; A Better Command Line</strong></p>
<p>The default Cygwin command line console is pretty naff.  I much prefer to use <a href="http://code.google.com/p/puttycyg/">PuttyCyg</a>, which is a patched version of the <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">PuTTY telnet/ssh client</a>.</p>
<p>To install PuttyCyg, download it from the <a href="http://code.google.com/p/puttycyg/downloads/list">project downloads page</a>, and unzip it to your Cygwin directory.  When you run it, simply select Cygterm as your session type and enter &#8220;<code>-</code>&#8221; as your host name.  Clicking Open will open up Cygwin.</p>
<p>There are a few minor tweaks I like to select when running PuttyCyg, which I save into a session called Cygwin:</p>
<ul>
<li>Window > Behaviour > System Menu Appears on ALT-Space</li>
<li>Window > Behaviour > Full Screen on ALT-Enter</li>
</ul>
<p><img src="http://www.jamiedigi.com/wp-content/uploads/2009/06/SS-20090622225317.png" alt="Setting up PuttyCyg" title="Setting up PuttyCyg" width="456" height="442" class="alignnone size-full wp-image-534" /></p>
<p>You can then create a Windows shortcut to the following command to run that saved session every time:</p>
<blockquote><pre>C:\cygwin\putty.exe -load Cygwin</pre>
</blockquote>
<p><strong>Part Five &#8211; Getting Your Keyboard Working</strong></p>
<p>If you have a bit of a play around with the Cygwin command line you might find that some things don&#8217;t work quite as you&#8217;d expect them to:</p>
<ul>
<li>There&#8217;s no way to paste into the PuttyCyg window (without using the mouse)</li>
<li><code>Home</code> and <code>End</code> keys might not work properly</li>
<li>The <code>Delete</code> key might not work as it&#8217;s supposed to</li>
<li>Auto-completion using the <code>Tab</code> key is case sensitive &#8211; fine in UNIX but hard to use with Windows</li>
</ul>
<p>To fix those issues (and a few more) create a file called <code>.inputrc</code> in your home directory.  The file should contain the following:</p>
<blockquote><pre>"\e[1~": beginning-of-line
"\e[7~": beginning-of-line
"\e[4~": end-of-line
"\e[8~": end-of-line
"\e[3~": delete-char
"\e[2~": paste-from-clipboard

set completion-ignore-case On
set meta-flag On
set convert-meta Off
set output-meta On

set horizontal-scroll-mode On</pre>
</blockquote>
<p>After restarting Cygwin you should find that the <code>Home</code>, <code>End</code> and <code>Delete</code> keys work nicely, and you can paste the contents of the Windows clipboard using <code>Insert</code>.  Auto-completion will be case insensitive.</p>
<p><strong>Part Six - Getting VIM Working</strong></p>
<p>As per any Linux installation, VIM without a <code>.vimrc</code> file defaults to the default behaviour of <code>vi</code>, which is fairly unfriendly.  Also some of the key mapping doesn't work beautifully in Cygwin.  The following is my <code>.vimrc</code> file (to be placed in your home directory):</p>
<blockquote><pre>set expandtab
set shiftwidth=3
set tabstop=4

set backspace=indent,eol,start
set scrolloff=1
set whichwrap=b,h,l,<,>,~,[,]

nnoremap <BS> dh

set background=light
set hidden
set showcmd
set ttyfast

set encoding=utf-8

set ignorecase
set smartcase
set incsearch
set hlsearch

syntax on
colorscheme torte</pre>
</blockquote>
<p><strong>Part Seven &#8211; Setting Up Some Folders</strong></p>
<p>Your Windows drives are all stored under a virtual folder called <code>/cygdrive/</code> in Cygwin.  So to access your <code>C:\</code> drive you would type:</p>
<blockquote><pre>cd /cygdrive/c</pre>
</blockquote>
<p>This is probably the best way they could have set this up, but its not exactly easy-access when you want to quickly get around your system.  To solve this I set up a few symbolic links.  You only need to run these commands once:</p>
<blockquote><pre>ln -s /cygdrive/c /c
ln -s /cygdrive/c/Documents\ and\ Settings/Your Windows Username/My\ Documents/ ~/docs
</pre>
</blockquote>
<p>You should now find that you can access your <code>C:\</code> drive using <code>cd /c</code> and your documents folder by using <code>cd ~/docs</code>.  Obviously you can set up as many of these symbolic links as you&#8217;d like &#8211; I usually set up links to My Pictures (~/pics), My Videos (~/videos) and My Music (~/music).</p>
<p><strong>Part Eight &#8211; Setting Up Some Shortcuts</strong></p>
<p>Personally I like to set up some shortcuts in my <code>~/.profile</code> file so that I can launch applications quickly:</p>
<blockquote><pre>alias cs='cygstart'
alias firefox='cygstart firefox'
alias ff='cygstart firefox'
alias notepad='cygstart notepad++'
alias navicat='cygstart /c/Program\ Files/PremiumSoft/Navicat\ 8.0\ MySQL/navicat.exe'
alias opera='cygstart /c/Program\ Files/Opera/opera.exe'
alias iexplore='cygstart /c/Program\ Files/Internet\ Explorer/iexplore.exe'
alias gimp='cygstart /c/Program\ Files/GIMP-2.0/bin/gimp-2.6.exe'
alias filezilla='cygstart /c/Program\ Files/FileZilla\ FTP\ Client/filezilla.exe'
alias cygwin='cygstart /c/cygwin/cygwin.lnk'</pre>
</blockquote>
<p>Note that the commands above won&#8217;t work unless you&#8217;ve implemented my <code>/c</code> symbolic link above &#8211; if you haven&#8217;t done that then you&#8217;ll need to use <code>/cygdrive/c</code> instead.  Also note that all of the commands use <code>cygstart</code> &#8211; <code>cygstart</code> is a great little command that basically launches whatever you want using Windows.  So if you&#8217;re in any folder and type:</p>
<blockquote><pre>cygstart .</pre>
</blockquote>
<p>&#8230; then it will open the current folder in Windows Explorer.  If you type:</p>
<blockquote><pre>cygstart somedoc.doc</pre>
</blockquote>
<p>&#8230; then somedoc.doc will open in Microsoft Office or Open Office.  In my aliases above I shorten <code>cygstart</code> to <code>cs</code> for quick access.</p>
<p><strong>Part Nine &#8211; What Next?</strong></p>
<p><img src="http://www.jamiedigi.com/wp-content/uploads/2009/06/SS-20090622232401.png" alt="Cygwin and PuttyCyg in action" title="Cygwin and PuttyCyg in action" width="646" height="488" class="alignnone size-full wp-image-546" /></p>
<p>After carrying out all of those steps you should have a nice, neat and easy to use Cygwin installation.  Next its down to you to leverage the power of the UNIX command line and give Windows a bit of kick.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jamiedigi.com/2009/06/getting-started-with-cygwin/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
