<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/rss2full.xsl" type="text/xsl" media="screen"?><?xml-stylesheet href="http://feeds.feedburner.com/~d/styles/itemcontent.css" type="text/css" media="screen"?><rss 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:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>output stream</title>
	
	<link>http://duncanandmeg.org/blogs/code</link>
	<description>riotous events in amateur development</description>
	<pubDate>Sat, 26 Jul 2008 19:10:40 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6</generator>
	<language>en</language>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/DJCode" type="application/rss+xml" /><item>
		<title>Burning CDs and DVDs in Windows XP… without unusual punishment</title>
		<link>http://feeds.feedburner.com/~r/DJCode/~3/346812543/</link>
		<comments>http://duncanandmeg.org/blogs/code/2008/07/26/burning-cds-and-dvds-in-windows-xp-without-unusual-punishment/#comments</comments>
		<pubDate>Sat, 26 Jul 2008 19:10:40 +0000</pubDate>
		<dc:creator>dtjohnso</dc:creator>
		
		<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://duncanandmeg.org/blogs/code/?p=27</guid>
		<description><![CDATA[Someday, burning optical media like CDs and DVDs may be a fully supported operating system feature. Until then, Windows users have to use burning software packages that unfortunately tend to bundle complexity and cost with a lot of frustration. Windows XP does support minimal burn operations when blank CDs and DVDs are opened as &#8220;writable&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p>Someday, burning optical media like CDs and DVDs may be a fully supported operating system feature. Until then, Windows users have to use burning software packages that unfortunately tend to bundle complexity and cost with a lot of frustration. Windows XP does support minimal burn operations when blank CDs and DVDs are opened as &#8220;writable&#8221; windows in My Computer, but this is not enough for creating anything beyond basic data disks.</p>
<p>I have used commercial burn software packages (like Nero and Roxio) before, but I chose <strong>not</strong> to use these packages again for a few recent projects. Here&#8217;s why:</p>
<ol>
<li>For a home user who rarely (read, almost never) needs to burn media anyway, it is difficult to justify the cost of either package.</li>
<li>Both Nero and Roxio bundle a lot of useless junkware into their packages (perhaps partially to justify their pricetag) and all I need is basic burn functionality.</li>
<li>Removing remnants of an expired &#8220;trial&#8221; version of either is not a task I relish. I&#8217;ve found (and Wikipedia concurs<sup>1</sup>) that even licensed copies of these programs are hard to remove.</li>
</ol>
<p>With this in mind, I set out to find open source or freeware burn software without needless frills. I was looking for actively developed projects which might continue to be useful in the future. Two programs which do just that are <strong>InfraRecorder</strong> and <strong>ImgBurn</strong>.</p>
<p><a href="http://infrarecorder.sourceforge.net/">InfraRecorder</a> is an open-source project, and it supports creating various data and audio CDs and DVDs (at this writing, it is in version 0.45). It does not yet support the creation of video DVDs, but it has a fairly simple interface, and enough basic documentation to help most users. I used it to burn a a large collection of MP3 files to a DVD without any problems.</p>
<p><a href="http://www.imgburn.com/">ImgBurn</a> is freeware, and supports data, audio, and video for CDs, DVDs, HD-DVDs, and  Blu-ray (at this writing, it is in version 2.4.2.0). The interface is very clean and operation is straightforward. I probably like this program better than InfraRecorder because its basic approach appeals to me.</p>
<p>Rather than modeling all its operations like other burning software which combine high-level tasks like &#8220;Copy disk&#8221; or &#8220;Burn data disk&#8221; with lower-level processes such as creating a disk image, ImgBurn does everything from a low-level perspective. If you want to copy a disk, you first run the process to create an image file of the original disk, and then you insert a blank disk and burn the image file into the new disk. Although this approach may seem more technical to a new user than a higher-level approach, this approach is so simple (and the tutorials so straightforward) that when I first used it I feared that I was missing some detail simply because it seemed <em>too easy</em> to be true.</p>
<p>Amazingly, with all this simplicity, it really does support a high degree of customization for advanced users. The complexity is accessible, but you don&#8217;t have to deal with it unless you seek it out.</p>
<p>I&#8217;m glad I finally put some effort into finding burn software for future projects. Both of these are good programs that don&#8217;t annoy me in any of the ways the commercial packages did, and hopefully they&#8217;ll still be useful as technology and media standards progress. At least for now, these two will probably remain my burn packages of choice.</p>
<p class="footnotes">NOTES:</p><ol class="footnotes"><li id="footnote_0_27" class="footnote">Wikipedia&#8217;s <a href="http://en.wikipedia.org/wiki/Easy_Media_Creator">article on Roxio&#8217;s &#8220;Easy Media Creator&#8221;</a> states <em>&#8220;Once installed, Easy Media Creator is difficult to uninstall. See the Roxio Community Forum, containing over 5000 posts concerning this topic. Roxio&#8217;s official six-step uninstall process, which includes registry edits, is available at &#8230;.&#8221;</em> </li></ol>]]></content:encoded>
			<wfw:commentRss>http://duncanandmeg.org/blogs/code/2008/07/26/burning-cds-and-dvds-in-windows-xp-without-unusual-punishment/feed/</wfw:commentRss>
		<feedburner:origLink>http://duncanandmeg.org/blogs/code/2008/07/26/burning-cds-and-dvds-in-windows-xp-without-unusual-punishment/</feedburner:origLink></item>
		<item>
		<title>Clean pastes with Excel VBA</title>
		<link>http://feeds.feedburner.com/~r/DJCode/~3/200435610/</link>
		<comments>http://duncanandmeg.org/blogs/code/2007/12/14/clean-pastes-with-excel-vba/#comments</comments>
		<pubDate>Fri, 14 Dec 2007 16:50:31 +0000</pubDate>
		<dc:creator>dtjohnso</dc:creator>
		
		<category><![CDATA[VBA]]></category>

		<guid isPermaLink="false">http://duncanandmeg.org/blogs/code/2007/12/14/clean-pastes-with-excel-vba/</guid>
		<description><![CDATA[I&#8217;ve talked about some of the Excel VBA I&#8217;ve done before, and here&#8217;s a new thing I learned today.
Problem
One part of my application selects a range from one worksheet in the spreadsheet and pastes it onto another. Originally, the code I used looked like this:
destinationWorksheet.Range("A2").Select
ActiveSheet.Paste

There is one problem I was unaware of. Every time the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve talked about <a href="/blogs/code/2007/09/13/fixing-memory-leaks-in-excel-vba-we-hope/">some of the Excel VBA I&#8217;ve done</a> before, and here&#8217;s a new thing I learned today.<br />
<h3>Problem</h3>
<p>One part of my application selects a range from one worksheet in the spreadsheet and pastes it onto another. Originally, the code I used looked like this:</p>
<pre><code class="prettyprint">destinationWorksheet.Range("A2").Select
ActiveSheet.Paste</pre>
<p></code></p>
<p>There is one problem I was unaware of. Every time the .Paste operation executed, it added a new &#8220;Named Range&#8221; to the ActiveSheet. I only realized this when snooping around in the Properties for the Excel file, where I found (to much dismay) 840 different &#8220;Named Ranges&#8221; referring to data long since thrown away! An older version of my application, which used this unspecified Paste operation much more frequently, had even more of these attached to numbers of other worksheets.<span id="more-23"></span><br />
<h3>Solution</h3>
<p>The first thing I looked into was the properties of the .Paste method itself. Turns out there are two of them, &#8220;Destination&#8221; and &#8220;Link,&#8221; and they are more or less mutually exclusive. The Excel VBA Help provides good explanation of that which I won&#8217;t reproduce here.</p>
<p>The reason my old call for an unparameterized .Paste worked was that it simply used the current selection as the &#8220;Destination&#8221; property. This is explained from this portion of the Help document:</p>
<blockquote><p><strong><em>Destination</em></strong> Optional <strong>Variant</strong>. &#8230; If this argument is omitted, the current selection is used.</p></blockquote>
<p>I had assumed that this parameter was irrelevant. The increase in these weird &#8220;Named Ranges&#8221; made me suspicious.</p>
<p>I modified my .Paste call this way to see if it would make a difference:</p>
<pre><code class="prettyprint">ActiveSheet.Paste Destination:=ActiveCell</code></pre>
<p>Adding a parameter that specifies the default parameter anyway isn&#8217;t a pretty fix, would it work? After executing a few calls to the .Paste statement in question, I was shocked! It <em>did</em> make a difference! No more &#8220;Named Ranges&#8221; were being appended to the ActiveSheet.<br />
<h3>Cleanup</h3>
<p>Wonderful, now I had 840 &#8220;Named Ranges&#8221; stuck in my file for no reason, referring to data long since wiped off the sheet. Some quick Help file searching taught me some things.</p>
<p>Each Worksheet object in a Workbook contains a collection of Name objects called Names. The Name objects in this collection are the &#8220;Named Ranges&#8221; in question. With some quick &#8216;n dirty coding, I wiped them all out. Solution is below:</p>
<pre><code class="prettyprint">Private Sub NameDR4ng3z()
    ThisWorkbook.Worksheets("destinationWorksheetName").Activate

    For Each myName In ActiveSheet.Names 'list '
        Debug.Print myName.Name
    Next myName

    For Each myName In ActiveSheet.Names 'delete '
        myName.Delete
    Next myName

    Debug.Print "Names deleted? We'll see"
    For Each myName In ActiveSheet.Names 'double-check '
        Debug.Print myName.Name
    Next myName
End Sub</code></pre>
<p>This worked. I&#8217;m happy.</p>
]]></content:encoded>
			<wfw:commentRss>http://duncanandmeg.org/blogs/code/2007/12/14/clean-pastes-with-excel-vba/feed/</wfw:commentRss>
		<feedburner:origLink>http://duncanandmeg.org/blogs/code/2007/12/14/clean-pastes-with-excel-vba/</feedburner:origLink></item>
		<item>
		<title>Lazy list management</title>
		<link>http://feeds.feedburner.com/~r/DJCode/~3/183811660/</link>
		<comments>http://duncanandmeg.org/blogs/code/2007/11/12/lazy-list-management/#comments</comments>
		<pubDate>Mon, 12 Nov 2007 21:28:09 +0000</pubDate>
		<dc:creator>dtjohnso</dc:creator>
		
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://duncanandmeg.org/blogs/code/2007/11/12/lazy-list-management/</guid>
		<description><![CDATA[I use a couple awk scripts to manage a list of students who come to church with me every week. When a student tells me he will be coming on Sunday, I put an asterisk (*) in front of his name in a static text file with everyone&#8217;s names. If they tell me they won&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>I use a couple awk scripts to manage a list of students who come to church with me every week. When a student tells me he will be coming on Sunday, I put an asterisk (*) in front of his name in a static text file with everyone&#8217;s names. If they tell me they won&#8217;t be coming, I put a minus sign (-) in front of their name so I remember that they said so. If for some reason I&#8217;m not sure, I put a question mark (?).</p>
<p>My main script spits out a list of all the students who are coming (i.e., have an asterisk). There&#8217;s no genius to it, I&#8217;m just logging it here for reference.</p>
<p>I also have another script that wipes all the attendance marks out of the main list file.<span id="more-10"></span></p>
<h2>Files</h2>
<p>All files are available here:
<ul>
<li>
<a href='http://duncanandmeg.org/blogs/code/wp-content/uploads/2007/11/studentlist.zip' title='List management files'>List management files</a></li>
</ul>
<p>Source is posted below without comment since it&#8217;s basically self-explanatory.</p>
<h3>StudentList.bash</h3>
<pre><code class="prettyprint">awk 'substr($1,0,1) == "*" { print substr($0,2,length($0)) } ' students.txt > studentsComing.txt
cat studentsComing.txt</code></pre>
<h3>WipeStudentList.bash</h3>
<pre><code class="prettyprint">awk -f Wipe.awk <students.txt >temp
cat temp >students.txt
rm temp
./studentlist.bash</code></pre>
<h3>Wipe.awk</h3>
<pre><code class="prettyprint">{if ( substr($1,0,1) == "*" || substr($1,0,1) == "-" || substr($1,0,1) == "?" )
  { print substr($0,2,length($0))  }
 else
  { print $0 }
}</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://duncanandmeg.org/blogs/code/2007/11/12/lazy-list-management/feed/</wfw:commentRss>
		<feedburner:origLink>http://duncanandmeg.org/blogs/code/2007/11/12/lazy-list-management/</feedburner:origLink></item>
		<item>
		<title>Producing valid RSS from my XSL Transform</title>
		<link>http://feeds.feedburner.com/~r/DJCode/~3/168713389/</link>
		<comments>http://duncanandmeg.org/blogs/code/2007/10/11/producing-valid-rss-from-my-xsl-transform/#comments</comments>
		<pubDate>Thu, 11 Oct 2007 21:15:37 +0000</pubDate>
		<dc:creator>dtjohnso</dc:creator>
		
		<category><![CDATA[XML &amp; XSL]]></category>

		<guid isPermaLink="false">http://duncanandmeg.org/blogs/code/2007/10/11/producing-valid-rss-from-my-xsl-transform/</guid>
		<description><![CDATA[In a previous post, I showed the XSL transform I designed to transform a simple XML file I use into &#8220;(almost) valid RSS.&#8221; I called it (almost) valid because of one problem, which I address here.
The problem reviewed
My original XSL assigned an arbitrary ID to each entry in the news file based on its order [...]]]></description>
			<content:encoded><![CDATA[<p>In <a href="/blogs/code/2007/08/13/rendering-xml-as-valid-rss-with-xslt/">a previous post</a>, I showed the XSL transform I designed to transform a simple XML file I use into &#8220;(almost) valid RSS.&#8221; I called it (almost) valid because of one problem, which I address here.</p>
<h2>The problem reviewed</h2>
<p>My original XSL assigned an arbitrary ID to each entry in the news file based on its order in the list. What this meant was that the first item received the integer 1 for an ID, the second 2, the third 3, etc. Here it is:</p>
<pre><code class="prettyprint">&lt;!--old method, produces invalid link &#038; guid--&gt;
&lt;link&gt;&lt;xsl:text&gt;http://www.duncanandmeg.org/news.php#&lt;/xsl:text&gt;
  &lt;xsl:value-of select="position()" /&gt;&lt;/link&gt;
&lt;guid&gt;&lt;xsl:text&gt;http://www.duncanandmeg.org/news.php#&lt;/xsl:text&gt;
  &lt;xsl:value-of select="position()" /&gt;&lt;/guid&gt;
</code></pre>
<p>This was fine for my original purposes, but since this hardly represented a unique &#038; permanent identifier for each post, it caused some problems when echoed into the RSS &lt;guid&gt; and &lt;link&gt; elements. The significance of the &lt;guid&gt; element in particular is eliminated with such an approach. Every time I added an item to the list (see the previous post for an explanation of my schema), it would be assigned the ID of 1, and the older posts would be assigned the ID numbers 2, 3, 4, 5 etc.</p>
<p>This meant that most RSS aggregators would not detect that a new post had appeared and would not update accordingly.<br />
<span id="more-8"></span></p>
<h2>Choosing a unique ID schema</h2>
<p>Since I could no longer use my simple numbering scheme based on position in the original XML file, I had to come up with some other identifier. I learned about the <a href="http://www.taguri.org/">Tag URI algorithm</a>, but decided that it was more complex than I really needed for this application, and the scheme of my original XML file didn&#8217;t lend itself to producing these anyway.</p>
<p>Since I don&#8217;t update the entries in this file frequently, I invented a simple method that produces relatively unique identifiers based on each entry&#8217;s data. All my new ID&#8217;s consist of two elements based on the data in my XML file:</p>
<ol>
<li>The text from the entry&#8217;s title preceding the first space (&#8221; &#8220;) character<br/><i>(If the entry consists of only one word, this element will not appear. This is a weakness that would be more important if updates were frequent)</i></li>
<li>The date of the entry encoded in the format <i>mm-dd-yy</i></li>
</ol>
<p>Once I settled on my scheme, producing it in XSL turned out to be fairly simple.</p>
<h2>XSL transform</h2>
<p>I&#8217;m including only the portion of the XSL tranform that deals with the entry &lt;link&gt; and &lt;guid&gt; here. The rest of it is in <a href="/blogs/code/2007/08/13/rendering-xml-as-valid-rss-with-xslt/">the previous post</a>.</p>
<pre><code class="prettyprint">&lt;item&gt;
  &lt;title&gt;&lt;xsl:value-of select="headline" /&gt;&lt;/title&gt;

  &lt;!--unique id created and stored in XSL variable--&gt;
  &lt;xsl:variable name="bookmark"&gt;

    &lt;!--find word before first space--&gt;
    &lt;xsl:value-of select="substring-before (headline, ' ')" /&gt;
    &lt;!--produce date in format mm-dd-yy--&gt;
    &lt;xsl:value-of select="translate (date, '/', '-')" /&gt;

  &lt;/xsl:variable&gt;

  &lt;!--variable placed in link and guid elements--&gt;
  &lt;link&gt;&lt;xsl:text&gt;http://www.duncanandmeg.org/news.php#&lt;/xsl:text&gt;
    &lt;xsl:value-of select="$bookmark" /&gt;&lt;/link&gt;
  &lt;guid&gt;&lt;xsl:text&gt;http://www.duncanandmeg.org/news.php#&lt;/xsl:text&gt;
    &lt;xsl:value-of select="$bookmark" /&gt;&lt;/guid&gt;

  &lt;!-- continue with transform, see original post --&gt;

&lt;/item&gt;
</code></pre>
<p>With a reasonably unique guid, most RSS readers properly update now when I post new items.</p>
]]></content:encoded>
			<wfw:commentRss>http://duncanandmeg.org/blogs/code/2007/10/11/producing-valid-rss-from-my-xsl-transform/feed/</wfw:commentRss>
		<feedburner:origLink>http://duncanandmeg.org/blogs/code/2007/10/11/producing-valid-rss-from-my-xsl-transform/</feedburner:origLink></item>
		<item>
		<title>Fixing memory leaks in Excel VBA… (we hope)</title>
		<link>http://feeds.feedburner.com/~r/DJCode/~3/156278855/</link>
		<comments>http://duncanandmeg.org/blogs/code/2007/09/13/fixing-memory-leaks-in-excel-vba-we-hope/#comments</comments>
		<pubDate>Fri, 14 Sep 2007 02:05:52 +0000</pubDate>
		<dc:creator>dtjohnso</dc:creator>
		
		<category><![CDATA[VBA]]></category>

		<guid isPermaLink="false">http://duncanandmeg.org/blogs/code/2007/09/13/fixing-memory-leaks-in-excel-vba-we-hope/</guid>
		<description><![CDATA[One project I constantly return to is an application I built in Excel VBA that does a lot of different things. One specific problem remains unsolved with this application, so I&#8217;ve decided to throw it out here and see if any gurus have an answer.
Basically, as a result of some Excel memory leaks beyond my [...]]]></description>
			<content:encoded><![CDATA[<p>One project I constantly return to is an application I built in Excel VBA that does a lot of different things. One specific problem remains unsolved with this application, so I&#8217;ve decided to throw it out here and see if any gurus have an answer.</p>
<p>Basically, as a result of some Excel memory leaks beyond my control (I can&#8217;t install recommended hotfixes on the computers where this application is used, and need to be able to handle the issue with/without hotfixes anyway), when I shut down Excel it typically remains stuck in memory (and visible under &#8220;Processes&#8221; in &#8220;Task Manager&#8221;) even though it looks like it is gone to the user.<br />
<h3>Paranoia</h3>
<p>I realize that many would argue that since this bug is Microsoft&#8217;s problem and not mine, that I shouldn&#8217;t trouble with addressing it. However, since this application I&#8217;ve built is something that gets used frequently (but not constantly) throughout the day, I can imagine this becoming a problem (or at least wasting resources) at some point.</p>
<p>Another rejoinder to this charge is quite simple: I&#8217;ve actually ignored this issue for about a year now already, and am only revisiting it because I&#8217;ve learned some things about VBA that enable me to possibly defeat it. If I didn&#8217;t have any hope of fixing this, I wouldn&#8217;t try.<span id="more-7"></span><br />
<h3>Problem (in more depth)</h3>
<p>Here is what I know about why one of my memory leak problems exists (this article won&#8217;t deal with all my memory leak issues in the application, since I still haven&#8217;t made any headway on the others). I may not have quite the right diagnosis here, but tests to this point suggest I might be on the right track.</p>
<p>The main worksheet in my application includes numerous embedded control buttons that marshall up various cool features I&#8217;ve built for this VBA app. Unfortunately, it appears that <a href="http://support.microsoft.com/default.aspx?scid=kb;en-us;238570">embedding any sort of control (as far as I can tell) on a worksheet can cause Excel to commit memory leaks</a>.</p>
<p>Before going further, a few notes about the KB article I just mentioned:</p>
<ol>
<li>This article refers to an issue with Excel 97.</li>
<li>I&#8217;m not sure that worksheet buttons are considered ActiveX or OCX controls</li>
</ol>
<p>The problem here is simple: I did some research on this a year ago when I first discovered the issue, and didn&#8217;t keep track of the KB article I found that suggested the problem with embedded buttons. I remember reading something like that then, but can&#8217;t find it now. The only reason I think this might actually be the source of the problem is because my hacked solution seems to prevent the memory leak in question.<br />
<h3>Ugly solution</h3>
<p>I learned that it is possible in Excel VBA to <a href="http://support.microsoft.com/kb/141563">write an Auto_Close sub to override default behavior when closing a spreadsheet</a>. <strike>(If I ever track down the documentation that tipped me off to this, I&#8217;ll link to it here.)</strike></p>
<p>This suggested that I might be able to do something on the file&#8217;s close event that might at least catch and destroy the source of the memory leak before the Excel app slipped out of my control until an inevitable Ctrl+Alt+Del. Here&#8217;s the ugly, scary code:</p>
<pre><code class="prettyprint">
Sub Auto_Close()
    'Deletes the spreadsheet with the embedded buttons '
    Application.DisplayAlerts = False
    ThisWorkbook.Worksheets("Sheet1").Delete

    'Prevents prompt to save changes, so that workbook closes without saving '
    'This prevents the sheet from being really deleted '
    ThisWorkbook.Saved = True
End Sub
</code></pre>
<h3>Concerns</h3>
<p>After this function fires, Excel closes as normal, without saving the file. Since this fires whenever the user closes the file, it seems safe enough to do this, but you can&#8217;t help being a little bit frightened with an approach like this.<br />
<h3>Tests and reasoning</h3>
<p>I think this works. Before, when I would exit Excel, an instance of EXCEL.EXE would remain in my &#8220;Process&#8221; list in Task Manager until I manually terminated it, logged out, or rebooted. Now when I exit Excel, it disappears from the list as normal (unless I&#8217;ve used some other parts of the application which induce the other memory leak problems I&#8217;m not discussing here). It appears that the embedded button memory leak problem is bypassed (I really hesitate to say &#8220;solved&#8221;).<br />
<h3>Suggestions?</h3>
<p>If you have handled this problem before, have more information, or know how a better (and saner) way to fix this, please post a comment!</p>
]]></content:encoded>
			<wfw:commentRss>http://duncanandmeg.org/blogs/code/2007/09/13/fixing-memory-leaks-in-excel-vba-we-hope/feed/</wfw:commentRss>
		<feedburner:origLink>http://duncanandmeg.org/blogs/code/2007/09/13/fixing-memory-leaks-in-excel-vba-we-hope/</feedburner:origLink></item>
		<item>
		<title>Processing barcode scanner data with awk and sed</title>
		<link>http://feeds.feedburner.com/~r/DJCode/~3/145266729/</link>
		<comments>http://duncanandmeg.org/blogs/code/2007/08/17/processing-barcode-scanner-data-with-awk-and-sed/#comments</comments>
		<pubDate>Fri, 17 Aug 2007 19:23:54 +0000</pubDate>
		<dc:creator>dtjohnso</dc:creator>
		
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://duncanandmeg.org/blogs/code/2007/08/17/processing-barcode-scanner-data-with-awk-and-sed/</guid>
		<description><![CDATA[We had an interesting project pop up here at the Library where I work yesterday. Apparently, part of our inventory process here involves downloading text files with raw barcode data from our barcode scanners, extracting the barcode from amidst the other junk data that pads it in the file, and then loading a freshly formatted [...]]]></description>
			<content:encoded><![CDATA[<p>We had an interesting project pop up here at the Library where I work yesterday. Apparently, part of our inventory process here involves downloading text files with raw barcode data from our barcode scanners, extracting the barcode from amidst the other junk data that pads it in the file, and then loading a freshly formatted list into Millennium, our library&#8217;s catalogue software.</p>
<p>I&#8217;m not typically involved with inventory or the particulars of maintaining the Millennium catalogue, but I was called in to help with writing some bash scripts to facilitate the process.<span id="more-6"></span></p>
<h2>The data</h2>
<p>Let me begin by showing some sample data from our barcode scanners. The scanners store the barcode in text files, one barcode per line, with some interesting pad characters that I don&#8217;t understand and we don&#8217;t really want for this project.</p>
<h3>Data example A</h3>
<pre><code>TXT<font color="red">95053542</font>95012010:12
TXT<font color="red">95053534</font>95012010:12
TXT<font color="red">95053559</font>95012010:12
TXT<font color="red">95053567</font>95012010:12
TXT<font color="red">95053575</font>95012010:12</code></pre>
<h3>Data example B</h3>
<pre><code>0000030000000000<font color="red">8016039R</font>07051001:56
0000030000000000<font color="red">8110727Q</font>07051001:56
0000030000000000<font color="red">84220078</font>07051001:56
0000030000000000<font color="red">8122772T</font>07051001:56</code></pre>
<p>In both examples here, I&#8217;ve made the actual barcode red. The rest of the line is garbage data.</p>
<p>From this data, let me make three observations.
<ol>
<li>The characters preceding the barcode may be <i>n</i> characters long.</li>
<li>Our barcodes are always 8 characters long.<br/>(I already knew this, but needed to make it clear for this post)</li>
<li>The characters following the barcode appear to always be 11 characters long.</li>
</ol>
<h2>The output</h2>
<p>Our catalogue software likes to receive barcodes from text files with each barcode on a line prefixed with n: like so&#8230;</p>
<h3>Output example A</h3>
<pre><code>n:<font color="red">95053542</font>
n:<font color="red">95053534</font>
n:<font color="red">95053559</font>
n:<font color="red">95053567</font>
n:<font color="red">95053575</font></code></pre>
<h3>Output example B</h3>
<pre><code>n:<font color="red">8016039R</font>
n:<font color="red">8110727Q</font>
n:<font color="red">84220078</font>
n:<font color="red">8122772T</font></code></pre>
<h2>The problem</h2>
<p>It seems that folks who do this all the time used to do it by some sort of fiddly method of importing it into Excel and setting a field delimiter at fixed widths to get the barcode into a column by itself, and then outputting everything in the proper format for Millennium somehow. All very tricky, manual, and not much fun&#8230;</p>
<h2>The solution</h2>
<p>The approach we took with this problem was proposed to me by a co-worker (kudos to Bryan Tyson!) who is better versed in Linux and bash scripting than I, but my limited experience with tools like awk and sed really made it seem like one of the easiest solutions to me too.</p>
<p>The following is the bash script we wrote to do the hard work for us. Since I&#8217;m not terribly versed in shell scripting with awk and sed, this took a bit of finding, and we tried to comment the script heavily to make it legible in the future. Although awk and sed are powerful, they surely don&#8217;t win points for preventing code obfuscation.</p>
<pre><code class="prettyprint">#!/bin/bash

if (test $# = 2) then #If file names are entered as input params
  INFILE=$1 #store first param as input
  OUTFILE=$2 #store second param as output
else #prompt for filenames
  echo "******************************************"
  echo "Welcome to inventory at J.S. Mack Library!"
  echo "This script takes the barcode file from"
  echo "the scanner and formats it for the"
  echo "Millennium inventory program."
  echo "******************************************"
  echo ""

  #Ask user to enter filename to be processed

  echo "What file to process?"
  echo "Include the full path if the file is not"
  echo "in the same directory as this script."
  echo ""
  read INFILE

  echo ""
  echo "What file to save the reformatted results?"
  echo "Include the full path if the file is not"
  echo "in the same directory as this script."
  echo ""
  read OUTFILE
fi 

#Input files from the scanner may have variable length lines in the following
#format:
#  n chars prefix, 8 char barcode, 11 char postfix
#  n is set by the scanner's "Major Division" setting
#We want to cut out all but the 8 char barcode.
#First, we must determine the length of the prefix.
#We do this with awk to find the length of every line and subtract the barcode
#and postfix from the total, then pipe to sed to get the prefix found for the
#first line. This assumes that every line in the file is the same length.
#(Can the major division change in the middle of a scanner file? Let's hope not!)
PREFIX=`awk '{print length($0) - 8 - 11}' $INFILE | sed 1q`

#Following calculating the prefix length, we store two values to pass
#to the cut command later based on the prefix.
let BEGIN=$PREFIX+1 #begin on first char after prefix
let END=$PREFIX+8 #end last char of barcode

#The following executes the cut command and pushes output (1 barcode on each line)
#directly to sed.
#Millennium needs "n:" before each barcode. Using sed, we will insert this at
#beginning of each line and output to filename given by the user.
cut -c$BEGIN-$END $INFILE | sed -e 's/^/n:/' > $OUTFILE

echo ""
echo "Your barcode file, ${INFILE}, has been reformatted and saved to ${OUTFILE}."
</code></pre>
<h3>Conclusion</h3>
<p>Perhaps the most unique part of this project was that we needed to be able to run this in Windows, so I had to find out how to run bash shell scripts on a Windows box. We used <a href="http://www.cygwin.com/">Cygwin</a> with some success.</p>
<p>Whether this solution is the most efficient is up for grabs. However, it works! If anyone has suggestions for improvements, please comment.</p>
]]></content:encoded>
			<wfw:commentRss>http://duncanandmeg.org/blogs/code/2007/08/17/processing-barcode-scanner-data-with-awk-and-sed/feed/</wfw:commentRss>
		<feedburner:origLink>http://duncanandmeg.org/blogs/code/2007/08/17/processing-barcode-scanner-data-with-awk-and-sed/</feedburner:origLink></item>
		<item>
		<title>Rendering XML as valid RSS with XSLT</title>
		<link>http://feeds.feedburner.com/~r/DJCode/~3/144021417/</link>
		<comments>http://duncanandmeg.org/blogs/code/2007/08/13/rendering-xml-as-valid-rss-with-xslt/#comments</comments>
		<pubDate>Mon, 13 Aug 2007 21:30:02 +0000</pubDate>
		<dc:creator>dtjohnso</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[XML &amp; XSL]]></category>

		<guid isPermaLink="false">http://duncanandmeg.org/blogs/code/2007/08/13/rendering-xml-as-valid-rss-with-xslt/</guid>
		<description><![CDATA[I&#8217;ve been using a simple XML file to store updates to the main duncanandmeg.org website, and wanted to use XSL to transform that data into a valid RSS feed. This took a little more work than most of my XSL templates to date, so I thought I&#8217;d log the details here.
XML data
The following is the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been using a simple XML file to store updates to the <a href="/">main duncanandmeg.org website</a>, and wanted to use XSL to transform that data into a <a href="http://www.feedvalidator.org/">valid RSS feed</a>. This took a little more work than most of my XSL templates to date, so I thought I&#8217;d log the details here.<span id="more-5"></span></p>
<h3>XML data</h3>
<p>The following is the structure of my XML file. I&#8217;ve been storing dates in the file in short date format. The detail element is optional, and since is the only element where I embed raw HTML, I encapsulate the contents in CDATA elements.<br />
Also, since I&#8217;m updating this file manually at this point, I add new entries to the top of the file rather than worry about using more automated date sorting.</p>
<pre><code class="prettyprint">&lt;news&gt;
  &lt;entry&gt;
    &lt;headline&gt;headline&lt;/headline&gt;
     &lt;date&gt;mm/dd/yy&lt;/date&gt;
    &lt;detail&gt;&lt;![CDATA[more details (if applicable)]]&gt;&lt;/detail&gt;
  &lt;/entry&gt;
&lt;/news&gt;
</code></pre>
<h3>XSL transform</h3>
<p>Because RSS is a more complex standard than my simple XML file, the XSL transform is necessarily complex.</p>
<pre><code class="prettyprint">&lt;xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  &lt;xsl:output method="xml"/&gt;

  &lt;xsl:template match="/news"&gt;
  &lt;rss version="2.0"&gt;
    &lt;channel&gt;
      &lt;title&gt;hard-coded title&lt;/title&gt;
      &lt;link&gt;hard-coded url&lt;/link&gt;
      &lt;description&gt;hard-coded description&lt;/description&gt;
      &lt;ttl&gt;60&lt;/ttl&gt;
      &lt;xsl:apply-templates select="entry[position() &lt; 6]" /&gt;
    &lt;/channel&gt;
  &lt;/rss&gt;
  &lt;/xsl:template&gt;

  &lt;xsl:template match="entry"&gt;
    &lt;item&gt;
      &lt;title&gt;&lt;xsl:value-of select="headline" /&gt;&lt;/title&gt;
      &lt;link&gt;&lt;xsl:text&gt;http://www.duncanandmeg.org/news.php#&lt;/xsl:text&gt;
        &lt;xsl:value-of select="position()" /&gt;&lt;/link&gt;
      &lt;guid&gt;&lt;xsl:text&gt;http://www.duncanandmeg.org/news.php#&lt;/xsl:text&gt;
        &lt;xsl:value-of select="position()" /&gt;&lt;/guid&gt;
      &lt;pubDate&gt;&lt;xsl:value-of select="date" /&gt;&lt;/pubDate&gt;
      &lt;description&gt;
        &lt;xsl:if test="detail"&gt;
          &lt;xsl:value-of disable-output-escaping="no" select="detail" /&gt;
        &lt;/xsl:if&gt;
      &lt;/description&gt;
    &lt;/item&gt;
   &lt;/xsl:template&gt;

&lt;/xsl:stylesheet&gt;</code></pre>
<p>This produces <b>almost</b> valid RSS feed for the top 5 items in the feed.</p>
<p>The generated RSS includes dates in short date format (mm/dd/yy), which violates the RSS standard which calls for the <a href="http://feedvalidator.org/docs/error/InvalidRFC2822Date.html">RFC-822 date-time format</a>. Since all the implementations I could find on the web for doing date conversions in XSL were far too complex for my taste, I proceeded to do that with PHP when I actually transform the XML file into RSS.</p>
<h3>PHP code</h3>
<p>The following PHP represents a minimalist approach to executing the XSL transform and returning RSS as output. This script doesn&#8217;t handle caching, and there are issues with the &lt;link&gt; and &lt;guid&gt; elements in the template for each &lt;entry&gt;.</p>
<p>The consequence of these weaknesses is that some feed readers don&#8217;t properly update when a new post is added to the news.xml file. This is something I&#8217;ll perhaps address in another post.</p>
<pre><code class="prettyprint">&lt;?php
  //Optional. Sets default time zone to something other than GMT
  date_default_timezone_set('EST');

  //Load XML data and transform to RSS
  $xslt = new xsltprocessor;
  $xslt->importStyleSheet(DomDocument::load('rss.xsl'));
  $xmlResult = $xslt->transformToXML(DomDocument::load('news.xml'));

  //Load resulting data into the SimpleXML processor
  $xml=simplexml_load_string($xmlResult);

  //Loop through RSS data in SimpleXML
  //Replace each short date with an RFC-822 date-time
  $count = 0;
  foreach($xml->xpath('//pubDate') as $dates){
    $newDates = date(DATE_RFC822, strtotime($dates));
    $xml->channel->item[$count]->pubDate = $newDates;
    $count++;
  }

  //Optional. These lines insert RFC-822 values
  //for channel/lastBuildDate and channel/pubDate
  $xml->channel->addChild("lastBuildDate",$xml->channel->item[0]->pubDate);
  $xml->channel->addChild("pubDate",$xml->channel->item[0]->pubDate);

  //Set output content-type to XML
  header('Content-type: application/rss+xml; charset=utf-8');
  print $xml->asXML(); //Return as XML
?&gt;
</code></pre>
<p><!--header('Cache-Control: no-cache');<br />
      header('Pragma: no-cache');--></p>
]]></content:encoded>
			<wfw:commentRss>http://duncanandmeg.org/blogs/code/2007/08/13/rendering-xml-as-valid-rss-with-xslt/feed/</wfw:commentRss>
		<feedburner:origLink>http://duncanandmeg.org/blogs/code/2007/08/13/rendering-xml-as-valid-rss-with-xslt/</feedburner:origLink></item>
	</channel>
</rss>
