<?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/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Simple Things</title>
	<atom:link href="http://hexeract.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://hexeract.wordpress.com</link>
	<description>Ink and Incapability</description>
	<lastBuildDate>Wed, 12 Jun 2013 18:45:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='hexeract.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Simple Things</title>
		<link>http://hexeract.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://hexeract.wordpress.com/osd.xml" title="Simple Things" />
	<atom:link rel='hub' href='http://hexeract.wordpress.com/?pushpress=hub'/>
		<item>
		<title>A poor man&#8217;s version check for programs on Microsoft Windows using PowerShell</title>
		<link>http://hexeract.wordpress.com/2012/08/06/a-poor-mans-version-check-for-programs-on-windows-using-powershell/</link>
		<comments>http://hexeract.wordpress.com/2012/08/06/a-poor-mans-version-check-for-programs-on-windows-using-powershell/#comments</comments>
		<pubDate>Mon, 06 Aug 2012 14:47:16 +0000</pubDate>
		<dc:creator>hexeract</dc:creator>
				<category><![CDATA[Powershell]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Windows XP]]></category>
		<category><![CDATA[check]]></category>
		<category><![CDATA[powershell]]></category>
		<category><![CDATA[version]]></category>
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://hexeract.wordpress.com/?p=513</guid>
		<description><![CDATA[Keeping up to date is a lot of work, especially for programs which do not have an auto-update feature. The first step is to be able to find out what version of a program is actually installed. PowerShell to the rescue! For example, to find out what version of InternetExplorer is installed without actually launching [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=513&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Keeping up to date is a lot of work, especially for programs which do not have an auto-update feature. The first step is to be able to find out what version of a program is actually installed.</p>
<p>PowerShell to the rescue!</p>
<p>For example, to find out what version of InternetExplorer is installed without actually launching it, clicking on the gear and selecting the &#8220;Info&#8221; option the following PowerShell command can be used:</p>
<pre class="brush: powershell; title: ; wrap-lines: false; notranslate">
[System.Diagnostics.FileVersionInfo]::GetVersionInfo(&quot;C:\Program Files\Internet Explorer\iexplore.exe&quot;).Fileversion
</pre>
<p>&nbsp;<br />
The output should look something like this:</p>
<pre class="brush: plain; title: ; wrap-lines: false; notranslate">
$ [System.Diagnostics.FileVersionInfo]::GetVersionInfo(&quot;C:\Program Files\Internet Explorer\iexplore.exe&quot;).Fileversion
9.00.8112.16421 (WIN7_IE9_RTM.110308-0330)
</pre>
<p>Carveat: Not every program provides the metadata and thus the above can and will fail on some programs&#8230;</p>
<p>&nbsp;<br />
The PowerShell has a hash table data structure which can be made use of if one wants to check various programs. An example script could look like this:</p>
<pre class="brush: powershell; title: ; notranslate">
# clear screen
clear

# set up hash for the programs/paths
$programs = @{}
$programs[&quot;Internet Explorer&quot;]     = &quot;C:\Program Files\Internet Explorer\iexplore.exe&quot;
$programs[&quot;Notepad&quot;]               = &quot;C:\Windows\system32\notepad.exe&quot; 
$programs[&quot;Remote Desktop&quot;]        = &quot;$Env:WinDir\system32\mstsc.exe&quot;
$programs[&quot;Windows Media Player&quot;]  = &quot;C:\Program Files (x86)\Windows Media Player\wmplayer.exe&quot; 

# loop the hash and obtain the version for each item in the hash
foreach($key in $programs.keys){
    $mypath=($programs[&quot;$key&quot;])
    $version=([System.Diagnostics.FileVersionInfo]::GetVersionInfo(&quot;$mypath&quot;).FileVersion) 
    # prettyprint the output
    &quot;{0,-30}{1,-30}&quot; -f &quot;$key&quot;,&quot;$version&quot;
}
</pre>
<p>&nbsp;<br />
Checking for the currently installed Flash version is not quite as easy but can be done like this:</p>
<pre class="brush: powershell; title: ; wrap-lines: false; notranslate">
# Check Adobe Flash Version
&quot;Adobe Flash Version Check&quot;
Get-ChildItem -Path C:\Windows -Filter NPSWF32* -Recurse -ErrorAction &quot;SilentlyContinue&quot; | select -expand VersionInfo  |select InternalName,Fileversion
</pre>
<p>&nbsp;<br />
Which, on my machine, shows the following output:</p>
<pre class="brush: plain; title: ; notranslate">
InternalName                                                FileVersion
------------                                                -----------
Adobe Flash Player 11.3                                     11,3,300,268
Adobe Flash Player 11.3                                     11,3,300,268
</pre>
<p>This is all you need to create a PowerShell script of your own to check specific programs for their versions.<br />
&nbsp;<br />
The script can be run like this:</p>
<pre class="brush: powershell; title: ; notranslate">
powershell -ExecutionPolicy Unrestricted -File cmv.ps1
</pre>
<p>&nbsp;<br />
For a quick overview you could do the following which creates a CSV (seperatly for each path):</p>
<pre class="brush: powershell; title: ; wrap-lines: false; notranslate">
Get-ChildItem -Path &quot;C:\Program Files&quot; -Filter *.exe -Recurse -ErrorAction &quot;SilentlyContinue&quot; | select -expand VersionInfo  | select InternalName,FileDescription,Fileversion  | Export-Csv -Path versions.csv
Get-ChildItem -Path &quot;C:\Program Files (x86)&quot; -Filter *.exe -Recurse -ErrorAction &quot;SilentlyContinue&quot; | select -expand VersionInfo  | select InternalName,FileDescription,Fileversion  | Export-Csv -Path versions-32bit.csv
</pre>
<p>You could omit the &#8220;| Export-Csv&#8221; command and substitute the last &#8220;select&#8221; command for &#8220;Format-List&#8221; or &#8220;Format-Table&#8221; if you want to make the output a little prettier. If all you do is to have a text file, I would recommend using Export-Csv.</p>
<p>As you can see, PowerShell is indeed quite powerful but it is very difficult to grasp if you come from a Unix shell background. And it probably would make a lot more sense to implement the above in C# which could also compare the result against a database which contains a list of programs and their current versions&#8230;  This is left as an exercise for the reader :)</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/hexeract.wordpress.com/513/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/hexeract.wordpress.com/513/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=513&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://hexeract.wordpress.com/2012/08/06/a-poor-mans-version-check-for-programs-on-windows-using-powershell/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/290abdb318ff76c5a995d01d90e722fc?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hexeract</media:title>
		</media:content>
	</item>
		<item>
		<title>How to expand the root filesystem of a 12.04 Ubuntu running inside VMWare Player</title>
		<link>http://hexeract.wordpress.com/2012/04/30/how-to-expand-the-root-filesystem-of-a-11-10-ubuntu-running-inside-vmware-player/</link>
		<comments>http://hexeract.wordpress.com/2012/04/30/how-to-expand-the-root-filesystem-of-a-11-10-ubuntu-running-inside-vmware-player/#comments</comments>
		<pubDate>Mon, 30 Apr 2012 15:45:33 +0000</pubDate>
		<dc:creator>hexeract</dc:creator>
				<category><![CDATA[howto]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[expand]]></category>
		<category><![CDATA[fdisk]]></category>
		<category><![CDATA[filesystem]]></category>
		<category><![CDATA[player]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[vmware]]></category>

		<guid isPermaLink="false">http://hexeract.wordpress.com/?p=491</guid>
		<description><![CDATA[Yesterday, I ran into the problem that I was missing enough disk space to upgrade my virtualised Ubuntu from 11.10 to 12.04. At least &#8216;do-release-upgrade&#8217; told me so prior to having 100% disk full errors. As I do not know of any magic tricks to cheat &#8216;do-release-upgrade&#8217; I decided to expand the root file system. [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=491&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Yesterday, I ran into the problem that I was missing enough disk space to upgrade my virtualised Ubuntu from 11.10 to 12.04. At least &#8216;do-release-upgrade&#8217; told me so prior to having 100% disk full errors.</p>
<p>As I do not know of any magic tricks to cheat &#8216;do-release-upgrade&#8217; I decided to expand the root file system.</p>
<p>For this article I have used a fresh installation/setup of Ubunutu 12.04 to show the steps required. Although VMWare Player recommends 20GB when installing Ubuntu 12.04, I have reduced this to 10GB. The VMWare Player version used for this demonstration was 4.0.2 build-591240.</p>
<p>For all following steps, Terminal (xterm) is required, </p>
<p>Checking the filesystem setup:</p>
<pre class="brush: plain; gutter: false; highlight: [1,3]; light: true; title: ; notranslate">
cruz@ubuntu:~$ sudo bash
[sudo] password for cruz: 
root@ubuntu:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       9.0G  2.7G  5.9G  32% /
udev            488M  4.0K  488M   1% /dev
tmpfs           199M  800K  198M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            497M   76K  496M   1% /run/shm
root@ubuntu:~# 
</pre>
<p>Checking the partition layout:</p>
<pre class="brush: plain; gutter: false; highlight: [1]; light: true; title: ; notranslate">
root@ubuntu:~# fdisk -l /dev/sda

Disk /dev/sda: 10.7 GB, 10737418240 bytes
255 heads, 63 sectors/track, 1305 cylinders, total 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00001dec

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048    18874367     9436160   83  Linux
/dev/sda2        18876414    20969471     1046529    5  Extended
/dev/sda5        18876416    20969471     1046528   82  Linux swap / Solaris
root@ubuntu:~# 
</pre>
<p>Make a note of how many blocks the Linux swap is using, in this case it is <em>1046528</em>. If the Linux swap had been on its own device (/dev/sdb for example) things would have been a lot easier as we would not need to worry about the swap partition at all and could just extend /dev/sda. In this example, the Linux swap is also located in /dev/sda and thus also requires relocation.</p>
<p>At first, shut down the Linux from within</p>
<pre class="brush: plain; gutter: false; highlight: [1]; light: true; title: ; notranslate">
root@ubuntu:~# shutdown -h now
</pre>
<p>Then expand the hardisk via &#8220;Virtual Machine -&gt; Virtual Machine Settings -&gt; Harddisk(SCSI) -&gt; Utilities -&gt; Expand&#8221;. For this example, the maximum disk size was set to 15GB. If this step completes successfully, start the virtual machine.</p>
<p>For new partitions to be created/setup up, all old partitions have to be deleted.<br />
To be able to delete the swap partition the swap has to be turned off: </p>
<pre class="brush: plain; gutter: false; highlight: [7,13]; light: true; title: ; notranslate">
cruz@ubuntu:~$ sudo bash
[sudo] password for cruz: 
root@ubuntu:~# free -m
             total       used       free     shared    buffers     cached
Mem:           992        924         67          0         43        426
-/+ buffers/cache:        454        537
Swap:         1021          0       1021
root@ubuntu:~# swapoff -a
root@ubuntu:~# free -m
             total       used       free     shared    buffers     cached
Mem:           992        924         67          0         43        426
-/+ buffers/cache:        454        537
Swap:            0          0          0
root@ubuntu:~# 
</pre>
<p>The next step involves deleting both /dev/sda1 and /dev/sda2. It is <strong>very important</strong> to make a note of the start of the partition, in this example <em>2048</em>!</p>
<pre class="brush: plain; gutter: false; light: true; title: ; notranslate">
root@ubuntu:~# fdisk /dev/sda

Command (m for help): p

Disk /dev/sda: 16.1 GB, 16106127360 bytes
255 heads, 63 sectors/track, 1958 cylinders, total 31457280 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00001dec

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *        2048    18874367     9436160   83  Linux
/dev/sda2        18876414    20969471     1046529    5  Extended
/dev/sda5        18876416    20969471     1046528   82  Linux swap / Solaris

Command (m for help): d
Partition number (1-5): 1

Command (m for help): d
Partition number (1-5): 2

Command (m for help): p

Disk /dev/sda: 16.1 GB, 16106127360 bytes
255 heads, 63 sectors/track, 1958 cylinders, total 31457280 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00001dec

   Device Boot      Start         End      Blocks   Id  System

Command (m for help): 
</pre>
<p>Do not quit fdisk just yet! New partitions have to be created.</p>
<pre class="brush: plain; gutter: false; light: true; title: ; notranslate">
Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-31457279, default 2048): 
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-31457279, default 31457279): 30410751

Command (m for help): n
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): p
Partition number (1-4, default 2): 
Using default value 2
First sector (30410752-31457279, default 30410752): 
Using default value 30410752
Last sector, +sectors or +size{K,M,G} (30410752-31457279, default 31457279): 
Using default value 31457279
</pre>
<p>You might notice, the end of the first primary partition is not 31457279 but 30410751. This number was calculated by subtracting the amount of blocks used by the swap from the block count of the new disk (in our example: 31457279-1046528 = 30410751).</p>
<p>The primary partition is now correct but the swap partition is not. The type of the partition should not be &#8220;83&#8243; but &#8220;82&#8243; (marking it as a partition for the Linux swap)</p>
<pre class="brush: plain; gutter: false; light: true; title: ; notranslate">
Command (m for help): p

Disk /dev/sda: 16.1 GB, 16106127360 bytes
255 heads, 63 sectors/track, 1958 cylinders, total 31457280 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00001dec

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1            2048    30410751    15204352   83  Linux
/dev/sda2        30410752    31457279      523264   83  Linux

Command (m for help): t
Partition number (1-4): 2
Hex code (type L to list codes): 82
Changed system type of partition 2 to 82 (Linux swap / Solaris)

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.
root@ubuntu:~# 
</pre>
<p>Reboot the virtual machine from within:</p>
<pre class="brush: plain; gutter: false; light: true; title: ; notranslate">
root@ubuntu:~# shutdown -r now
</pre>
<p>The swap partition will be mounted using an UUID identifier. After creating a new partition for swap the UUID will not match and upon reboot, no swap will be available. At this point two possibilites are available: Either enter the new UUID into the /etc/fstab or just &#8220;slap&#8221; the old UUID onto the new partition. I chose the latter. </p>
<p>For some odd/strange reason, Ubuntu actually finds the swap after reboot, although the UUID does not (yet) match. Testing this on a plain Debian system did not have the same results. My wild guess is that it did somehow find the &#8220;chunk&#8221; of swap space within the new /dev/sda1 and used that. As this could not have been used by data anyway, no damage should occur but in any case setting up swap properly is the way to go.</p>
<p>The awk command is to show the UUID which is used for mkswap.</p>
<p>The dd is just to make sure that there is no data (usable by the kernel) at the beginning of the partition.</p>
<pre class="brush: plain; gutter: false; light: true; title: ; notranslate">
cruz@ubuntu:~$ sudo bash
[sudo] password for cruz: 
root@ubuntu:~#  awk '/swap/ { print $1 }' /etc/fstab
#
UUID=8bb62351-4436-47df-92fe-af2865f03461
root@ubuntu:~# swapoff -a
root@ubuntu:~# free -m
             total       used       free     shared    buffers     cached
Mem:           992        695        296          0         23        325
-/+ buffers/cache:        346        645
Swap:            0          0          0
root@ubuntu:~# dd if=/dev/zero of=/dev/sda2
dd: writing to `/dev/sda2': No space left on device
1046529+0 records in
1046528+0 records out
535822336 bytes (536 MB) copied, 11.9388 s, 44.9 MB/s
root@ubuntu:~# mkswap -U 8bb62351-4436-47df-92fe-af2865f03461 /dev/sda2
Setting up swapspace version 1, size = 523260 KiB
no label, UUID=8bb62351-4436-47df-92fe-af2865f03461
root@ubuntu:~# swapon -a
root@ubuntu:~# free -m
             total       used       free     shared    buffers     cached
Mem:           992        693        298          0         23        325
-/+ buffers/cache:        345        646
Swap:          510          7        503
root@ubuntu:~# 
</pre>
<p>The final and easiest step is to resize the filesystem :)</p>
<pre class="brush: plain; gutter: false; light: true; title: ; notranslate">
root@ubuntu:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       9.0G  2.8G  5.8G  33% /
udev            488M  4.0K  488M   1% /dev
tmpfs           199M  788K  198M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            497M  200K  496M   1% /run/shm
root@ubuntu:~# resize2fs /dev/sda1
resize2fs 1.42 (29-Nov-2011)
Filesystem at /dev/sda1 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
Performing an on-line resize of /dev/sda1 to 3801088 (4k) blocks.
The filesystem on /dev/sda1 is now 3801088 blocks long.

root@ubuntu:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        15G  2.8G   11G  21% /
udev            488M  4.0K  488M   1% /dev
tmpfs           199M  788K  198M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            497M  200K  496M   1% /run/shm
root@ubuntu:~# 
</pre>
<p>Happy upgrading :)</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/hexeract.wordpress.com/491/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/hexeract.wordpress.com/491/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=491&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://hexeract.wordpress.com/2012/04/30/how-to-expand-the-root-filesystem-of-a-11-10-ubuntu-running-inside-vmware-player/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/290abdb318ff76c5a995d01d90e722fc?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hexeract</media:title>
		</media:content>
	</item>
		<item>
		<title>Example on how to use jquery.dataTables and jquery.jeditable</title>
		<link>http://hexeract.wordpress.com/2012/03/16/example-on-how-to-use-jquery-datatables-and-jquery-jeditable/</link>
		<comments>http://hexeract.wordpress.com/2012/03/16/example-on-how-to-use-jquery-datatables-and-jquery-jeditable/#comments</comments>
		<pubDate>Fri, 16 Mar 2012 15:24:47 +0000</pubDate>
		<dc:creator>hexeract</dc:creator>
				<category><![CDATA[howto]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[in-place editing]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[jquery-ui]]></category>
		<category><![CDATA[jquery.dataTables.editable]]></category>
		<category><![CDATA[jquery.jeditable]]></category>
		<category><![CDATA[jquery.validate /jquery.dataTables]]></category>
		<category><![CDATA[searchable]]></category>
		<category><![CDATA[table]]></category>

		<guid isPermaLink="false">http://hexeract.wordpress.com/?p=480</guid>
		<description><![CDATA[Recently I was asked to help on a private project of making a database accessible and searchable in a HTML frontend using HTML tables. The database was migrated from Microsoft Access using mdbtools, although you could export the Microsoft Access database to Microsoft Excel and use phpMyAdmin to import the data. Depending on the target [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=480&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Recently I was asked to help on a private project of making a database accessible and searchable in a HTML frontend using HTML tables. The database was migrated from Microsoft Access using <a title="mdbtools" href="http://mdbtools.sourceforge.net/" target="_blank">mdbtools</a>, although you could export the Microsoft Access database to Microsoft Excel and use <a title="phpmyadmin" href="http://www.phpmyadmin.net/home_page/index.php" target="_blank">phpMyAdmin</a> to import the data. Depending on the target environment each method has its advantages.</p>
<p>For this example, the database will be set up manually and with only a few columns although the examples shown here should work with a lot more columns too.</p>
<p>Spoiler: This posting contains a ton of PHP source code.</p>
<p>Disclaimer: I am not a PHP programmer and the examples provided are written to the best of my PHP programming abilities. Even worse, I can not program Javascript and all Javascript shown was copied/modified from the respective documentation/examples of the Javascript libraries. Also, I have not implemented any type of security against MySQL injection attacks, this is left as an excersise to the reader :)</p>
<p>&nbsp;</p>
<p>Create the database, the table and the user for the table:</p>
<pre class="brush: plain; light: true; title: ; wrap-lines: false; notranslate">
create database mybooks;
use mybooks;
create table books ( id INT auto_increment not NULL, primary key (id), surname TINYTEXT, name TINYTEXT, title TINYTEXT, published INT, whenread INT);
grant all privileges on mybooks.* TO nabaat@localhost identified by 'serenity';
flush privileges;
</pre>
<p>&nbsp;</p>
<p>What you should see now is:</p>
<pre class="brush: plain; light: true; title: ; wrap-lines: false; notranslate">
describe books;
+-----------+----------+------+-----+---------+----------------+
| Field     | Type     | Null | Key | Default | Extra          |
+-----------+----------+------+-----+---------+----------------+
| id        | int(11)  | NO   | PRI | NULL    | auto_increment |
| surname   | tinytext | YES  |     | NULL    |                |
| name      | tinytext | YES  |     | NULL    |                |
| title     | tinytext | YES  |     | NULL    |                |
| published | int(11)  | YES  |     | NULL    |                |
| whenread  | int(11)  | YES  |     | NULL    |                |
+-----------+----------+------+-----+---------+----------------+
</pre>
<p>&nbsp;</p>
<p>If you want, you could add &#8220;comments&#8221;, &#8220;rating&#8221;, &#8220;genre&#8221; or &#8220;url&#8221; as columns.</p>
<p>Time to fill the database with content.</p>
<pre class="brush: plain; light: true; title: ; wrap-lines: false; notranslate">
insert into books (surname, name, title, published, whenread) values (&quot;Asimov&quot;, &quot;Isaac&quot;, &quot;Nightfall&quot;, &quot;1941&quot;, &quot;1999&quot;);
insert into books (surname, name, title, published, whenread) values (&quot;Barker&quot;, &quot;Clive&quot;, &quot;Weaveworld&quot;, &quot;1987&quot;, &quot;1994&quot;);
insert into books (surname, name, title, published, whenread) values (&quot;Heinlein&quot;, &quot;Robert A.&quot;, &quot;Starship Troopers&quot;, &quot;1959&quot;, &quot;2000&quot;);
insert into books (surname, name, title, published, whenread) values (&quot;Stephenson&quot;, &quot;Neal&quot;, &quot;Snow Crash&quot;, &quot;1992&quot;, &quot;2004&quot;);
insert into books (surname, name, title, published, whenread) values (&quot;Suarez&quot;, &quot;Daniel&quot;, &quot;Daemon&quot;, &quot;2006&quot;, &quot;2010&quot;);
insert into books (surname, name, title, published, whenread) values (&quot;Bamford&quot;, &quot;James&quot;, &quot;The Puzzle Palace&quot;, &quot;1982&quot;, &quot;1990&quot;);
</pre>
<p>At this point we have a nice database table, some content and all we are missing is a nice front end to display the data. To test if things really work, the first step is to create a simple PHP script to display the contents of the books table in a simple HTML table.</p>
<p>The simple PHP HTML table script &#8216;books_list.php&#8217;:</p>
<pre class="brush: php; light: true; title: ; wrap-lines: false; notranslate">
&lt;?php

// The HTML header
echo '
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;Books&lt;/p&gt;
&lt;table cellpadding=&quot;3&quot; cellspacing=&quot;3&quot; border=&quot;0&quot; class=&quot;display&quot; id=&quot;example&quot;&gt;
';

// Database access data
$database=&quot;mybooks&quot;;
$host=&quot;localhost&quot;;
$user=&quot;nabaat&quot;;
$password=&quot;serenity&quot;;
$table=&quot;books&quot;;

// Database access
mysql_connect($host,$user,$password);
mysql_select_db($database) or die( &quot;Unable to select database&quot;);

// HTML table header 
echo '
&lt;thead align=&quot;left&quot;&gt;
&lt;tr&gt;
';

// Obtain the column names from the table 
$query  = &quot;show columns from $table&quot;;
$result = mysql_query($query);

// And print them into a theader structure 
while ( $row = mysql_fetch_assoc($result) )
{
    $fieldname = $row['Field'];

    // No need to display the 'id' field 
    if ( $fieldname == 'id' ) continue; 

    echo &quot;&lt;th&gt;$fieldname&lt;/th&gt;&quot;;
}

// HTML table header end, begin table body
echo '
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
';

// And now for the actual content 
$query  = &quot;select * from $table&quot;;
$result = mysql_query($query);

// And print the content into the tbody structure 
while ( $row = mysql_fetch_assoc($result) )
{

    // Remove the id column 
    unset($row['id']);

    echo &quot;&lt;tr&gt;&quot;;
    foreach ($row as $key =&gt; $value)
    {
        echo &quot;&lt;td&gt;$value&lt;/td&gt;&quot;;
    }
    echo &quot;&lt;/tr&gt;&quot;;
}

// HTML table body end 
echo ' 
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
';
?&gt;
</pre>
<p>Not very pretty but it works. The list is not big so you could use the browser search function to jump to a specific entry. But you could also provide a search box for a user to type into and this is where <a title="jQuery" href="http://jquery.com/" target="_blank">jQuery</a> and the jQuery plugin comes into play. This is the script:</p>
<p>Using jquery and jquery.dataTables &#8216;books_list_js.php&#8217;:</p>
<pre class="brush: php; collapse: true; light: false; title: ; toolbar: true; wrap-lines: false; notranslate">
&lt;?php

echo '
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;

&lt;title&gt;My Books&lt;/title&gt;
&lt;style type=&quot;text/css&quot; title=&quot;currentStyle&quot;&gt;
    @import &quot;http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/css/jquery.dataTables.css&quot;;
&lt;/style&gt;

&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/jquery.dataTables.js&quot;&gt;&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;
$(document).ready(function() {
    $(\'#example\').dataTable( {
        &quot;bPaginate&quot;: false,
    } );
} );

&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;
&lt;p&gt;Books&lt;/p&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; border=&quot;0&quot; class=&quot;display&quot; id=&quot;example&quot;&gt;
';


$database=&quot;mybooks&quot;;
$host=&quot;localhost&quot;;
$user=&quot;nabaat&quot;;
$password=&quot;serenity&quot;;
$table=&quot;books&quot;;

mysql_connect($host,$user,$password);
mysql_select_db($database) or die( &quot;Unable to select database&quot;);

echo '
&lt;thead&gt;
&lt;tr&gt;
';

$query  = &quot;show columns from $table&quot;;
$result = mysql_query($query);

while ( $row = mysql_fetch_assoc($result) )
{
    $fieldname = $row['Field'];
    if ( $fieldname == 'id' ) continue; 
    echo &quot;&lt;th&gt;$fieldname&lt;/th&gt;&quot;;
}

echo '
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
';

$query  = &quot;select * from $table&quot;;
$result = mysql_query($query);

while ( $row = mysql_fetch_assoc($result) )
{
    unset($row['id']);
    echo &quot;&lt;tr&gt;&quot;;
    foreach ($row as $key =&gt; $value) 
    { 
        echo &quot;&lt;td&gt;$value&lt;/td&gt;&quot;;
    }
    echo &quot;&lt;/tr&gt;&quot;;
}

echo ' 
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
';
?&gt;
</pre>
<p>This is basically the same PHP script with the addition of some Javascript (jquery and jquery.dataTables). Please note that the table has an id called &#8216;example&#8217; which is used to tell jquery.DataTable on which table to work on. If all went well you should be seeing quite a different table with a search box you can use to search for any entry.</p>
<p>This is nice, especially if you have many more (and similar) entries. But wouldn&#8217;t it be nice to be able to edit an entry right there, with a click of the mouse button? This is where <a title="jquery.jeditable" href="http://www.appelsiini.net/projects/jeditable" target="_blank">jquery.jeditable</a> comes to the rescue. Although this requires a little more work and an additional PHP script (to update the database entry).</p>
<p>This is how the frontend looks like. Again, the same basic script, just a lot more data in the header. Also, another PHP script is referenced named &#8216;books_edit.php&#8217;.</p>
<p>&#8216;books_edit_js.php&#8217;:</p>
<pre class="brush: php; collapse: true; light: false; title: ; toolbar: true; wrap-lines: false; notranslate">
&lt;?php

echo '
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;

&lt;title&gt;Books&lt;/title&gt;
&lt;style type=&quot;text/css&quot; title=&quot;currentStyle&quot;&gt;
    @import &quot;http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/css/jquery.dataTables.css&quot;;
&lt;/style&gt;

&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/jquery.dataTables.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://www.appelsiini.net/download/jquery.jeditable.mini.js&quot;&gt;&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot;&gt;

$(document).ready(function() {

 $(\'#example\').dataTable( {
        &quot;bPaginate&quot;: false,
        &quot;bStateSave&quot;: true
    } );

    /* Init DataTables */
    var oTable = $(\'#example\').dataTable();
     
    /* Apply the jEditable handlers to the table */
    oTable.$(\'td\').editable( \'./books_edit.php\', {
        &quot;callback&quot;: function( sValue, y ) {
            var aPos = oTable.fnGetPosition( this );
            oTable.fnUpdate( sValue, aPos[0], aPos[1] );
            window.location.reload();
        },
        &quot;submitdata&quot;: function ( value, settings ) {
            return {
                &quot;row_id&quot;: this.parentNode.getAttribute(\'id\'),
                &quot;column&quot;: oTable.fnGetPosition( this )[2]
            };
        },
        &quot;height&quot;: &quot;14px&quot;,
        &quot;width&quot;: &quot;100%&quot;
    } );
} );


&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;
&lt;p&gt;Books&lt;/p&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; border=&quot;0&quot; class=&quot;display&quot; id=&quot;example&quot;&gt;
';

$database=&quot;mybooks&quot;;
$host=&quot;localhost&quot;;
$user=&quot;nabaat&quot;;
$password=&quot;serenity&quot;;
$table=&quot;books&quot;;

mysql_connect($host,$user,$password);
mysql_select_db($database) or die( &quot;Unable to select database&quot;);

echo '
&lt;thead&gt;
&lt;tr&gt;
';

$query  = &quot;show columns from $table&quot;;
$result = mysql_query($query);

while ( $row = mysql_fetch_assoc($result) )
{
    $fieldname = $row['Field'];
    if ( $fieldname == 'id' ) continue; 
    echo &quot;&lt;th&gt;$fieldname&lt;/th&gt;&quot;;
}

echo '
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
';

$query  = &quot;select * from $table&quot;;
$result = mysql_query($query);

while ( $row = mysql_fetch_assoc($result) )
{

    $id = $row['id'];
    echo &quot;&lt;tr id=\&quot;$id\&quot;&gt;&quot;;
   
    unset($row['id']);

    foreach ($row as $key =&gt; $value)
    {
        echo &quot;&lt;td&gt;$value&lt;/td&gt;&quot;;
    }
    echo &quot;&lt;/tr&gt;&quot;;
}



echo ' 
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
';

?&gt;
</pre>
<p>And this is the corresponding &#8216;books_edit.php&#8217; script:</p>
<pre class="brush: php; collapse: true; light: false; title: ; toolbar: true; wrap-lines: false; notranslate">
&lt;?php

// This script gets data via POST from books_edit_js.php

// The POST request supplies the following data:
//
// row_id   =&gt; corresponds to the id in the database table 
// value    =&gt; the actual (changed) content to be written 
// column   =&gt; which column number(!) 

$database=&quot;mybooks&quot;;
$host=&quot;localhost&quot;;
$user=&quot;nabaat&quot;;
$password=&quot;serenity&quot;;
$table=&quot;books&quot;;

mysql_connect($host,$user,$password);
mysql_select_db($database) or die( &quot;Unable to select database&quot;);

$rawdata    = $_POST;

// as only the column number is provided
// map the column number to the column name 
$query      = &quot;show columns from $table&quot;;
$result=mysql_query($query);

// grab the field names and pack them into an array
$fields     = array();
while ( $row = mysql_fetch_assoc($result) )
{
            $feldname = $row['Field'];
            array_push($fields, $feldname);
}

// Grab the data from the $_POST request
$id             = $rawdata['row_id'];
$value          = $rawdata['value'];
$column         = $rawdata['column'];

// As id was &quot;stripped&quot; from the first table
// The index to the column name in the array will be wrong
// We need to add 1 to the column number
$column         = $column + 1;

// Get the column name by index number
$fieldname      = $fields[$column];

// The database was created in ISO-8859-x
// so better convert the UTF-8 input from the browser 
$value = utf8_decode($value);

$query  = &quot;update $table set $fieldname = '$value' where id = '$id'&quot;;
$result = mysql_query($query);

// Provide feedback to the entry field 
if (!$result) { echo &quot;Update failed&quot;; }
else          { echo &quot;UPD: $value&quot;; }

// Close the connection 
mysql_close();
?&gt;
</pre>
<p>So far, so good. The table contents can now be edited. But how about adding or deleting entries?</p>
<p>This time, four files are required:</p>
<ul>
<li>books_manip_js.php</li>
<li>books_edit.php (from the edit only version, see above)</li>
<li>add.php</li>
<li>delete.php</li>
</ul>
<p>So here are three new files:</p>
<p>&#8216;books_manip_js.php&#8217;:</p>
<pre class="brush: php; collapse: true; light: false; title: ; toolbar: true; wrap-lines: false; notranslate">
&lt;?php

echo '
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&gt;
&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;
&lt;head&gt;

&lt;title&gt;Books&lt;/title&gt;
&lt;style type=&quot;text/css&quot; title=&quot;currentStyle&quot;&gt;
    @import &quot;http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/css/jquery.dataTables.css&quot;;
    @import &quot;http://jquery-datatables-editable.googlecode.com/svn/trunk/media/css/themes/base/jquery-ui.css&quot;;
&lt;/style&gt;

&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.5/jquery-ui.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.aspnetcdn.com/ajax/jquery.validate/1.9/jquery.validate.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/jquery.dataTables.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://jquery-datatables-editable.googlecode.com/svn-history/r122/trunk/media/js/jquery.dataTables.editable.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; language=&quot;javascript&quot; src=&quot;http://www.appelsiini.net/download/jquery.jeditable.mini.js&quot;&gt;&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot;&gt;

$(document).ready(function() {

 $(\'#example\').dataTable( {
        &quot;bPaginate&quot;: false,
        &quot;bStateSave&quot;: true
    } );

    /* Init DataTables */
    var oTable = $(\'#example\').dataTable();
     
    /* Apply the jEditable handlers to the table */
    oTable.$(\'td\').editable( \'./books_edit.php\', {
        &quot;callback&quot;: function( sValue, y ) {
            var aPos = oTable.fnGetPosition( this );
            oTable.fnUpdate( sValue, aPos[0], aPos[1] );
            window.location.reload();
        },
        &quot;submitdata&quot;: function ( value, settings ) {
            return {
                &quot;row_id&quot;: this.parentNode.getAttribute(\'id\'),
                &quot;column&quot;: oTable.fnGetPosition( this )[2]
            };
        },
        &quot;height&quot;: &quot;14px&quot;,
        &quot;width&quot;: &quot;100%&quot;
    } );

 $(\'#example\').dataTable().makeEditable({
        &quot;sAddURL&quot;:              &quot;./add.php&quot;,
        &quot;sDeleteURL&quot;:           &quot;./delete.php&quot;,
        &quot;sAddNewRowFormId&quot;:     &quot;addme&quot;,
    } ); 
} );

&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;
&lt;p&gt;Books&lt;/p&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; border=&quot;0&quot; class=&quot;display&quot; id=&quot;example&quot;&gt;
';

$database=&quot;mybooks&quot;;
$host=&quot;localhost&quot;;
$user=&quot;nabaat&quot;;
$password=&quot;serenity&quot;;
$table=&quot;books&quot;;

mysql_connect($host,$user,$password);
mysql_select_db($database) or die( &quot;Unable to select database&quot;);

echo '
&lt;thead&gt;
&lt;tr&gt;
';

$query  = &quot;show columns from $table&quot;;
$result = mysql_query($query);


while ( $row = mysql_fetch_assoc($result) )
{
    $fieldname = $row['Field'];
    if ( $fieldname == 'id' ) continue; 
    echo &quot;&lt;th&gt;$fieldname&lt;/th&gt;&quot;;
}

echo '
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
';

$query  = &quot;select * from $table&quot;;
$result = mysql_query($query);

while ( $row = mysql_fetch_assoc($result) )
{

    $id = $row['id'];
    echo &quot;&lt;tr id=\&quot;$id\&quot;&gt;&quot;;
   
    unset($row['id']);

    foreach ($row as $key =&gt; $value)
    {
        echo &quot;&lt;td&gt;$value&lt;/td&gt;&quot;;
    }
    echo &quot;&lt;/tr&gt;&quot;;
}



echo ' 
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&quot;add_delete_toolbar&quot; /&gt;

&lt;form id=&quot;addme&quot; action=&quot;#&quot; title=&quot;Add new record&quot;&gt;

    &lt;label for=&quot;surname&quot;&gt;Surname&lt;/label&gt;&lt;br /&gt;
    &lt;input type=&quot;text&quot; name=&quot;surname&quot; id=&quot;surname&quot; class=&quot;required&quot; rel=&quot;0&quot; /&gt;
    &lt;br /&gt;

    &lt;label for=&quot;name&quot;&gt;Name&lt;/label&gt;&lt;br /&gt;
    &lt;input type=&quot;text&quot; name=&quot;name&quot; id=&quot;name&quot; class=&quot;required&quot; rel=&quot;1&quot; /&gt;
    &lt;br /&gt;

    &lt;label for=&quot;title&quot;&gt;Title&lt;/label&gt;&lt;br /&gt;
    &lt;input type=&quot;text&quot; name=&quot;title&quot; id=&quot;title&quot; class=&quot;required&quot; rel=&quot;2&quot; /&gt;
    &lt;br /&gt;

    &lt;label for=&quot;published&quot;&gt;Published&lt;/label&gt;&lt;br /&gt;
    &lt;input type=&quot;text&quot; name=&quot;published&quot; id=&quot;published&quot; rel=&quot;3&quot; /&gt;
    &lt;br /&gt;

    &lt;label for=&quot;whenread&quot;&gt;When Read&lt;/label&gt;&lt;br /&gt;
    &lt;input type=&quot;text&quot; name=&quot;whenread&quot; id=&quot;whenread&quot; rel=&quot;4&quot; /&gt;
    &lt;br /&gt;

&lt;/form&gt;

&lt;/body&gt;
&lt;/html&gt;
';

?&gt;
</pre>
<p>&#8216;add.php&#8217;:</p>
<pre class="brush: php; collapse: true; light: false; title: ; toolbar: true; wrap-lines: false; notranslate">
&lt;?php

$database=&quot;mybooks&quot;;
$host=&quot;localhost&quot;;
$user=&quot;nabaat&quot;;
$password=&quot;serenity&quot;;
$table=&quot;books&quot;;

mysql_connect($host,$user,$password);
mysql_select_db($database) or die( &quot;Unable to select database&quot;);


file_put_contents('test.txt', file_get_contents('php://input'));

$surname    = $_POST['surname'];
$name       = $_POST['name'];
$title      = $_POST['title'];
$published  = $_POST['published'];
$whenread   = $_POST['whenread'];

$query = &quot;insert into $table (surname, name, title, published, whenread) values (\&quot;$surname\&quot;, \&quot;$name\&quot;, \&quot;$title\&quot;, \&quot;$published\&quot;, \&quot;$whenread\&quot;)&quot;;

$result = mysql_query($query);

if (!$result) { echo &quot;Insert failed&quot;; }
else          { echo &quot;ok&quot;; }

mysql_close();
?&gt;
</pre>
<p>&#8216;delete.php&#8217;:</p>
<pre class="brush: php; collapse: true; light: false; title: ; toolbar: true; wrap-lines: false; notranslate">
&lt;?php

$database=&quot;mybooks&quot;;
$host=&quot;localhost&quot;;
$user=&quot;nabaat&quot;;
$password=&quot;serenity&quot;;
$table=&quot;books&quot;;

mysql_connect($host,$user,$password);
mysql_select_db($database) or die( &quot;Unable to select database&quot;);

$id         = $_POST['id'];

$query      = &quot;delete from $table where id=$id&quot;;
$result     = mysql_query($query);


if (!$result) { echo &quot;Delete failed: $query&quot;; }
else          { echo &quot;ok&quot;; }

mysql_close();
?&gt;
</pre>
<p>At this stage you should have a nice looking, searchable table with in-place editing and being able to add and delete rows. Also, there are quite a few javascript libraries referenced in books_manip_js.php and also two CSS files. I hope the comments within the PHP scripts help to understand what is being done.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/hexeract.wordpress.com/480/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/hexeract.wordpress.com/480/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=480&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://hexeract.wordpress.com/2012/03/16/example-on-how-to-use-jquery-datatables-and-jquery-jeditable/feed/</wfw:commentRss>
		<slash:comments>82</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/290abdb318ff76c5a995d01d90e722fc?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hexeract</media:title>
		</media:content>
	</item>
		<item>
		<title>How to query software program vendor websites for the program version using ruby</title>
		<link>http://hexeract.wordpress.com/2011/07/17/how-to-query-software-program-vendor-websites-for-the-program-version-using-ruby/</link>
		<comments>http://hexeract.wordpress.com/2011/07/17/how-to-query-software-program-vendor-websites-for-the-program-version-using-ruby/#comments</comments>
		<pubDate>Sun, 17 Jul 2011 10:37:59 +0000</pubDate>
		<dc:creator>hexeract</dc:creator>
				<category><![CDATA[howto]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[mechanize]]></category>
		<category><![CDATA[nokogiri]]></category>
		<category><![CDATA[source]]></category>
		<category><![CDATA[sqlite3]]></category>
		<category><![CDATA[threads]]></category>

		<guid isPermaLink="false">http://hexeract.wordpress.com/?p=412</guid>
		<description><![CDATA[This is a followup to my previous article. The example in the previous post only checked three programs, but with more entries the overall execution time increases. With about 36 entries the execution time is around 20-40 seconds, depending on how fast the websites answer as each website is being accessed in sequence and waiting [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=412&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>This is a followup to my previous <a href="http://hexeract.wordpress.com/2011/07/15/how-to-keep-up-with-new-versions-of-programs/" title="How to keep up with new versions of programs" target="_blank">article</a>.</p>
<p>The example in the previous post only checked three programs, but with more entries the overall execution time increases. With about 36 entries the execution time is around 20-40 seconds, depending on how fast the websites answer as each website is being accessed in sequence and waiting for each request to complete. This can and will add up. Additionally running the various unix commands in the pipe will not help towards overall efficiency. </p>
<p>So I decided to give it a try to make the script better and a hopefully a lot faster, especially when more websites are being accessed. My first thought was to try it with perl but parsing HTML with perl never quite worked for me when I wrote some test scripts. This may be due to me being inapt using HTML::TreeBuilder and/or XML::XPath. In any case, I was getting nowhere&#8230;</p>
<p>The second choice of a scripting language to use was ruby. As I already had done some website scraping using the ruby gems <a href="http://rubyforge.org/projects/mechanize/" title="RubyForge: Mechanize: Project Info" target="_blank">mechanize</a>, <a href="http://nokogiri.org/" title="An HTML, XML, SAX, &amp; Reader parser" target="_blank">nokogiri</a> and <a href="https://github.com/hpricot/hpricot/wiki" title="A Fast, Enjoyable HTML Parser for Ruby" target="_blank">hpricot</a> I thought trying to convert the shell script into a ruby script would be easy. Little did I know :)</p>
<p>Despite the fact it only accesses some websites and stores the version number (and the program name) in a sqlite3 database I hope it can serve as an example on how to use threads, mechanize, nokogiri, sqlite3 and how to pass code to a function.</p>
<p>Adding more checks does not increase the overall runtime too much. A test with 36 entries results in an overall runtime between two to five seconds. </p>
<p>Finding the actual xpath is not that easy, <a href="https://addons.mozilla.org/de/firefox/addon/firebug/" title="Firebug Firefox Addon" target="_blank">firebug</a> (Firefox) or the Developer Tools from Google Chrome can provide helpful hints but this is best tested in a small test script where also the version extraction ruby code can be written.</p>
<p>XPath and version extraction test script (example Metapad):</p>
<pre class="brush: ruby; auto-links: false; collapse: true; light: false; title: ; toolbar: true; notranslate">
#!/usr/bin/ruby

require 'rubygems'
require 'mechanize'
require 'nokogiri'

# exit upon control-c
trap(&quot;INT&quot;) { exit 1 } 

# site data
url   = 'http://liquidninja.com/metapad/download.html'
name  = 'MetaPad'
xpath = '/html/body/table/tr/td/table/tr/td/table/tr/td/table/tr/td'

# and access the site 
agent                  = Mechanize.new
agent.history.max_size = 0 
agent.user_agent_alias = 'Mac Safari'
agent.read_timeout     = 3 
page                   = agent.get(url)
found                  = page.search(xpath)

# this is to make it easier to just &quot;drop&quot; in the source code
# into the &quot;real script&quot;.
data = found

# display the &quot;elements&quot; we have hit to see if we are &quot;close&quot; with the xpath
    data.each do |node|
        puts &quot;-- NODE --&quot;
        puts node
    end
    puts &quot;--- OUTPUT ---&quot;

##### version extraction code 

versions = Array.new

data.each  do |node|
   versions.push(node.text) if node.to_s.include?('Version')
end

data = versions.first
data = data.strip.gsub!(/Version /, '');

#### version extraction code end
# assign the result back to 'result' 
result = data

# and print what we have
puts &quot;#{name}: #{result}&quot;
exit 0
</pre>
<p>&nbsp;<br />
The sqlite3 database table</p>
<pre class="brush: plain; auto-links: false; title: ; wrap-lines: false; notranslate">
sqlite&gt; .schema win32
CREATE TABLE win32 (program varchar(30) PRIMARY KEY, cver TEXT, pver TEXT, err INT default '0');
sqlite&gt; 
</pre>
<p>Prior to writing to the database, an entry for the program must exist. This is an example on how to create an entry:</p>
<pre class="brush: plain; auto-links: false; title: ; wrap-lines: false; notranslate">
sqlite&gt; insert into win32 (program) values ('MetaPad');
sqlite&gt; select * from win32 where program='MetaPad';
MetaPad|||0
sqlite&gt; 
</pre>
<p>&nbsp;</p>
<p>And here is the final ruby script that checks three vendor websites and writes to the sqlite3 database:</p>
<pre class="brush: ruby; auto-links: false; title: ; wrap-lines: false; notranslate">
#!/usr/bin/ruby

require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'sqlite3'
require 'socket'

# exit upon control-c
trap(&quot;INT&quot;) { exit 1 }

# Setup the database
dbfile  = 'win32.db'
dbtable = 'win32'

# no database, no party
if not File.exists?(dbfile)
    puts &quot;Error, could not find database: #{dbfile}&quot;
    exit 10
end

# connect to the database
database = SQLite3::Database.new(dbfile)

# This hash will store the program name
# and its (scraped) version string
pv = Hash.new

# Using threads to make website scraping concurrent (and thus faster)
mythreads = Array.new

# This is the core routine of the script
# It takes three(!) arguments:
#  url:      What URL to access/get HTML from
#  xpath:    Using nokogiri, &quot;extract&quot; only a part of the HTML content
#  CODE:     The source code to parse the part of the HTML content to obtain
#            the version string

def getversion(url,xpath)
    # Set the default return value to ''
    result = ''

    # TCP connection check, host/service available?
    host   = url.split('/')
    host   = host[2]
    socket = TCPSocket.open(host, '80')

    # No socket, no access
    begin 
        socket = TCPSocket.open(host, '80')
    rescue  
        return result 
    end
    socket.close

    # This sets up a simple web client
    agent                  = Mechanize.new
    agent.history.max_size = 0
    agent.user_agent_alias = 'Mac Safari'
    agent.read_timeout     = 3
    agent.keep_alive       = false

    # Try and download the page
    # If something goes wrong, result will not have changed
    # and thus will be '' which will force the error counter to be increased
    begin
        page                   = agent.get url 
    rescue Mechanize::ResponseCodeError
        return result
    end

    # Parse the content using the xpath and nokogiri
    # which is accessed via the mechanize method .search 
    found                  = page.search xpath

    # If the array (of nokogiri) objects is not empty
    # execute the provided code block to obtain the version number
    if not found.empty?
    then
        result = yield found if block_given?
    end

    return result
end

###
# Metapad
t = Thread.new {
    url    = 'http://liquidninja.com/metapad/download.html'
    name   = 'MetaPad'
    xpath  = '/html/body/table/tr/td/table/tr/td/table/tr/td/table/tr/td'

# This is the call where url, xpath and some code is being supplied to the function
# The code passed uses the 'data' object which is the one also being 'returned'
# to the subroutine when the code block finishes

    result = getversion( url, xpath ) { |data|
                                        versions = Array.new
                                        data.each  do |node|
                                            versions.push(node.text) if node.to_s.include?('Version')
                                        end
                                        data = versions.first
                                        data = data.strip.gsub!(/Version /, '')
                                      }
    pv[&quot;#{name}&quot;] = result
}
mythreads.push(t)

###
# Pidgin
t = Thread.new {
    name   = 'Pidgin'
    url    = 'http://developer.pidgin.im/wiki/ChangeLog'
    xpath  = '/html/body/div/div/div/div/div/h2'

# Simpler code to extract the version number
    result = getversion( url, xpath ) { |data|
                                        data = data.first.text
                                        data = data.split(' ')
                                        data = data[1]
                                      }
    pv[&quot;#{name}&quot;] = result
}
mythreads.push(t)

###
# Adobe Flash Player
t = Thread.new {
    url    = 'http://www.adobe.com/de/software/flash/about/'
    name   = 'Adobe Flash Plugin'
    xpath  = '/html/body/div/div/div/div/table/tbody/tr/td'

# Simplest version :)
    result = getversion( url, xpath ) { |data| data = data[5].text }
    pv[&quot;#{name}&quot;] = result
}
mythreads.push(t)

#### More entries could go here :)

# Collect all threads and wait, if necessary
mythreads.each { |t| t.join }

# Create a string of database commands by
# looping through the 'pv' hash and create the individual update commands for sqlite3
# Each command is one element of the 'sql' array
sql = Array.new

pv.each do |p,v|
    sql.push(&quot;update #{dbtable} set cver = '#{v}' where program = '#{p}';&quot;)
end

# Convert the 'sql' array to one big string and batch execute
database.execute_batch( sql.join(&quot;\n&quot;) )

# The cver column for each program has been updated
# What is left is to query the database and print the results, if there are any

# reset error counter when cver has a value
database.execute(&quot;update #{dbtable} set err = 0 WHERE cver != ''&quot;)

# Report if the err counter is &gt;= 5
# Rhis means 5 consecutive runs went 'wrong' and requires a check as to # what/why.
# For example, if the vendor changes the output/structure of the website...
data = database.execute2(&quot;select program AS ProgramName,err AS ErrorRuns from win32 where err &gt;= 5&quot;)

# When executing the method execute2 the table header is also returned
# this means, the data array will always contain one element
# It will contain a second (and more) element(s) if actual data is returned
if data[1]
then
    data.each do |line|
        printf(&quot;%-20s %-15s\n&quot;, line[0], line[1])
    end
end

# Increase the error counter if current version is empty
database.execute(&quot;update win32 set err = err  + 1 WHERE cver=''&quot;)

# Compare the current version with previous version
# Do not compare when current version is empty
data =  database.execute2(&quot;select program AS ProgramName,pver AS OldVersion,cver AS CurrentVersion from win32 where ( cver != pver and cver != '')&quot;)

# see remark about execute2 above
if data[1]
then
    data.each do |line|
        printf(&quot;%-20s %-15s %-15s\n&quot;, line[0], line[1], line[2])
    end
end

# Set the previous version to the value of current version
# Do not set if the current version is empty
database.execute(&quot;update win32 SET pver = cver WHERE ( pver != cver and cver != '')&quot;)

exit 0
</pre>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/hexeract.wordpress.com/412/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/hexeract.wordpress.com/412/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=412&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://hexeract.wordpress.com/2011/07/17/how-to-query-software-program-vendor-websites-for-the-program-version-using-ruby/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/290abdb318ff76c5a995d01d90e722fc?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hexeract</media:title>
		</media:content>
	</item>
		<item>
		<title>How to keep up with new versions of programs</title>
		<link>http://hexeract.wordpress.com/2011/07/15/how-to-keep-up-with-new-versions-of-programs/</link>
		<comments>http://hexeract.wordpress.com/2011/07/15/how-to-keep-up-with-new-versions-of-programs/#comments</comments>
		<pubDate>Fri, 15 Jul 2011 15:40:38 +0000</pubDate>
		<dc:creator>hexeract</dc:creator>
				<category><![CDATA[howto]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[scraping]]></category>
		<category><![CDATA[sqlite3]]></category>
		<category><![CDATA[website]]></category>

		<guid isPermaLink="false">http://hexeract.wordpress.com/?p=363</guid>
		<description><![CDATA[Keeping up with versions on your computer can be quite tedious, especially if those programs do not provide an auto-update feature. You could visit the vendor homepage for each software product you intend to keep up to date and check manually or use a program and hope it knows how to check for updates for [&#8230;]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=363&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Keeping up with versions on your computer can be quite tedious, especially if those programs do not provide an auto-update feature. You could visit the vendor homepage for each software product you intend to keep up to date and check manually or use a program and hope it knows how to check for updates for your software and last but not least visit a website regularly showing newly released/updated software.</p>
<p>Or you can build something yourself.</p>
<p>The example used here is awful and I am sure there a a million better ways to scrape websites (and as you will see later, can be easily replaced by something else) but should suffice to demonstrate on how to keep track of versions of programs you deem important.</p>
<p>Shown below is a method on how to scrape version information from vendor websites using curl and the usage of some unix commands to modify output from curl. If you want to know what each part in the pipe does just start with the initial command, look at the output and add a step at a time.</p>
<p>Example:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
#!/bin/bash
echo -n &quot;Metapad: &quot;
curl -s -m 3 http://liquidninja.com/metapad/download.html | grep -A 5 &quot;Latest Release&quot;  | tail -1 | sed &quot;s/Version //g&quot;
echo -n &quot;Pidgin: &quot;
curl -s -m 3 http://developer.pidgin.im/wiki/ChangeLog | grep version | head -2 | tail -1 | perl -p -i -e &quot;s/&lt;.*?&gt;//g,&quot;  | awk '{ print $2 }'
echo -n &quot;Adobe Flash Plugin: &quot;
curl -s -m 3 http://www.adobe.com/de/software/flash/about/ | grep -A 2 &quot;Firefox, Mozilla, Netscape, Opera&quot;  | tail -1 | perl -p -i -e &quot;s/&lt;.*?&gt;//g,&quot; | awk '{ print $1 }'
</pre>
<p>&nbsp;<br />
For the purpose of this article, the amount of checks is limited to three. Most likely you want to keep track of versions for more than three programs. Even if one assumes that you know the versions of all programs on your computer off by heart and you always install the latest software when available it is still easy to miss if an update is available just by looking at the script output. Especially with a lot more than just three&#8230;</p>
<p>In this scenario it would be helpful if the check could remember the version for each program from the previous run. Again, like with the example above on how to scrape websites, there are a million ways to keep track of such data, may it be a flat file (with either shell wizardry in-place editing/comparisons or using a revision control system) or a full blown Oracle Database 11g.</p>
<p>As I only want to store four values (program name, current version, previous version, error count) I decided to use sqlite3. This allows for a future migration to a proper database if the need arises.</p>
<p>Creating a database with sqlite3:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; toolbar: true; wrap-lines: false; notranslate">
$ sqlite3 win32.db &quot;create table win32 (program varchar(30) PRIMARY KEY, cver TEXT, pver TEXT, err INT default '0');&quot;
</pre>
<p>&nbsp;<br />
Now, the database needs content. With just three examples, it would be faster to just manually enter the data.</p>
<p>Here are the individual commands:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 win32.db &quot;insert into win32 (program, cver, pver) values ('Metapad','3,6','3,6');&quot;
$ sqlite3 win32.db &quot;insert into win32 (program, cver, pver) values ('Pidgin','2.9','2.9');&quot;
$ sqlite3 win32.db &quot;insert into win32 (program, cver, pver) values ('Adobe Flash Plugin','10.3.181.34','10.3.181.34');&quot;
</pre>
<p>&nbsp;<br />
Checking if all data is in the database:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad     3,6         3,6         0         
Pidgin      2.9         2.9         0         
Adobe Flas  10.3.181.3  10.3.181.3  0  
</pre>
<p>&nbsp;<br />
As seen, the data is available. But as the check script requires the functionality to write to the sqlite3 database this can be exploited to fill the database with initial values.</p>
<p>At first, delete the database and recreate it from scratch:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ rm win32.db
$ sqlite3 win32.db &quot;create table win32 (program varchar(30) PRIMARY KEY, cver TEXT, pver TEXT, err INT default '0');&quot;
</pre>
<p>&nbsp;<br />
For the script to write to the database, a function will do fine. The function takes the name and version as arguments, strips ^M and leading/trailing whitespaces from the version string and then writes the data to the database. The following shows the script that will fill the database with initial values.</p>
<p>Example:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
#!/bin/bash

DATABASE=&quot;win32.db&quot;

if [ ! -e ${DATABASE} ]
then
    echo &quot;Error: Can not find sqlite3 Database to update/check&quot;
    exit 10
fi
function updatedb()
{
    name=$1
    version=$2
    sver=$(echo $version | tr -d '&#092;&#048;15' | sed 's/^[ \t]*//;s/[ \t]*$//')
    sqlite3 ${DATABASE} &quot;insert into win32 (program, cver, pver) values ('$name','$sver','$sver');&quot;
}

VERSION=$(curl -s -m 3 http://liquidninja.com/metapad/download.html | grep -A 5 &quot;Latest Release&quot;  | tail -1 | sed &quot;s/Version //g&quot;)
updatedb &quot;Metapad&quot; &quot;$VERSION&quot;

VERSION=$(curl -s -m 3 http://developer.pidgin.im/wiki/ChangeLog | grep version | head -2 | tail -1 | perl -p -i -e &quot;s/&lt;.*?&gt;//g,&quot;  | awk '{ print $2 }' )
updatedb &quot;Pidgin&quot; &quot;$VERSION&quot;

VERSION=$(curl -s -m 3 http://www.adobe.com/de/software/flash/about/ | grep -A 2 &quot;Firefox, Mozilla, Netscape, Opera&quot;  | tail -1 | perl -p -i -e &quot;s/&lt;.*?&gt;//g,&quot; | awk '{ print $1 }')
updatedb &quot;Adobe Flash Plugin&quot; &quot;$VERSION&quot;
</pre>
<p>Check if all the data has made it into the database as intended:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad     3,6         3,6         0         
Pidgin      2.9         2.9         0         
Adobe Flas  10.3.181.3  10.3.181.3  0 
</pre>
<p>&nbsp;<br />
As you can see, the &#8220;Adobe Flash Player&#8221; string looks truncated.</p>
<p>Better check that:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 -header -column win32.db &quot;select * from win32 where program like 'Adobe%';&quot;
program             cver         pver         err       
------------------  -----------  -----------  ----------
Adobe Flash Plugin  10.3.181.34  10.3.181.34  0
</pre>
<p>&nbsp;<br />
Only the output was truncated, not the actual value.</p>
<p>As the database now has an intial state, the script requires some modifications so it can use and update the database. The function updatedb only requires a minor modification but at the end of the script there are some sqlite3 commands that check the version (and failed checks) and outputs program names where there is an update available.</p>
<p>This is what the complete script looks like:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
#!/bin/bash

DATABASE=&quot;win32.db&quot;

if [ ! -e ${DATABASE} ]
then
    echo &quot;Error: Can not find sqlite3 Database to update/check&quot;
    exit 10
fi

function updatedb()
{
    name=$1
    version=$2
    sver=$(echo $version | tr -d '&#092;&#048;15' | sed 's/^[ \t]*//;s/[ \t]*$//')
    sqlite3 ${DATABASE} &quot;update win32 set cver='$sver' where program='$name';&quot;
}

VERSION=$(curl -s -m 3 http://liquidninja.com/metapad/download.html | grep -A 5 &quot;Latest Release&quot;  | tail -1 | sed &quot;s/Version //g&quot;)
updatedb &quot;Metapad&quot; &quot;$VERSION&quot;

VERSION=$(curl -s -m 3 http://developer.pidgin.im/wiki/ChangeLog | grep version | head -2 | tail -1 | perl -p -i -e &quot;s/&lt;.*?&gt;//g,&quot;  | awk '{ print $2 }' )
updatedb &quot;Pidgin&quot; &quot;$VERSION&quot;

VERSION=$(curl -s -m 3 http://www.adobe.com/de/software/flash/about/ | grep -A 2 &quot;Firefox, Mozilla, Netscape, Opera&quot;  | tail -1 | perl -p -i -e &quot;s/&lt;.*?&gt;//g,&quot; | awk '{ print $1 }')
updatedb &quot;Adobe Flash Plugin&quot; &quot;$VERSION&quot;


sqlite3                 ${DATABASE} &quot;update win32 set err = 0 WHERE cver != '';&quot;
sqlite3 -header -column ${DATABASE} &quot;select program AS ProgramName,err AS ErrorRuns from win32 where err &gt;= 5;&quot;
sqlite3                 ${DATABASE} &quot;update win32 set err = err  + 1 WHERE cver='';&quot;
sqlite3 -header -column ${DATABASE} &quot;select program AS ProgramName,pver AS OldVersion,cver AS CurrentVersion from win32 where ( cver != pver and cver != '');&quot;
sqlite3                 ${DATABASE} &quot;update win32 SET pver = cver WHERE ( pver != cver and cver != '' ); &quot;
</pre>
<p>The function updatedb was modified to update the current version column and five sqlite3 commands were added.</p>
<p>There is a fourth field in the database called &#8216;err&#8217; which is an error counter.  This is used to detect if the check for a version returns an empty string and for how many consecutive executions of the program.</p>
<p>Set the error counter to zero when the current version contains anything but an empty string.<br />
Display the program name and error counter if the error counter is equal or greater than five.<br />
Increase the error counter when the current version contains an empty string.<br />
Show the program name, the previous version and current version if current version is not an empty string.<br />
Set previous version to the value of current version when they are not equal and current version is not an empty string.</p>
<p>This should catch the problem when a check returns nothing. The check may be broken and needs attention/fixing.<br />
In any other case the version string may be some odd text which can be easily spotted when the current version and the previous version differ.</p>
<p>To check if the script works as intended, the pver value for one program can be modified using the following command:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 win32.db &quot;update win32 set pver = 12345 where program like 'Adobe%';&quot;
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad     3.6         3.6         0         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  12345       0  
</pre>
<p>&nbsp;<br />
Running the script now should provide an output:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ ./checkscript.sh
ProgramName         OldVersion  CurrentVersion
------------------  ----------  --------------
Adobe Flash Plugin  12345       10.3.181.34   
</pre>
<p>&nbsp;<br />
Also, the OldVersion should be overwritten by the value of CurrentVersion:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad     3.6         3.6         0         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  10.3.181.3  0 
</pre>
<p>&nbsp;<br />
A subsequent run of the script should not yield any output, unless the vendor updated the version in the time frame we ran the script again.  But does the error counter work as intended? Let&#8217;s find out!</p>
<p>Manually setting the current version to an empty string:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 win32.db &quot;update win32 set cver='' where program = 'Metapad';&quot;
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad                 3.6         0         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  10.3.181.3  0 
</pre>
<p>Rerunning the five sqlite3 commands and checking the result:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3                 win32.db &quot;update win32 set err = 0 WHERE cver != '';&quot;
$ sqlite3 -header -column win32.db &quot;select program AS ProgramName,err AS ErrorRuns from win32 where err &gt;= 5;&quot;
$ sqlite3                 win32.db &quot;update win32 set err = err  + 1 WHERE cver='';&quot;
$ sqlite3 -header -column win32.db &quot;select program AS ProgramName,pver AS OldVersion,cver AS CurrentVersion from win32 where ( cver != pver and cver != '');&quot;
$ sqlite3                 win32.db &quot;update win32 SET pver = cver WHERE ( pver != cver and cver != '' ); &quot;
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad                 3.6         1         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  10.3.181.3  0  
</pre>
<p>Running the five sqlite3 commands another four times will bump the error counter to 5 and it should look like this:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad                 3.6         5         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  10.3.181.3  0 
</pre>
<p>The next time the 5 commands are run and current version is still an empty string, the following should happen:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 -header -column win32.db &quot;select program AS ProgramName,err AS ErrorRuns from win32 where err &gt;= 5;&quot;
ProgramName  ErrorRuns 
-----------  ----------
Metapad      5         
$ sqlite3                 win32.db &quot;update win32 set err = err  + 1 WHERE cver='';&quot;
$ sqlite3 -header -column win32.db &quot;select program AS ProgramName,pver AS OldVersion,cver AS CurrentVersion from win32 where ( cver != pver and cver != '');&quot;
$ sqlite3                 win32.db &quot;update win32 SET pver = cver WHERE ( pver != cver and cver != '' ); &quot;
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;  
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad                 3.6         6         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  10.3.181.3  0      
</pre>
<p>The error counter did not get reset but the program was shown where obtaining the version has failed.<br />
Manually setting current version again to simulate that the next run of the version check for Metapad was successful:</p>
<pre class="brush: plain; auto-links: false; gutter: false; title: ; wrap-lines: false; notranslate">
$ sqlite3 win32.db &quot;update win32 set cver='3.6' where program = 'Metapad';&quot;
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad     3.6         3.6         6         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  10.3.181.3  0         
$ sqlite3                 win32.db &quot;update win32 set err = 0 WHERE cver != '';&quot;
$ sqlite3 -header -column win32.db &quot;select program AS ProgramName,err AS ErrorRuns from win32 where err &gt;= 5;&quot;
$ sqlite3                 win32.db &quot;update win32 set err = err  + 1 WHERE cver='';&quot;
$ sqlite3 -header -column win32.db &quot;select program AS ProgramName,pver AS OldVersion,cver AS CurrentVersion from win32 where ( cver != pver and cver != '');&quot;
$ sqlite3                 win32.db &quot;update win32 SET pver = cver WHERE ( pver != cver and cver != '' ); &quot;
$ sqlite3 -header -column win32.db &quot;select * from win32;&quot;    
program     cver        pver        err       
----------  ----------  ----------  ----------
Metapad     3.6         3.6         0         
Pidgin      2.9.0       2.9.0       0         
Adobe Flas  10.3.181.3  10.3.181.3  0       
</pre>
<p>Now you should have a utility that can check for versions and tell you if new versions are available every time it is run.<br />
What you can do from there is left to the imagination of the reader :)</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/hexeract.wordpress.com/363/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/hexeract.wordpress.com/363/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=hexeract.wordpress.com&#038;blog=7317442&#038;post=363&#038;subd=hexeract&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://hexeract.wordpress.com/2011/07/15/how-to-keep-up-with-new-versions-of-programs/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/290abdb318ff76c5a995d01d90e722fc?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">hexeract</media:title>
		</media:content>
	</item>
	</channel>
</rss>
