<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;A0MFSHw7cCp7ImA9WhBUF0g.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804</id><updated>2013-05-05T09:10:19.208-04:00</updated><category term="install" /><category term="spray" /><category term="fog creek software" /><category term="package" /><category term="dtd" /><category term="video game" /><category term="knife" /><category term="p/invoke" /><category term="wtf" /><category term="open source" /><category term="lyrics" /><category term="sybase" /><category term="c#" /><category term="yum" /><category term="css" /><category term="git" /><category term="xpath" /><category term="makefile" /><category term="userland" /><category term=".net" /><category term="evil" /><category term="xhtml" /><category term="hg" /><category term="unglobal" /><category term="critic" /><category term=".net4" /><category term="cars" /><category term="asp" /><category term="story" /><category term="xml" /><category term="linq" /><category term="sqsh" /><category term="counter-strike" /><category term="wasabi" /><category term="callback" /><category term="ct-library" /><category term="bash" /><category term="pdf" /><category term="xpath namespaces" /><category term="movie" /><category term="global" /><category term="monopoly" /><category term="text" /><category term="entry point" /><category term="clean code" /><category term="html" /><category term="CD" /><category term="asp.net" /><category term="film industry" /><category term="access violation" /><category term="delegate" /><category term="compiler" /><category term="subversion" /><category term="tailgating" /><category term="opinionated" /><category term="serialization" /><category term="zbar" /><category term="fedora" /><category term="banking" /><category term="rpm" /><category term="hollywood" /><category term="incompetence" /><category term="csharp" /><category term="kernel space" /><category term="linq to sql" /><category term="webkit" /><category term="shell" /><category term="script" /><category term="code" /><category term="vbscript" /><category term="driving" /><category term="linux" /><category term="gun-game" /><category term="branching" /><category term="thistle" /><category term="personal" /><category term="dvcs" /><category term="programming" /><category term="politics" /><category term="configure" /><category term="scm" /><category term="Michelle Branch" /><category term="freetds" /><category term="music" /><category term="xslt" /><category term="sql server" /><category term="Eisley" /><category term="garbage collector" /><category term="allegro" /><category term="sql" /><category term="Singleton" /><category term="kernel" /><category term="sqshell" /><category term="film" /><category term="command line" /><category term="critique" /><category term="traffic" /><category term="mercurial" /><category term="fogbugz" /><category term="evanescence" /><category term="Eminem" /><title>Brandon McCaig's Blog</title><subtitle type="html">This is the blog of Brandon McCaig, a computer programmer from Ontario, Canada. Expect to see code and probably some strong opinions.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.bamccaig.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.bamccaig.com/" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/BrandonMccaigsBlog" /><feedburner:info uri="brandonmccaigsblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;A0EFRH0yeip7ImA9WhBQEU8.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-7295008724294374929</id><published>2013-03-12T19:04:00.000-04:00</published><updated>2013-03-12T19:06:55.392-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-03-12T19:06:55.392-04:00</app:edited><title>Singing/Rapping - Acapella - 50 Cent's "My Life" - Eminem's Verse and Hook</title><content type="html">&lt;p&gt;Just messing around, challenging myself by trying to perform Eminem's verse to My Life. And for the fun of it, trying to sing the hook (which is sung by Adam Levine).&lt;/p&gt;

&lt;audio controls&gt;
    &lt;source src="http://castopulence.org/tmp/ml-t3billion200.ogg" type="audio/ogg" /&gt;
    &lt;source src="http://castopulence.org/tmp/ml-t3billion200.mp3" type="audio/mpeg" /&gt;
    &lt;p&gt;It seems your browser does not support the HTML5 &lt;code&gt;audio&lt;/code&gt; tag (or neither Ogg/Vorbis nor MPEG audio formats) so instead you can &lt;a href="http://castopulence.org/tmp/ml-t3billion200.ogg"&gt;download the&lt;/a&gt; &lt;a href="http://castopulence.org/tmp/ml-t3billion200.mp3"&gt;audio files&lt;/a&gt; (you only need either/or) and play them with whatever player you prefer.&lt;/p&gt;
&lt;/audio&gt;

&lt;p&gt;Let me know what you think. :)&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/YnuU1tH3cxA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/7295008724294374929/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2013/03/singingrapping-acapella-50-cents-my.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/7295008724294374929?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/7295008724294374929?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/YnuU1tH3cxA/singingrapping-acapella-50-cents-my.html" title="Singing/Rapping - Acapella - 50 Cent's &quot;My Life&quot; - Eminem's Verse and Hook" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2013/03/singingrapping-acapella-50-cents-my.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAGQXkzeSp7ImA9WhJTGU4.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-6538596901740388793</id><published>2012-06-24T21:29:00.000-04:00</published><updated>2012-06-28T22:25:20.781-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-06-28T22:25:20.781-04:00</app:edited><title>What. The. Fuck. Netflix. (The New Ending User Interface)</title><content type="html">&lt;p&gt;
I've been a subscriber to Netflix (Canada) for well over a year. Probably over two years by now. I really didn't use it very much in the beginning. There wasn't anything that interested me. I hoped though that it would eventually fill that void and offer content that was worth paying for. Compared to the usual $5/day rental or $20/movie or $50/month to watch &lt;i&gt;when they tell you to&lt;/i&gt; it is a big step forward in intellectual property business models.
&lt;/p&gt;

&lt;p&gt;
I would watch the occasional show or movie here and there, but didn't really have a regular reason to use it. I literally paid for months in a row where I never even logged into it (I didn't keep track, but 6+ months is probably well within reason). To date there are basically two classes of content that I have consumed a lot of from Netflix: documentaries and old TV series'.
&lt;/p&gt;

&lt;p&gt;
I have had the luxury of watching a great many documentaries that I probably otherwise wouldn't have had the legal opportunity to watch. They're too political and unpredictable to pay typical theatrical, rental, or home licensing fees to watch, even though there are a great many of them worth watching, even if you don't agree with them all.
&lt;/p&gt;

&lt;p&gt;
Then there are TV series: some of them that you really enjoyed and want to watch again, and others that you just didn't have the time or interest during their original network television run to keep up and want the opportunity to experience it in a more timely manner. In my experience, all of the best TV shows seem to be cancelled prematurely. A few of the good ones manage to slip through, but far too many awesome characters are erased.
&lt;/p&gt;

&lt;p&gt;
Moving on, I began watching Xena: Warrior Princess on Netflix many months ago. I used to catch the occasional episode during its original run, and I liked it, but for whatever reasons I lost track with it. I've been really enjoying it, despite the obvious flaws in physics and logic. I can tolerate scientific laws in a fictional world that violate our own, but only if they stay consistent. So far I have been relatively satisfied, but they've begun to annoy me somewhat. That said, I have been forced to take the entire series somewhat less seriously with each progressing episode (the writing seems to get weaker with each successive season). I digress. Being able to watch a TV series episode to episode also offers you the freedom to criticize it in ways that aren't possible on a weekly cycle. I care less and less for the writing as the episodes progress (I &lt;span style="text-decoration: underline;"&gt;loved&lt;/span&gt; it when I first started watching; perhaps the writing failures are why I lost interest during its original run, but I digress). I watched the first 2 or 3 seasons and then catastrophe struck: my PlayStation 3 died.
&lt;/p&gt;

&lt;p&gt;
It was an original NTSC 60 GB model so it had lasted a good 5 or 6 years, which seems to be a pretty good run for a gaming console these days, but I digress. It was pretty disappointing, but I bit the bullet, contacted Sony support (several times), and eventually shipped it off to be repaired. It took me a few months to figure out a payment method and trust them not to fuck me over, but in the end it only took them about a week to repair and return it to me. It came back with a 90 day warranty too, which I am pleased about. Considering it cost me over CAD$200 to repair it I'm happy that there is some guarantee on workmanship. Moving on...
&lt;/p&gt;

&lt;p&gt;
During the 2 or 3 months while my PS3 was out of commission (most of which I spent trying to build trust for Sony's repair process [i.e., I was guaranteed the same model returned and I appear to have gotten it, so thank you, Sony] and finding the time to meet their payment options [i.e., no online payment options]) I didn't use Netflix. As a gamer, Netflix is more of a "I'm too exhausted and/or drunk to sit at the computer so I'm going to relax on the couch, but there's nothing on TV or on my PVR so I'm going to watch Netflix instead" thing. I had actually been watching Xena pretty regularly prior to my PS3 failure and was pretty crushed by the wait. When I finally got my PS3 back the first thing I did (despite being a gamer) was login to Netflix and watch the next episode of Xena.
&lt;/p&gt;

&lt;p&gt;
To my dismay, I have discovered that the Netflix UI has changed. It used to work quite well. You would select your programming, watch it (including the credits), and when the programming ended so did playback and the software would wait for you to respond. Now the morons at Netflix have paid a team of programmers to "upgrade" things so that when the credits appear the program playback becomes a tiny window within your screen and you are presented with a shitty GUI to choose to begin the next program. The major flaw with this system is that the credits become too small to read. Granted, most of us usually ignore them, but sometimes you do want to watch them. Moreso, some programs actually present Easter eggs within the credits. These are now rendered unwatchable in my experience. The GUI prompt is also on a timer, and after a given time period (I think it's 30 seconds) expires the next episode is automatically run. Normally one would get up to get another beer here, but instead they have to fuck with a GUI and network delay to get into the next episode and pause it so that they can get up to do their business.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;It's fucking stupid.&lt;/b&gt; It accomplishes nothing and ruins the user-experience. At best, you want to watch the next episode immediately and either wait for the timeout to expire (&lt;strong&gt;if you're too goddamn lazy to push a button, which probably makes you a fat American&lt;/strong&gt;) or hit a button to trigger it immediately (which you could have done before so you haven't gained anything). At worst, you miss something in the credits that you were actually interested in, or have to wait around interacting with the software to get it to &lt;span style="text-decoration: underline;"&gt;motherfucking&lt;/span&gt; pause so you can get up to take a piss or get a new beer.
&lt;/p&gt;

&lt;p&gt;
I really wanted to rant directly to Netflix about this, but I can't seem to find any "report a bug" or equivalent feature within their Web site. The closest that I could find was the "Contact Us" page, which seemed to be phone only. Either their people are not in the office on a Sunday evening, are contracted call center personnel that don't represent Netflix at all, or I'm too drunk to have a meaningful conversation with them right now. In any case, they have even failed to provide me adequate means to provide them with constructive feedback. That which is, &lt;strong&gt;&lt;em&gt;&lt;span style="text-decoration: underline;"&gt;FUCK YOU, PUT IT BACK, YOU SHORT-BUS MOTHERFUCKERS&lt;/span&gt;&lt;/em&gt;&lt;/strong&gt; (sorry, I'm a little intoxicated, I mean you managers-trying-to-convince-your-higher-ups-that-you-actually-do-something-of-value-for-the-company-but-act-without-actually-comprehending-your-problem-domain-or-consulting-with-the-people-smarter-than-you-or-testing-the-solution-with-an-unbiased-sample-group).
&lt;/p&gt;

&lt;p&gt;
I appreciate you taking the time to read this. I hope that Netflix staff find it and use it to better their service so that I don't have to cancel my subscription and replace them with piracy.
&lt;/p&gt;

&lt;p&gt;
&lt;i&gt;Good day to you, sir.&lt;/i&gt;
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/u-K70-uq7-U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/6538596901740388793/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2012/06/what-fuck-netflix-new-ending-user.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/6538596901740388793?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/6538596901740388793?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/u-K70-uq7-U/what-fuck-netflix-new-ending-user.html" title="What. The. Fuck. Netflix. (The New Ending User Interface)" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2012/06/what-fuck-netflix-new-ending-user.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIEQHYzfip7ImA9WhVWFko.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-326747016225687999</id><published>2012-04-29T00:04:00.000-04:00</published><updated>2012-04-29T00:08:21.886-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-29T00:08:21.886-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="spray" /><category scheme="http://www.blogger.com/atom/ns#" term="gun-game" /><category scheme="http://www.blogger.com/atom/ns#" term="video game" /><category scheme="http://www.blogger.com/atom/ns#" term="evanescence" /><category scheme="http://www.blogger.com/atom/ns#" term="knife" /><category scheme="http://www.blogger.com/atom/ns#" term="counter-strike" /><title>New Counter-Strike Spray!</title><content type="html">&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-W-JUZslP53w/T5y1B15QhGI/AAAAAAAAAHw/e13SeVv-2Sc/s1600/freefall-spray-2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img alt="" border="0" height="320" src="http://2.bp.blogspot.com/-W-JUZslP53w/T5y1B15QhGI/AAAAAAAAAHw/e13SeVv-2Sc/s320/freefall-spray-2.jpg" title="I can haz &amp;quot;hugz&amp;quot;?" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;I can haz "hugz"?&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;This is my new Counter-Strike spray (with a lot of help from "allefant"). Players are able to spray their own images on the map surfaces while in-game. It's common to see jokes, as well as more "explicit" images. Some players choose to personalize them as well. Many players seem to only spray their spray after defeating somebody, sort of as a way to rub it in. For me, spraying my spray is part of my routine. My beginning-of-round routine basically consists of spraying my spray at my feet, followed by saying "Affirmative!" / "Roger that!" or "Sector clear!" on the team "radio", and rapidly switching back and forth between my gun and knife (sort of a warm-up routine).&lt;/p&gt;

&lt;p&gt;Since most won't get the caption, I'll try to explain it. I predominantly play the gun-game mod, and knifing is my preference. The object of the game is to "level-up" by getting kills, but you can steal other people's levels by knifing them. So a "hug" from me in-game means I was holding a knife when I did it, and you lost a level. You are usually left with either humiliation or frustration (or a smile). xD&lt;/p&gt;

&lt;p&gt;The name "FreeFall" was actually inspired by an Evanescence song, "Weight of the World". They are my favorite band and I was listening to The Open Door while trying to think of a handle. Unsurprisingly, most people think of "Free Fallin'" by Tom Petty when they see my name, and many attempt to sing the chorus in-game as a joke (sometimes to mock me). xD&lt;/p&gt;

&lt;p&gt;Amazingly, the first person to ever ask me about my name actually asked if it was from a song. I said yes, but probably not the song he was thinking of. To my amazement, he actually named Evanescence! I was amazed that somebody else would randomly get the reference. I don't think it has happened since. ^^&lt;/p&gt;&lt;!-- The font used in the image is Evanescent, which I think was designed for the band. I rather like it. --&gt;

&lt;p&gt;The "--beer" and "--music" bits were originally meant to resemble command line options, and were flags to indicate that I was drinking (so my general abilities might be compromised) or listening to music (so at least my hearing is compromised), respectively. Most people call me "Beer" instead of "FreeFall" though (some have even called me "Free Beer") so I often don't bother to remove the "flags" when they don't apply. They've basically become part of my name.&lt;/p&gt;

&lt;p&gt;I'd really like to post some YouTube videos of me playing, but I've been pretty unsuccessful in actually turning demos into video. I'm also a n00b when it comes to video authoring so I'm not sure how to edit out the boring parts. Maybe eventually... Another challenge is actually recording demos that are worth posting. ^^ I've had a few, but often I don't think to record until /after/ I've done something noteworthy. I digress...&lt;/p&gt;

&lt;p&gt;I've been playing Counter-Strike: Source for about 5 years now, I think. Until now I don't think I've changed my spray since I first created it. My original spray was a drawing of an angel girl that I ripped from a tattoo artist's Web site. If I had the artistic ability I would have removed or crippled one of the wings in reference to Sephiroth from FFVII, but alas I do not. I often use FFVII: Advent Children graphics for avatars as well (I think my Steam one still is).&lt;/p&gt;

&lt;p&gt;Maybe someday I'll preach about the honorable way to play Counter-Strike. :) For now, you'll just have to enjoy my spray.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/AOLy2X2i0EA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/326747016225687999/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2012/04/new-counter-strike-spray.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/326747016225687999?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/326747016225687999?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/AOLy2X2i0EA/new-counter-strike-spray.html" title="New Counter-Strike Spray!" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-W-JUZslP53w/T5y1B15QhGI/AAAAAAAAAHw/e13SeVv-2Sc/s72-c/freefall-spray-2.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2012/04/new-counter-strike-spray.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMMSHwzeSp7ImA9WhJTGUk.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-583884602384622456</id><published>2012-04-23T20:40:00.000-04:00</published><updated>2012-06-29T00:18:09.281-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-06-29T00:18:09.281-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="mercurial" /><category scheme="http://www.blogger.com/atom/ns#" term="git" /><category scheme="http://www.blogger.com/atom/ns#" term="dvcs" /><category scheme="http://www.blogger.com/atom/ns#" term="hg" /><category scheme="http://www.blogger.com/atom/ns#" term="branching" /><title>DVCS Branching - Why Mercurial Sucks and What Mercurial Advocates Are Missing</title><content type="html">&lt;h3&gt;Introduction&lt;/h3&gt;&lt;p&gt;Version control software is basically used to track changes to files (usually plain-text files) over time. It is primarily used by software developers to track source code changes. It used to be that a centralized server was used to host a centralized repository of changes, and clients would connect to it to checkout or check-in changes.&lt;/p&gt;&lt;p&gt;Then came a revolution known as distributed version control. This model basically eliminates the centralized repository and gives every user (developer) their own complete clone of the repository.&lt;/p&gt;&lt;p&gt;There are basically two popular distributed version control systems (DVCS) available right now, and both are free/libre/open source software. They are Git and Mercurial. Both were developed to replace BitKeeper for use in maintaining the Linux kernel, but only one was written by Linus himself so the other one sucks. ;) There are other free ones and also commercial ones, but I don't know of any good reason to use them. Linus Torvalds did at one point say that if for whatever reason you needed a commercial DVCS that BitKeeper was the one to use.&lt;/p&gt;&lt;p&gt;Git and Mercurial work relatively similarly. As far as I know, they both represent history internally using a directed acyclic graph (DAG). Combining this with the distributed design means that branching and merging happens to work quite well in both (it has to). Effectively, whenever you work in a separate clone of the repository you create a new physical branch, even if you don't mean to. As a result, you often have to merge when you synchronize your local repository. That only tells part of the story of branching though.&lt;/p&gt;&lt;h3&gt;Branching In Mercurial&lt;/h3&gt;&lt;p&gt;The Mercurial community has basically come up with 3 branching strategies that you can use with Mercurial. The reason there's so many of them is because none of them really works very well. Proponents look for ways to justify these misfeatures, but when you actually put the strategies into practice you see that it's nonsense.&lt;/p&gt;&lt;h4&gt;Named Branches&lt;/h4&gt;&lt;p&gt;The core branching feature is called named branches. This is what the `&lt;tt&gt;hg branch&lt;/tt&gt;' and `&lt;tt&gt;hg branches&lt;/tt&gt;' commands manage. The implementation is a little bit finicky. It's effectively a name attribute stored with each changeset. That's all there really is to it. I imagine it must have been inspired by Subversion properties or something silly like that. The way it works is that you are basically free to set the working branch name anytime you want with `&lt;tt&gt;hg branch &lt;em&gt;&amp;lt;name&amp;gt;&lt;/em&gt;&lt;/tt&gt;'. The attribute is remembered and when you commit it is stored in the changeset as a permanent part of history. The head of a named branch is basically the most recent commit with that branch name.&lt;/p&gt;&lt;p&gt;Branches in Mercurial are shared by default. If you want to only synchronize selective branches then you need to explicitly specify only the ones you want synchronized with every relevant command. I find it extremely tedious. It basically makes it difficult for anyone to work under the radar without distracting others. In a big project you can expect that many people are not going to be interested in the work that others are doing. You're only really interested in the parts of the project that impact you in some way and don't want to be bothered with all of the other noise. Mercurial makes it difficult to do that.&lt;/p&gt;&lt;p&gt;Another problem is that in this distributed environment there is only one namespace for branches, and branches are permanent parts of history. This means that bad things can happen if two completely distinct developers happen to choose the same branch name for different branches. It also means that if you just want to create an experimental branch and throw it away later then you are forced to share it with the world anyway (short of history editing, which the Mercurial community also discourages). A workaround that was added later is `&lt;tt&gt;hg commit --close-branch&lt;/tt&gt;', which basically adds a new changeset with a new special attribute to indicate that the named branch is "closed" (i.e., finished with). This basically just tells Mercurial to hide it from view by default. The branch is automatically reopened though if you accidentally commit onto it again. In my experience, it's very difficult to keep things straight when the entire universe's branches are always available to the `&lt;tt&gt;hg update&lt;/tt&gt;' command, which is used to update your working copy to some specific version (e.g., the latest) &lt;span style="text-decoration: underline;"&gt;as well as change branches&lt;/span&gt;.&lt;/p&gt;&lt;h4&gt;Repository Clone&lt;/h4&gt;&lt;p&gt;What the Mercurial community would have you do for experimental changes that you may wish to throw away is clone the repository again and work in a throw-away repository instead. Due to the nature of the distributed DAG history you are actually automatically branching whenever you work on a cloned repository. This solution does work, but it's very Subversion-like in that you have to manually manage "branches" within your file system. Mercurial will use hard links on supported file systems to save space if you clone locally (basically each clone will share whatever physical files on disk that it can), but that's little comfort. In my experience, this strategy doesn't work overly well, and it clutters your project namespace (e.g., I generally keep all source code projects in &lt;tt&gt;~/src&lt;/tt&gt; or &lt;tt&gt;"%USERPROFILE%\src"&lt;/tt&gt;).&lt;/p&gt;&lt;p&gt;You also can't directly share these experimental changes with other people or with a shared remote repository without pushing a new anonymous head and screwing everybody up. At best you could tell your collaborator to explicitly clone anew, and keep the history separate from the main history, just as you have done, but that's a very error-prone approach.&lt;/p&gt;&lt;h4&gt;Bookmarks&lt;/h4&gt;&lt;p&gt;A lot of Git users complain about the branching options in Mercurial, and the Mercurial community's solution was to offer an alternative branching mechanism that is similar to how branches work in Git. The solution is called "bookmarks". Bookmarks began as a Mercurial extension, but has since been merged into the core.&lt;/p&gt;&lt;p&gt;A bookmark is basically an association between a name and a commit identifier. These associations are stored in &lt;tt&gt;.hg/bookmarks&lt;/tt&gt;. When you commit on a bookmark the bookmark is automatically updated to point to the new head. Since bookmarks are stored outside of history they can be easily created, renamed, and deleted at any time. They are very lightweight compared to the other branching options. Indeed, they are similar to a Git branch. The problem with bookmarks is that they aren't shared by default. If you choose to use bookmarks to manage branches in your project then you either have to tediously communicate each bookmark with all collaborators or risk them getting quite confused because there will be multiple seemingly anonymous branch heads when they pull your branches in.&lt;/p&gt;&lt;p&gt;Basically, you have to explicitly push bookmarks with `&lt;tt&gt;hg push -B &lt;em&gt;&amp;lt;name&amp;gt;&lt;/em&gt;&lt;/tt&gt;', and explicitly pull them with `&lt;tt&gt;hg pull -B &lt;em&gt;&amp;lt;name&amp;gt;&lt;/em&gt;&lt;/tt&gt;'. You can check for new bookmarks in the remote repository with `&lt;tt&gt;hg incoming -B&lt;/tt&gt;'. Unfortunately, this puts a lot of responsibility on the collaborators to manually keep bookmarks up to date. The documentation says that once both sides of the connection have a bookmark it will be updated automatically, but that's not much help. Without them, at least, Mercurial is quite happy to jump branches on you without really raising any alarms (I'm not sure it would stop you even with the bookmarks, to be honest, though I've been told it will). The current implementation is just not sufficient for real world usage. Most of my collaborators are even in the same room. I can't imagine if you had people spread out across a building or the world.&lt;/p&gt;&lt;h3&gt;Branching In Git&lt;/h3&gt;&lt;p&gt;Git is quite smart about most of what it can do. A branch in Git is a much more logical concept. You get to think of Git branches as tangible things. There is basically only one strategy for branching and it works quite well. Perhaps most importantly it's distribution friendly.&lt;/p&gt;&lt;p&gt;Git manages your branches for you. A branch is actually represented by a very light file that basically just references the head commit identifier of the branch. This allows you to create, rename, and delete branches at will, very efficiently, similar to Mercurial bookmarks. Git takes care of efficiently storing the actual history for you so you can be sure that it doesn't duplicate anything needlessly. The operation of switching branches is also different than the operation of updating your &lt;s&gt;working copy&lt;/s&gt; &lt;i&gt;[fix:] local branches&lt;/i&gt; (`&lt;tt&gt;git checkout&lt;/tt&gt;' and `&lt;tt&gt;git pull&lt;/tt&gt;'[&lt;a href="#dvcs-branching-why-mercurial-sucks-ref1" id="dvcs-branching-why-mercurial-sucks-ref1-src"&gt;1&lt;/a&gt;], respectively).&lt;/p&gt;&lt;p&gt;Git branches are not all implicitly synchronized with a remote repository. If you `&lt;tt&gt;git push&lt;/tt&gt;' then only the current branch's commits will be pushed. Similarly, when you `&lt;tt&gt;git pull&lt;/tt&gt;' then only the current branch will be pulled. Branches are namespaced by repository. Local ones are just in the global namespace, but remote branches are namespaced by the remote name. For example, origin/master refers to the 'master' branch of the remote 'origin'. This puts the user in complete control of their repository. The remote might refer to a branch as 'foo', but you can use a local branch named 'bar' if you want to. Git doesn't implicitly synchronize any branches with remote branches. You have to explicitly associate them in configuration before they will implicitly be synced. Otherwise, you can explicitly choose which local and remote branches you want to synchronize.&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;Mercurial proponents typically try to defend Mercurial's branching madness with excuses that branches should be permanent history and that it's a &lt;i&gt;feature&lt;/i&gt;, not a bug. The reality is that Mercurial has a very non-distributed nature to its branching policies. Instead of letting each user be in control of his view of the world, the Mercurial community seems to be in favor of encouraging everybody to always share the entire world with the entire world.&lt;/p&gt;&lt;p&gt;In short, Git is a much better DVCS for branching (and in my experience, pretty much everything else too, but I digress). Mercurial is a good alternative for people that need extra hand holding and don't plan to make extensive use of the tools. Branching in Mercurial is so painful that we basically don't do it where I work, just as we didn't really do it with Subversion. The little bit of branching that we do do ends up being ugly and distracting and it's very error-prone in my experience.&lt;/p&gt;&lt;h3&gt;References&lt;/h3&gt;[&lt;a id="dvcs-branching-why-mercurial-sucks-ref1" href="#dvcs-branching-why-mercurial-sucks-ref1-src"&gt;1&lt;/a&gt;] - `&lt;tt&gt;git pull&lt;/tt&gt;' is equivalent to `&lt;tt&gt;git fetch &amp;&amp; git merge&lt;/tt&gt;'. Note that if you're wise to it then you can use &lt;tt&gt;git rebase&lt;/tt&gt; instead of &lt;tt&gt;git merge&lt;/tt&gt; if you want to linearize [local] history.&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/nnPORDsN_nI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/583884602384622456/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2012/04/dvcs-branching-why-mercurial-sucks-and.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/583884602384622456?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/583884602384622456?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/nnPORDsN_nI/dvcs-branching-why-mercurial-sucks-and.html" title="DVCS Branching - Why Mercurial Sucks and What Mercurial Advocates Are Missing" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>3</thr:total><feedburner:origLink>http://www.bamccaig.com/2012/04/dvcs-branching-why-mercurial-sucks-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUINQn06fip7ImA9WhdWEE8.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-6974716381035647538</id><published>2011-09-01T20:56:00.023-04:00</published><updated>2011-09-03T00:19:53.316-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-03T00:19:53.316-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="xhtml" /><category scheme="http://www.blogger.com/atom/ns#" term="Eisley" /><category scheme="http://www.blogger.com/atom/ns#" term="Michelle Branch" /><category scheme="http://www.blogger.com/atom/ns#" term="lyrics" /><category scheme="http://www.blogger.com/atom/ns#" term="dtd" /><category scheme="http://www.blogger.com/atom/ns#" term="CD" /><category scheme="http://www.blogger.com/atom/ns#" term="xml" /><category scheme="http://www.blogger.com/atom/ns#" term="music" /><category scheme="http://www.blogger.com/atom/ns#" term="Eminem" /><category scheme="http://www.blogger.com/atom/ns#" term="html" /><category scheme="http://www.blogger.com/atom/ns#" term="xslt" /><category scheme="http://www.blogger.com/atom/ns#" term="css" /><title>"-//CS//Lyrics 1.0//EN" AKA Lyrics DTD/XML/XSL/XHTML/CSS</title><content type="html">&lt;p&gt;I know I hinted towards a technical blog post last time. I expected my next post to be about Java or Perl Web programming. Well, that one is coming, I suppose. I am making ground and eventually would like to write about it. I don't think I'm quite satisfied with my progress yet though (nor am I so dissatisfied that I need plea for help yet). This post is somewhat technical, but it's also somewhat personal.&lt;/p&gt;

&lt;p&gt;I love to sing. I sing all the time. In the shower, in my car, at the computer, etc. Everywhere, basically. I love music and singing is one of the things that I like most about it. Or perhaps, more accurately, the lyrics are. I love lyrics. For me to like a song the lyrics had better be clever. You get bonus points if they're emotional or humorous and I can relate to them. I guess then it shouldn't be surprising that I also love to rap.&lt;/p&gt;

&lt;p&gt;Indeed, it wouldn't be an exceptional day for somebody to notice me driving by them in Sault Ste. Marie with my stereo cranked, singing or rapping along. Whenever I hear a new song that I like I'll look up the lyrics online (in my younger days I would do it from the CD cover, but a wide screen monitor is a lot bigger and easier to read from in the dark). I'll then practice reciting them while listening to the song(s) on repeat. This could mean hearing a new or leaked song on YouTube and just starting the same video over and over again (shout out to &lt;a href="http://www.youtuberepeat.com/"&gt;YouTube Repeat!&lt;/a&gt;!). More often, though, it means that I just bought a new CD from one of my many preferred artists and am listening to it on repeat.&lt;/p&gt;

&lt;p&gt;The Amarok media player is great for this because it supports a lyrics module that allows you to view the lyrics for the current song right there in the media player automatically! It's great for people like me that enjoy analyzing and learning the lyrics of a song, or even just singing them after 6 or 8 months have gone by and they aren't fresh in your mind anymore.&lt;/p&gt;

&lt;p&gt;Often I find that the various lyrics sites online don't have 100% correct lyrics. Some people do a really bad job of transcribing them. I don't know if it's laziness or incompetence, but I personally find it really annoying when I'm trying to sing or rap along to a new song and I'm stumbling over the wrong lyrics on a page.&lt;/p&gt;

&lt;p&gt;It seems that most user maintained lyrics sites online make it difficult to contribute though. You generally have to register with some random site, which means a whole new set of credentials to remember, and a whole new server with your personal information just waiting to be hacked and compromised. They also don't do a very good job of describing the lyrics or presenting them to the user. There's usually a mix of slang and unslang and it's never really clear when the transcriber is sure of a particular word or just making an [un]educated guess. You may be surprised just how often a word or two is a mystery in very popular songs.&lt;/p&gt;

&lt;p&gt;This combined with my thirst for XML knowledge lead me to develop a simple XML document type for lyrics. It's a work in progress so nothing is feature-frozen yet. Currently I have a document type definition (DTD) to validate against, an XSL stylesheet to transform the lyric data into standards compliant XHTML 1.0 Strict, and a CSS stylesheet to accompany the XHTML output to prettify the output a bit (&lt;del&gt;no guarantees that the CSS is standards compliant, unfortunately&lt;/del&gt; &lt;ins&gt;and the W3C's validator seems to think it is valid CSS 2.1 also!&lt;/ins&gt;).&lt;/p&gt;

&lt;p&gt;Currently I only have two songs transcribed. The first is We As Americans, by Eminem, from the Encore bonus CD. That was basically my initial test drive, if you will, for the format and stylesheets.&lt;/p&gt;

&lt;p&gt;Two evenings ago I got to thinking about one of my favorite bands, Eisley, and wondering if they had released any new albums since I last checked. It turns out that indeed they had! The Valley was released back in March. I made it a plan to stop by the music store yesterday to look for it. Now Eisley is somehow still somewhat of a below-the-radar band, which is puzzling because they're so amazingly talented and creative and &lt;em&gt;beautiful&lt;/em&gt;. In any case, a side effect of this is that music stores often don't stock them (some of them don't even have a listing for them!). I could rant all day about music stores and the music industry and how stupid things have gotten (see my &lt;a href="http://www.bamccaig.com/2011/08/what-happened-to-film-industry.html"&gt;previous post&lt;/a&gt; about the movie industry for a hint of what to expect), but I'll try to avoid doing that now and save it for another post.&lt;/p&gt;

&lt;p&gt;Long story short, I went out to buy one CD and came home with seven...and none of them was the one that I went out for! &lt;em&gt;"But, Brandon,"&lt;/em&gt; I hear you wondering. &lt;em&gt;"What about Eisley?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Oh, I ordered it. &lt;em&gt;Don't you worry.&lt;/em&gt; As expected, the music store didn't have it in stock. Unfortunately, it cost me $20 to order through the store. The night before I had seen that the online retailer linked from the band's Web site was only asking $10. Without knowing the shipping fees though it's hard to say if I'm any worse off. Not that I'd mind buying an Eisley CD for $20. In fact, I think I probably paid $20 for each of Room Noises and Combinations too. What bothers me is that Eisley will only see a tiny percentage of that, if any, and the crappy music stores and record companies that I'm avoiding ranting about will take most of the profit.&lt;/p&gt;

&lt;p&gt;Moving on, the CDs that I actually came home with were as follows in no particular order: Theory of a Deadman (Theory of a Deadman), The Truth Is... (Theory of a Deadman), Taylor Swift (Taylor Swift), Greatest Hits (Guns N' Roses), 22 More Hits (George Strait), American Saturday Night (Brad Paisley), and This Is Country Music (Brad Paisley). I was also trying to buy or order The Spirit Room (Michelle Branch), but not only did the music store (the only one left in my city to my knowledge) not have any Michelle Branch CDs in stock, but her CDs weren't even available from their distributor peoples! &lt;em&gt;Ugh.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So anyway, having bought all of these CDs, I then had to rip them into my computer so I could sync them with my iPod (I could also rant about how much Apple sucks, but there's a different time and post for that too). In doing so, I found myself listening to old and new music and singing along and by the time 2 AM rolled around I wasn't really tired. In fact, I couldn't seem to stop singing or rapping. I tried going to bed, but eventually just started to rap the lyrics to one of the songs I had written back in high school, and then decided that I should transcribe it and publish the lyrics on my site. That way I'd have a digital copy (it was quite a challenge to remember the words after so many years), since I had destroyed, lost, or abandoned all others copies. I eventually gave in and decided to do this &lt;strong&gt;immediately&lt;/strong&gt; while it was fresh in my mind (both to do it and most of the lyrics).&lt;/p&gt;

&lt;p&gt;So the second song I have transcribed is actually my own! It's called &lt;a href="http://lyrics.castopulence.org/wanna_be_god.xml"&gt;Wanna Be God&lt;/a&gt;. I separated it into a separate file to initially keep it off the radar and also to keep it separate from "real" songs. :) There are a few other songs that I wrote back in high school. One or two of them I might feel are worth publishing online also. We'll see if I ever get around to that.&lt;/p&gt;

&lt;p&gt;In the meantime, please check out my XML document type, and accompanying DTD, XSLT, and CSS. And, of course, check out the lyrics to both songs:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;a href="http://lyrics.castopulence.org/"&gt;http://lyrics.castopulence.org/&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/lyrics.xml"&gt;http://lyrics.castopulence.org/lyrics.xml&lt;/a&gt; (This is the core document that I'll currently put all lyrics in, though eventually I might split them each into their own file)
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/lyrics.xsl"&gt;http://lyrics.castopulence.org/lyrics.xsl&lt;/a&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/lyrics.dtd"&gt;http://lyrics.castopulence.org/lyrics.dtd&lt;/a&gt;
        (&lt;a href="https://www.castopulence.org/2011/09/lyrics"&gt;https://www.castopulence.org/2011/09/lyrics&lt;/a&gt;)
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/lyrics.xhtml"&gt;http://lyrics.castopulence.org/lyrics.xhtml&lt;/a&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/lyrics.html"&gt;http://lyrics.castopulence.org/lyrics.html&lt;/a&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/lyrics.css"&gt;http://lyrics.castopulence.org/lyrics.css&lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;a href="http://lyrics.castopulence.org/wanna_be_god"&gt;http://lyrics.castopulence.org/wanna_be_god&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/wanna_be_god.xml"&gt;http://lyrics.castopulence.org/wanna_be_god.xml&lt;/a&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/wanna_be_god.xhtml"&gt;http://lyrics.castopulence.org/wanna_be_god.xhtml&lt;/a&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;a href="http://lyrics.castopulence.org/wanna_be_god.html"&gt;http://lyrics.castopulence.org/wanna_be_god.html&lt;/a&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually I'd like to implement a server-side backend to manage them (once I get either a Java or Perl Web environment setup to my satisfaction, of course ;), at which point I could potentially open the system up to open ID users and let the world help me to fix lyrics. I'm not yet certain where I stand with regards to copyright infringement though. Technically I am violating Eminem's (or his record company's) copyright by publishing the lyrics to his song without permission. I do have a disclaimer on the page to highlight the fact that I don't own the copyright... It seems harmless to me, but nevertheless, there is always the possibility that somebody will feel they are losing money as a result and try to convince a judge to make me reimburse them... Hopefully that doesn't happen... Honestly, I can't even figure out how I would contact Eminem or Interscope to ask for permission. Their Web sites are terrible. :P&lt;/p&gt;

&lt;p&gt;If you are or represent a copyright owner and wish for me to have a work removed from my site then please kindly address an E-mail to me (bamccaig at castopulence dot org) detailing who you are and what relationship you have to the copyrighted work, and the copyright owner's wishes, and I will promptly add any disclaimers you want or, if necessary, lock that work up behind a secured connection for personal use only.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/TLLD2G5q_9c" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/6974716381035647538/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2011/09/cslyrics-10en-aka-lyrics-dtdxmlxslcss.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/6974716381035647538?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/6974716381035647538?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/TLLD2G5q_9c/cslyrics-10en-aka-lyrics-dtdxmlxslcss.html" title="&quot;-//CS//Lyrics 1.0//EN&quot; AKA Lyrics DTD/XML/XSL/XHTML/CSS" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2011/09/cslyrics-10en-aka-lyrics-dtdxmlxslcss.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Dk4MRn49eSp7ImA9WhJTGUk.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-824801111147117916</id><published>2011-08-24T01:18:00.002-04:00</published><updated>2012-06-29T00:43:07.061-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-06-29T00:43:07.061-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="hollywood" /><category scheme="http://www.blogger.com/atom/ns#" term="film industry" /><category scheme="http://www.blogger.com/atom/ns#" term="film" /><category scheme="http://www.blogger.com/atom/ns#" term="critique" /><category scheme="http://www.blogger.com/atom/ns#" term="movie" /><category scheme="http://www.blogger.com/atom/ns#" term="critic" /><title>What Happened To The Film Industry?</title><content type="html">I've had this tab open for a week or more because I've wanted to write another technical blog post about something, but a topic to write about just never really came to mind. [Many more days later I now realize that my next technical blog post should be about my attempts to learn Java and Perl Web programming and my struggles with that.] So instead, I'm going to rant about something non-technical: movies.&lt;br /&gt;
&lt;br /&gt;
I'm sort of out of the loop on movies lately. I've only seen a small fraction of new releases in the past few years. Which is a shame because I do rather enjoy movies. I also enjoy criticizing them.&lt;br /&gt;
&lt;br /&gt;
On Wednesday I decided to rent some movies out of the blue. There was only really one that I had in mind when I went to the video store: The Adjustment Bureau. I wasn't sure exactly what it was about, but I knew that Matt Damon stars in it and his movies are usually pretty good (e.g., the Bourne series), so that was enough for me to invest in it. But here I find my first criticism of the movie industry as of late: either &lt;strong&gt;they fail horribly to advertise in this Internet age&lt;/strong&gt; or else &lt;strong&gt;there's actually very little good to advertise&lt;/strong&gt;.&lt;br /&gt;
&lt;br /&gt;
I think it's actually a lot of both. I do watch TV relatively often, but I rarely see movie previews anymore, and almost never see good ones. Online, where I spend the vast majority of my time, I never see movie ads. In fairness, that could be attributed to the &lt;u&gt;AdBlock Plus&lt;/u&gt; Firefox extension, which does an AWESOME job of hiding pretty much every ad on the Web. Especially ads that don't really belong on a particular page (i.e., third party ads). However, I don't actually mind clever, non-intrusive advertisements. In fact, I rather enjoy them. Still, I block all advertisements on the Web because every schmuck and his grandmother have their Web sites filled with intrusive and annoying ads about completely retarded "products" and "services" that nobody in their right mind would invest in. That, or the ads are incredibly intrusive and slow you down. So I just filter them out of my Web, see none of them ever, forget all about their existence, and guarantee that basically no site will ever get any ad revenue from me.&lt;br /&gt;
&lt;br /&gt;
So I'm left wondering, do the movies made today suck or am I just not getting the previews for the good ones due to ad blocking? I can't even really remember watching any truly good movies in the past few YEARS. Perhaps I'm just spoiled by video games and as a consumer of art have become superior to mere movie audiences, expecting more? Perhaps not. Still, it seems that all of the movies that I watch nowadays just suck. The characters are lame and uninteresting. The story doesn't really make any fucking sense at all. The action sequences defy all understanding of the laws of physics and logic, both in the real world and the movie universe too. Basically the entire movie is either predictable, ridiculous, or both.&lt;br /&gt;
&lt;br /&gt;
On this particular visit to the video store I did what I usually do: I walked from one end of the store to the other, browsing all of the new releases to find which ones seemed most interesting. Which in the past few years has actually turned into &lt;em&gt;interesting at all&lt;/em&gt;. For the most part, I wasn't really excited about anything other than The Adjustment Bureau. In fact, had I been interested only in getting good movies, I probably would have only even rented The Adjustment Bureau, since it's the only one that actually looked good to me. For some reason though I felt like getting movies that I thought would probably suck, hoping to be proven wrong and perhaps restore some faith in the movie industry. The movies I rented were as follows (in the approximate order that I found them in the video store): For Colored Girls, Battle: Los Angeles, Drive Crazy, Just Go With It, Gun, and The Adjustment Bureau. I was tempted to also get Source Code (being a computer programmer those words have a special meaning to me), but from what I could recall from previews and what I could gather from the back of the case it didn't seem even related to computer programming so I expected I would be disappointed. [I happened to see a preview shortly after that made me think I &lt;u&gt;might&lt;/u&gt; enjoy it, but nevertheless I didn't think I would when I rented these movies.]&lt;br /&gt;
&lt;br /&gt;
I'll now discuss the movies in the order that I watched them. I watched the movies in what I predicted to be worst-to-best order, with the exception of Gun and For Colored Girls since at random and on the spot I had rented them for an extended period of time to avoid late fees. I'll try to flag spoilers, but no promises.&lt;br /&gt;
&lt;h4&gt;Day One&lt;/h4&gt;I couldn't really remember any previews for Battle: Los Angeles in the video store, but I had a feeling that it would suck. Nevertheless, it was a Sci-Fi/"war" movie (both things that I like under the right circumstances) and it was one of the movies that the video store has a ton of copies of so it was obviously supposed to be a popular title (of course, popular certainly doesn't mean good, and often means quite the contrary). Again, against my better judgement, I decided to get it anyway. It was indeed terrible. Primarily because of lazy writing (perhaps Hollywood fired all of the talented writers?). &lt;strong&gt;(SPOILER ALERT; skip to the next paragraph if you care)&lt;/strong&gt; For example, the aliens that attacked the Earth came from &lt;strong&gt;space&lt;/strong&gt; inside of what were identified as meteorites, IIRC. The American forces mobilized shortly after realizing the potential threat, but naively identified that the invading forces were grounded, assuming that they had no air forces and that the air would belong to the humans. Of course, it doesn't take long for advanced enemy aircraft to appear, wreaking havoc on the U.S. forces. That's just a very simple example. I could write an entire blog post about all of the things wrong with this movie if I cared. Maybe even turn it into a series of posts. I digress... I did like some of the characters (familiar actors and actresses helped with that), but ultimately it was just a disappointment.&lt;br /&gt;
&lt;h4&gt;Day Two&lt;/h4&gt;Next I watched Drive Angry. I had low expectations for this one as well. Again, I couldn't remember any previews, at least not in detail, but for some reason the movie reminded me of Ghost Rider, another Nicolas Cage movie that I found terribly disappointing. IIRC, Ghost Rider had a certain "cheap" quality to it, and I got the same first impression from Drive Angry. Again, I rented it just in case I was wrong, but unfortunately I was not. The movie made basically no sense, and while it was clear from the special features that the crew enjoyed making the movie (which is all well and good for them), some even citing it as some of their best work, I personally thought that it sucked horribly. I'm not criticizing the actors or actresses so much as the overall story and script. &lt;strong&gt;(SPOILER ALERT; skip to the next paragraph if you care)&lt;/strong&gt; For example, Cage's character, who is an escapee from Hell (the fiery one from Christian fiction, which in this movie is portrayed as a more traditional prison with a supernatural flare), walks into a church to rescue his infant granddaughter from a Satanic cult planning to murder her as a sacrifice for Satan. His granddaughter is laying down in a basket or something at the front of the church where a church leader would normally be preaching from, but foolishly Cage's character just walks in silently as if to avoid disturbing the service, and eventually gets disarmed when the entire church basically pulls a gun on him. Sure, he's "undead", so practically speaking he can't really be hurt, but his granddaughter is not so undead so I consider it rather risky to allow himself to be so easily captured. Personally, it frustrates the Hell out of me whenever a protagonist in a movie makes a foolish mistake like that. It's like they aren't even trying to ensure success. Again, there are many more examples of stupidity that I could cite from this movie. That wasn't even a very good example, but it has been a few days since I saw the movie and it wasn't all that memorable, so that's the best that I've got. Even the characters seemed ridiculous in this movie. Their actions do not make sense. During the special features it was explained that they didn't really go through a whole lot of revisions with this film's script. They basically decided to do a movie of this style, wrote up a quick draft in a couple of weeks, and submitted the first version. Apparently it was praised for it's originality (it wasn't original at all) and creativity. I guess people thought it had a pure, untainted artistic quality to it. I disagree. It didn't fit together well. It lacked polish. It wasn't well thought out, or perhaps thought out at all. I guess they figured they could substitute an interesting script with guns, tits, and cars. While these three things are all good, they alone don't make for an interesting movie.&lt;br /&gt;
&lt;br /&gt;
Next I watched Just Go For It, an Adam Sandler film. Adam Sandler is awesome and I like pretty much all of his movies. For some reason though I didn't have high hopes for this one. Perhaps because I had already been let down by Grown Ups. Perhaps I had seen previews that tipped me off. As usual, I was right. This movie was pretty terrible. Let me also say that I love Jennifer Aniston: Friends is one of my favorite all time shows (I have the entire series on DVD). I think in just about every movie I've seen Anison in though (at least since Friends ended) I've been disappointed. Her acting seems blatantly poor. To give her the benefit of the doubt, I choose to believe that it's because she played Rachel Green for so long that it became a part of who she is, making it difficult to portray other characters. Just Go With It lacked structure and polish. Watching the film I got the distinct feeling that the movie was designed to incorporate improvisation by the actors and actresses into their roles. Given the title and Adam Sandler's roots, this wasn't really a surprise. However, it was clear that many of the cast members were terrible at improv, and as a result I think the performances suffered. I noted a few moments where Jennifer Aniston appeared to screw up her lines. I guess since the movie was somewhat improvised they decided not to bother with too many (any?) retakes, but in my opinion this hurt the quality of the film. Perhaps worst of all was the improv of Nick Swardson, which often seemed to be of a somewhat vulgar nature (which seemed out of character for the movie). I remember a particular scene where the two young children were present that particularly struck the wrong nerve with me; he even tried to involve them in the dialogue when the adults withdrew. I also got the distinct impression that his "improv" on numerous occasions bordered on sexual harassment towards the female cast members, and in particular Jennifer Aniston. While I can commend them for doing something different and risky, especially so for those that were clearly outside of their comfort zone with the improv, I have to criticize the film for poor performances. Adam Sandler did a decent job of it, which is no surprise, but even he seemed to struggle with it at times. I might have been more open to the idea if it was made blatantly clear that it was improvised so I wasn't blindsided by it. All of that out of the way, I did like parts of the movie, and enjoyed some of the characters. &lt;strong&gt;(SPOILER ALERT; skip to the next paragraph if you care)&lt;/strong&gt; I liked the way Sandler's and Aniston's characters fell for each other in the end; that sort of made up for the amateur improv in the end.&lt;br /&gt;
&lt;h4&gt;Day Three&lt;/h4&gt;I finally got around to watching The Adjustment Bureau. I loved it. I was a little bit afraid of it going in a direction that I wouldn't like, but instead it remained interesting and gripping and well put together. There were a few unexpected twists that I really enjoyed. I loved the characters and I loved the story. It was actually so emotionally jarring (?) that I was hurting myself with the rate at which I was drinking bottled water (the water being a substitute for beer). I love to feel emotions through art, especially strong ones, and this movie delivered for me. I really enjoyed Matt Damon's and Emily Blunt's characters. &lt;strong&gt;(SPOILER ALERT; skip to the next paragraph if you care)&lt;/strong&gt; I initially expected the Adjustment Bureau to be a purely political force. Something relating to corrupt government and potentially secret societies and things of that nature. This was interesting to a point, but it would have ultimately frustrated and bored me. I found the change of direction that the movie takes unexpected and very welcome. It was timed very well too. The "twists and turns" were well executed. I really liked the unique and original take on the creator concept (I am, however, an atheist). It was an interesting concept the way the Adjustment Bureau was run formally and like a company hierarchy. I particularly liked how it was pretty much completely independent of the religious beliefs that I imagine inspired it. I thought it was just brilliant.&lt;br /&gt;
&lt;h4&gt;Day Five (?)&lt;/h4&gt;I hadn't heard of For Colored Girls, but it seemed like a movie that I would be interested in so I read the synopsis on the back and decided to get it &lt;em&gt;just in case&lt;/em&gt; I'd like it. I watched it next. It wasn't at all what I expected. I have mixed feelings about it. I didn't really enjoy it, but perhaps that was the point. I will say this though: it's a Tyler Perry movie. I wouldn't even know who that is if it weren't for one thing: I have a brother that &lt;em&gt;loves&lt;/em&gt; the "Madea" movies. I personally think they're highly overrated and as a result feel the same way about Tyler Perry, since he seems to be the mind behind them all. In a way, I guess For Colored Girls was more or less just another Madea movie without Madea. In fact, some of the cast seemed to be the same, and though I never pay much attention when I get pressured into watching a Madea movie, I think some of the shared cast members even played a similar role. The title sounded to me like it was supposed to send a positive message, but I didn't really get one at all from this movie. I will say this though: it was nice to see some actresses that I haven't seen in a while, such as Whoopi Goldberg and Phylicia Rashād.&lt;br /&gt;
&lt;h4&gt;Day Seven (?)&lt;/h4&gt;Gun is a 50 Cent movie, so that's pretty much the only reason that I rented it. I watched it last for no good reason at all. It was undoubtedly the worst movie of all six, and with this selection that is saying something. It looked pretty low budget (indeed, Wikipedia says $10 million) so I wasn't really expecting much. Nevertheless, I like a lot of 50 Cent's music and I liked the Get Rich Or Die Tryin' movie. I suspect that Before I Self Destruct (the movie) was also low budget so I expected something on par with that, which itself wasn't very good. Indeed, Gun had a similar low-quality feel to it. I think probably what I found the worst was the dialogue, which didn't really feel natural, and even seemed improvised at times. I don't know if the script was just poorly written or if it was intentionally left with holes in it for the actors to fill, but next time I think I'd rather see a fully thought-out script. :-X &lt;strong&gt;(SPOILER ALERT; ... but you probably shouldn't care here)&lt;/strong&gt; The movie reveals that Kilmer's character is a rat sometime near the middle of the movie, but towards the end I actually expected a twist where it turned out that he had been loyal and another character had been the rat. Instead, the movie remained blatantly predictable with Kilmer's character actually being the rat. Perhaps the worst part was Jackson's character not figuring this out. It's painful to watch most of the dialogue in the film, but in particular there is a moment where Jackson's character invites Kilmer's character to a warehouse to talk in person, and then invites him to take a walk through the warehouse, etc. The dialogue is very hesitant and awkward here, and you're almost sure that Jackson's character MUST know that Kilmer's character is the rat. Indeed, he begins to question him about it, but then he foolishly just takes Kilmer's character's awkward word for it and instead makes him his "number one" (I forget exactly how they worded it in the movie, but the idea was that he was basically considered the most trusted). It's quite literally painful to watch how bad it is...&lt;br /&gt;
&lt;h4&gt;Summary&lt;/h4&gt;Overall, I rented 6 movies, and 5 of them were pretty much terrible. The Adjustment Bureau was &lt;em&gt;awesome&lt;/em&gt;! There were a few redeeming qualities in Just Go For It, but not enough to make it enjoyable. The other 4 just completely sucked. Perhaps the reason I haven't been watching many movies is because most of Hollywood stopped even trying to make decent movies anymore and instead relies on marketing and pop culture to make money...&lt;br /&gt;
&lt;br /&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/fD0jR1axY8w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/824801111147117916/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2011/08/what-happened-to-film-industry.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/824801111147117916?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/824801111147117916?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/fD0jR1axY8w/what-happened-to-film-industry.html" title="What Happened To The Film Industry?" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2011/08/what-happened-to-film-industry.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUAHSXc-eSp7ImA9WhZUFkg.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-1543539596237306043</id><published>2011-06-09T16:58:00.001-04:00</published><updated>2011-06-09T17:08:58.951-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-09T17:08:58.951-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="unglobal" /><category scheme="http://www.blogger.com/atom/ns#" term="clean code" /><category scheme="http://www.blogger.com/atom/ns#" term="entry point" /><category scheme="http://www.blogger.com/atom/ns#" term="global" /><category scheme="http://www.blogger.com/atom/ns#" term="Singleton" /><title>UNglobal State Programming Environment</title><content type="html">A year or two ago I stumbled across a very useful Google Tech Talk on YouTube about global state and Singletons and why they're bad:&lt;br /&gt;
&lt;br /&gt;
&lt;object width="425" height="344"&gt; &lt;param name="movie" value="http://www.youtube.com/watch?v=-FRm3VPhseI?fs=1" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;embed src="http://www.youtube.com/v/-FRm3VPhseI?fs=1"
  type="application/x-shockwave-flash"
  allowfullscreen="true"
  allowscriptaccess="always"
  width="425" height="344" /&gt; &lt;/object&gt;&lt;br /&gt;
&lt;br /&gt;
It makes a lot of sense and after letting it sink in and experimenting with dependency injection myself I can say that it results in much better designs, whether or not you're writing tests (the video is Java-centric and test-centric, but neither is necessary for it to apply). Thinking about it though popular programming environments do impose a lot of global state that is in my experience regularly accessed without a second thought. When you collaborate with other people it's hard to make them understand how global state and Singletons are bad when to them it's the easy and fast way to get their job done. It's my opinion that a large part of the problem is that programmers are just &lt;em&gt;used&lt;/em&gt; to global state from their programming environments. What we need is for languages to be updated and newer languages to be done right in the first place. No more global state in the programming environment. Pass that state into the program's entry point. For example:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;// C
int main(const system_t * const system, const int argc, const char * argv[])
{
    fprintf(system-&gt;stderr, "This was a triumph.\n");

    return 0;
}

// C++
int main(const System &amp; system, const int argc, const char * const argv[])
{
    system.cerr &lt;&lt; "This was a triumph." &lt;&lt; std::endl;

    return 0;
}

// C#
private static int Main(System system, string[] args)
{
    system.Error.WriteLine("This was a triumph.");

    return 0;
}

/* Java (albeit, while they're at it, main should return an int in Java too) */
public static void main(System system, String [] args)
{
    system.err.println("This was a triumph.");

    system.exit(0);
}
&lt;/pre&gt;
&lt;br /&gt;
Etc. Wouldn't that be a wonderful platform to develop on? I certainly think so.&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/Y675fsYzVQs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/1543539596237306043/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2011/06/unglobal-state-programming-environment.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/1543539596237306043?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/1543539596237306043?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/Y675fsYzVQs/unglobal-state-programming-environment.html" title="UNglobal State Programming Environment" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2011/06/unglobal-state-programming-environment.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMAR3s6fip7ImA9WhZVEUU.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-5033810578344170728</id><published>2011-05-23T16:46:00.003-04:00</published><updated>2011-05-23T17:00:46.516-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-23T17:00:46.516-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="userland" /><category scheme="http://www.blogger.com/atom/ns#" term="banking" /><category scheme="http://www.blogger.com/atom/ns#" term="monopoly" /><category scheme="http://www.blogger.com/atom/ns#" term="kernel" /><category scheme="http://www.blogger.com/atom/ns#" term="politics" /><category scheme="http://www.blogger.com/atom/ns#" term="kernel space" /><title>Kernel-space Politics</title><content type="html">I thought of an interesting analogy the other day during an IRC conversation that I thought I'd share with you. :) It was probably almost a week ago (Blogger was down for maintenance at the time) and I forget exactly what the discussion was about, but more or less just how the members of government exploit the people for their own personal gain. In defense of privatizing as much as possible, I argued that the government is a little bit like the kernel of an operating system. If you want the system to be secure and reliable then you need to move as much as possible into userland (i.e., &lt;u&gt;outside&lt;/u&gt; of the kernel). This way the individual processes (services, businesses, etc.) can attempt to do wrong, but ultimately the authority (kernel AKA government) has the power to stop them. On the other hand, if these processes are occurring directly within the kernel then they have unrestricted access to the entire system and can do whatever they want, even if they happen to corrupt or crash the system in doing so.&lt;br /&gt;
&lt;br /&gt;
Then again, with the world's major currencies under private banking monopoly control you have to ask yourself whether the government is actually the kernel or only the system drivers. &lt;tt&gt;:-X&lt;/tt&gt; The government can only hope to keep the system secure and reliable if it is actually ring 0 and free of corruption itself.&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/6TAqjSRqYu8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/5033810578344170728/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2011/05/kernel-space-politics.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/5033810578344170728?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/5033810578344170728?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/6TAqjSRqYu8/kernel-space-politics.html" title="Kernel-space Politics" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2011/05/kernel-space-politics.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8AR3gzfip7ImA9WhZUFkg.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-3874745277432822142</id><published>2011-05-17T09:44:00.004-04:00</published><updated>2011-06-09T17:10:46.686-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-09T17:10:46.686-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="webkit" /><category scheme="http://www.blogger.com/atom/ns#" term="garbage collector" /><category scheme="http://www.blogger.com/atom/ns#" term="callback" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="pdf" /><category scheme="http://www.blogger.com/atom/ns#" term="access violation" /><category scheme="http://www.blogger.com/atom/ns#" term="zbar" /><category scheme="http://www.blogger.com/atom/ns#" term="p/invoke" /><category scheme="http://www.blogger.com/atom/ns#" term="html" /><category scheme="http://www.blogger.com/atom/ns#" term="delegate" /><title>.NET P/Invoke With Managed Callbacks</title><content type="html">&lt;b&gt;Update:&lt;/b&gt; See &lt;a href="#net_pinvoke_with_managed_callbacks_app1"&gt;append at the bottom&lt;/a&gt; for an update.&lt;br /&gt;
&lt;b&gt;Update2:&lt;/b&gt; Open sourced the bindings. See &lt;a href="#net_pinvoke_with_managed_callbacks_app2"&gt;append2 at the bottom&lt;/a&gt; for an update.&lt;br /&gt;
&lt;br /&gt;
I've finally thought of another topic to write about. Or, rather, the topic fell on my desk. &lt;i&gt;Again.&lt;/i&gt; It is about using managed methods as callbacks in native code. I first learned about this months (a year?) ago when I was working on a ZBar.Processor proxy class for &lt;a href="https://github.com/jopsen/zbar-sharp"&gt;zbar-sharp&lt;/a&gt; (.NET bindings for the &lt;a href="http://zbar.sourceforge.net/"&gt;zbar&lt;/a&gt; bar code scanning library). This was my first real experience using P/Invoke.&lt;br /&gt;
&lt;br /&gt;
The &lt;code&gt;zbar_processor&lt;/code&gt; is effectively a high-level object to scan frames from a Web cam for bar codes and return the encoded data back to the calling application. The data is returned via a callback function. This seemed simple enough once I figured out how P/Invoke basically is done. I just wrote up a compatible method and passed it into the zbar API. While it did seem to work, the native library would eventually throw memory access violations.&lt;br /&gt;
&lt;br /&gt;
It turns out that the problem was me (SHOCK). Or rather, my code. Or rather, missing code. See in .NET the equivalent of a function pointer is basically a delegate instance. This is effectively a method object (I imagine it to be similar to a functor in C++). In any case, calling the delegate instance is equivalent to calling the method. Even the &lt;code&gt;this&lt;/code&gt; reference is preserved.&lt;br /&gt;
&lt;h4&gt;#digress Delegates In C#&lt;/h4&gt;A delegate type is declared like so:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;namespace Application
{
    class Example
    {
        public delegate void foo(int bar);
    }
}&lt;/code&gt;&lt;/pre&gt;What this does is basically declare a delegate type named "foo" with no return value and a single &lt;code&gt;int&lt;/code&gt; parameter. You can then create an instance of the delegate like so:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;foo f = new foo(MyMethod);&lt;/code&gt;&lt;/pre&gt;Where MyMethod is a static or non-static method that matches the signature. The instantiation can also be implicit:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;foo f = MyMethod;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
For a more complete example:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;using System;

namespace Application
{
    class Program
    {
        static void Main()
        {
            new Program().Run();
        }

        public void Run()
        {
            // Using a method, explicit instantiation.
            Example.foo f1 = new Example.foo(this.Foo);

            // Using a method, implicit instantiation.
            Example.foo f2 = this.Foo;

            // Using a lambda, explicit instantiation.
            Example.foo f3 = new Example.foo(
                    (int bar) =&gt; Console.WriteLine(bar));

            // Using a lambda, implicit instantiation.
            Example.foo f4 = (int bar) =&gt; Console.WriteLine(bar);

            f1(1);
            f2(2);
            f3(3);
            f4(4);
        }

        void Foo(int bar)
        {
            Console.WriteLine(bar);
        }
    }

    class Example
    {
        public delegate void foo(int bar);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;#enddigress&lt;/h4&gt;As I was saying before I so rudely interrupted me, I was missing a tiny little detail. In order to use one of these delegates as a callback in native code you'd first have to instantiate one. OK, first we actually need a native method to call, and a callback type.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;namespace Application
{
    class Example
    {
        public class NativeMethods
        {
            public delegate void zbar_image_data_handler_t(
                    IntPtr image,
                    IntPtr userdata);

            [DllImport("libzbar0")]
            public static extern IntPtr zbar_processor_set_data_handler(
                    IntPtr processor,
                    zbar_image_data_handler_t handler,
                    IntPtr userdata);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;Now in order to use it we simply do as above:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;using System;

namespace Application
{
    class Program
    {
        static void Main()
        {
            new Program().Run();
        }

        public void Run()
        {
            IntPtr processor = IntPtr.Zero;

            /*
             * Use your imagination here. We would have had to call native
             * methods to create and initialize a zbar_processor struct.
             */
            
            var handler =
                    new Example.NativeMethods.zbar_image_data_handler_t(
                    this.ZBarProcessorImageDataHandler);

            Example.NativeMethods.zbar_processor_set_data_handler(
                    processor,
                    handler,
                    null);
        }

        void ZBarProcessorImageDataHandler(IntPtr image, IntPtr userdata)
        {
            Console.WriteLine("Bar code successfully scanned!");
        }
    }
}&lt;/code&gt;&lt;/pre&gt;Pretty straightforward, really, but there is a problem! Can you see it?! Not likely because it isn't even in the code. Remember that .NET is managed and garbage collected and delegates are basically objects. What this means is that when the garbage collector finds a delegate that is no longer referenced by anything it will eventually garbage collect it. This was something that certainly hadn't occurred to me when I was writing this code. It wasn't until I sought help online that somebody (it was many months ago, but I'm guessing in #csharp on freenode) pointed out that the delegate object could be destroyed by the time the zbar library attempted to call it. So what is the solution? We need to keep a reference to it around so it isn't destroyed. We can do that by simply creating a field to hold it.&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;...
    class Program
    {
        Example.NativeMethods.zbar_image_data_handler_t
                imageDataHandler_;

        ...

        public void Run()
        {
            IntPtr processor = IntPtr.Zero;

            /*
             * Use your imagination here. We would have had to call native
             * methods to create and initialize a zbar_processor struct.
             */
            
            this.imageDataHandler_ =
                    new Example.NativeMethods.zbar_image_data_handler_t(
                    this.ZBarProcessorImageDataHandler);

            Example.NativeMethods.zbar_processor_set_data_handler(
                    processor,
                    this.imageDataHandler_,
                    null);
        }

        ...
    }
...&lt;/code&gt;&lt;/pre&gt;At this point, after fixing a few other subtle bugs, the program ran fine without access violations! Unfortunately, the zbar_processor (at least, at the time) wasn't very reliable and we ultimately ended up implementing the Web cam interface ourselves and passing the scanned frames to zbar.&lt;br /&gt;
&lt;br /&gt;
In any case, I've recently been writing .NET bindings for wkhtmltox, another native library. This one is for converting HTML/CSS Web pages to PDF documents. It works &lt;i&gt;really&lt;/i&gt; well because it's based on the WebKit framework (hence, the "wk" in the name). So I forgot all about the lifetime of the callback delegates and ran into the same problem. Today I finally remembered and looked back at the zbar bindings to see what was wrong.&lt;br /&gt;
&lt;br /&gt;
I've now fixed that by adding delegate fields to the appropriate proxy class, but unfortunately I'm &lt;u&gt;STILL&lt;/u&gt; getting access violation exceptions. It's probably going to be some other subtle bug as a result of .NET speaking to native code. I haven't figured it out yet though.&lt;br /&gt;
&lt;br /&gt;
If you have any ideas about what else would cause memory access violations in P/Invoke programs or have any questions about what I've shown then please leave a comment. :)&lt;br /&gt;
&lt;h4&gt;&lt;a id="net_pinvoke_with_managed_callbacks_app1"&gt;Append:&lt;/a&gt;&lt;/h4&gt;Seems there is more to the story than I thought. See the following thread for details:&lt;br /&gt;
&lt;br /&gt;
http://www.gamedev.net/topic/270670-pinvoke-callback-access-violation/&lt;br /&gt;
&lt;br /&gt;
The gist of it is that there is a &lt;code&gt;System.Diagnostics.InteropServices.UnmanagedFunctionPointerAttribute&lt;/code&gt; attribute that can be used to mark up your callback delegates with P/Invoke attributes (for example, calling convention). I'm not sure why, but specifying &lt;code&gt;CallingConvention.CDecl&lt;/code&gt; for my callback delegates seems to postpone or avoid the access violations. I don't really understand why because the functions of the library are being called with the default calling convention, which I think is &lt;code&gt;CallingConvention.StdCall&lt;/code&gt;. Attempting to specify &lt;code&gt;CallingConvention.CDecl&lt;/code&gt; for the native functions immediately causes the stack to become imbalanced/corrupted (according to the .NET runtime; I think I trust it in this case ;). It doesn't really make sense to me that the callbacks would use a different calling convention so I suspect that the different calling convention for the callbacks just coincidentally changes the way the memory is accessed to avoid violations... I don't know. I'm lost. :(&lt;br /&gt;
&lt;h4&gt;&lt;a id="net_pinvoke_with_managed_callbacks_app2"&gt;Append2:&lt;/a&gt;&lt;/h4&gt;I've turned the .NET bindings for wkhtmltox (wkhtmltopdf only) into an open source class library. :) You can find the code at &lt;a href="http://github.com/bamccaig/WkHtmlToPdfDflat"&gt;its GitHub repository&lt;/a&gt;. It doesn't work properly yet (in fact, I've given up on it for now and am working on invoking the wkhtmltopdf.exe process instead), but I see no technical reason why it can't so I (or you) just need to figure out what's currently wrong. :)&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/0AKfhDP5OKA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/3874745277432822142/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2011/05/net-pinvoke-with-managed-callbacks.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/3874745277432822142?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/3874745277432822142?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/0AKfhDP5OKA/net-pinvoke-with-managed-callbacks.html" title=".NET P/Invoke With Managed Callbacks" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>3</thr:total><feedburner:origLink>http://www.bamccaig.com/2011/05/net-pinvoke-with-managed-callbacks.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEEFRH09cSp7ImA9WhZRGUw.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-3435007921091775425</id><published>2011-04-15T14:33:00.002-04:00</published><updated>2011-04-15T20:10:15.369-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-15T20:10:15.369-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="open source" /><category scheme="http://www.blogger.com/atom/ns#" term="package" /><category scheme="http://www.blogger.com/atom/ns#" term="allegro" /><category scheme="http://www.blogger.com/atom/ns#" term="rpm" /><category scheme="http://www.blogger.com/atom/ns#" term="linux" /><category scheme="http://www.blogger.com/atom/ns#" term="fedora" /><category scheme="http://www.blogger.com/atom/ns#" term="yum" /><title>Fedora Package Contributor for Allegro</title><content type="html">It's been a long time since I've posted anything on here and I guess it's long overdue. I've either lacked a good topic to type about or was just too drained to do so. Well no longer!&lt;br /&gt;
&lt;br /&gt;
I figured I would take a moment to announce that I've become a package contributor for the &lt;a href="http://fedoraproject.org/"&gt;Fedora&lt;/a&gt; project. What does that mean? It just means that I'm currently contributing to the development or maintenance of one or more of the RPM packages for the project. In particular, I'm maintaining the Allegro 5 packages. What this means is that I took the Allegro 5 source code and turned it into RPM packages. The main repository that I'm using to track Allegro 5 package development is available on GitHub: &lt;a href="http://github.com/bamccaig/allegro5-rpm"&gt;http://github.com/bamccaig/allegro5-rpm&lt;/a&gt;. Don't confuse me though with the actual Allegro developers who deserve the real praise for the Allegro library!&lt;br /&gt;
&lt;br /&gt;
There are plans for me to help out with or take over the Allegro 4 packages as well (which I think will include games and other related projects). I've just recently gotten Allegro 5 adopted into Fedora 13 and 14 &lt;del&gt;(15 AKA Alpha is pending)&lt;/del&gt; &lt;ins&gt;and 15&lt;/ins&gt;. AFAIK, Fedora is the first distribution to get Allegro 5 packages! So yay for that! I'll now be working on updating the Allegro 4 packages (allegro) to the latest stable version, 4.4.x.y.&lt;br /&gt;
&lt;br /&gt;
I guess that makes me an "official" contributor to Fedora. So that's pretty exciting. I think so, at least. What are you waiting for? Go put my packages in action! You can learn how to &lt;a href="http://www.allegro.cc/forums/thread/607029"&gt;here&lt;/a&gt; if you don't already know.&lt;br /&gt;
&lt;br /&gt;
Since there isn't much else to say about that, I'll take a few moments to discuss a few of the things I learned about working with RPMs. RPMs are based on a .spec file that basically just describes the package in a text file: name, description, version, release, where to get the source code, the software license, build instructions, which files to install, what their permissions should be, etc. To turn a .spec file into an SRPM[&lt;a id="fedora-package-contributor-for-allegro-ref1-up" href="#fedora-package-contributor-for-allegro-ref1-down"&gt;1&lt;/a&gt;] or RPM you use the &lt;tt&gt;rpmbuild&lt;/tt&gt; tool. Once installed you can easily query the package system for information about a package using &lt;tt&gt;rpm&lt;/tt&gt; or &lt;tt&gt;yum&lt;/tt&gt;. For example:&lt;br /&gt;
&lt;pre&gt;$ # Lets query the basic information about the core allegro5 package.
$ yum info allegro5
&lt;em&gt;*snip*&lt;/em&gt;
Name        : allegro5
Arch        : i686
Version     : 5.0.0
Release     : 3.fc13
Size        : 994 k
Repo        : installed
From repo   : updates
Summary     : A game programming library
URL         : http://liballeg.org/
License     : zlib
Description : Allegro is a cross-platform library intended for use in computer games
            : and other types of multimedia programming. Allegro 5 is the latest major
            : revision of the library, designed to take advantage of modern hardware
            : (e.g. hardware acceleration using 3D cards) and operating systems.
            : Although it is not backwards compatible with earlier versions, it still
            : occupies the same niche and retains a familiar style.

$ # Lets query the changelog of the core allegro5 package.
$ rpm -q --changelog allegro5
* Wed Mar 09 2011 Brandon McCaig &lt;bamccaig@gmail.com&gt; 5.0.0-3
- Adding file permissions to subpackages.
- Moving devel files (namely .so symlinks) to devel packages.
- Added %doc section proper; readmes, changes, license, etc.
- Fixed SF.net URI.
- Modified BuildRequires.
- Added main devel dependency to subpackage devels.
- Replaced many al_*.3* manpage files with a glob.
- Replaced many header files with directory and %exclude macros.
- Added allegro5.cfg file under /etc/allegro5rc.

* Fri Mar 04 2011 Brandon McCaig &lt;bamccaig@gmail.com&gt; 5.0.0-2
- Merged primitives addon packages into core packages.
- Merged memfile addon packages into core packages.
- Merged "main" addon packages into core packages.
- Merged font packages into core packages.
- Merged color packages into core packages.
- Merged doc package into the devel package.
- Fixed spelling mistakes.
- Removed explicit library dependencies.

* Fri Feb 25 2011 Brandon McCaig &lt;bamccaig@gmail.com&gt; 5.0.0-1
- Initial version.

$ 
&lt;/pre&gt;I was actually quite happy to learn just how easy it is to create RPMs that work. RPM seems to be very smart and figures out a lot of the dependencies itself. Contributing simple packages really isn't very difficult if you already know a thing or two about how to build them from source (programming experience helps too). So if you're a programmer that uses Fedora (or any other Linux distribution) and wants to help out by becoming a packager then I suggest you look into it. It's not as difficult as you might think.&lt;br /&gt;
&lt;br /&gt;
Thanks to my sponsor[&lt;a id="fedora-package-contributor-for-allegro-ref2-up" href="#fedora-package-contributor-for-allegro-ref2-down"&gt;2&lt;/a&gt;] for guiding me through the process and helping me out when I got stuck.&lt;br /&gt;
&lt;h4&gt;References&lt;/h4&gt;[&lt;a id="fedora-package-contributor-for-allegro-ref1-down" href="#fedora-package-contributor-for-allegro-ref1-up"&gt;1&lt;/a&gt;] An SRPM is a source RPM. It basically is just an RPM that contains the .spec file, source tarball, and any patches that might be bundled. The SRPM is an intermediary stage between the source code and .spec and the fully-fledged RPM.&lt;br /&gt;
[&lt;a id="fedora-package-contributor-for-allegro-ref2-down" href="#fedora-package-contributor-for-allegro-ref2-up"&gt;2&lt;/a&gt;] I'll refrain from naming him without his permission to do so, just in case, but you can probably find out who it is easily enough by checking out the fedora bugzilla system if you care. :) He knows who he is. :)&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/9mpQT2EerzA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/3435007921091775425/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2011/04/fedora-package-contributor-for-allegro.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/3435007921091775425?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/3435007921091775425?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/9mpQT2EerzA/fedora-package-contributor-for-allegro.html" title="Fedora Package Contributor for Allegro" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2011/04/fedora-package-contributor-for-allegro.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMBSXs4eCp7ImA9WxFaGE0.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-5184791547759553273</id><published>2010-07-06T16:31:00.024-04:00</published><updated>2010-07-22T10:04:18.530-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-22T10:04:18.530-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="sql" /><category scheme="http://www.blogger.com/atom/ns#" term="configure" /><category scheme="http://www.blogger.com/atom/ns#" term="freetds" /><category scheme="http://www.blogger.com/atom/ns#" term="sql server" /><category scheme="http://www.blogger.com/atom/ns#" term="makefile" /><category scheme="http://www.blogger.com/atom/ns#" term="fedora" /><category scheme="http://www.blogger.com/atom/ns#" term="command line" /><category scheme="http://www.blogger.com/atom/ns#" term="sqsh" /><category scheme="http://www.blogger.com/atom/ns#" term="ct-library" /><category scheme="http://www.blogger.com/atom/ns#" term="shell" /><category scheme="http://www.blogger.com/atom/ns#" term="install" /><category scheme="http://www.blogger.com/atom/ns#" term="text" /><category scheme="http://www.blogger.com/atom/ns#" term="linux" /><category scheme="http://www.blogger.com/atom/ns#" term="sybase" /><category scheme="http://www.blogger.com/atom/ns#" term="sqshell" /><title>SQshelL - Building and Installing sqsh In Linux (Fedora 13) Without The Sybase CT-Library</title><content type="html">If you're like me then you generally prefer fast, efficient command-line interfaces to expensive (CPU- and RAM-wise) and slow GUI interfaces. Recently, my Windows 7 (Seven) box got really slow, and in particular Visual Studio and SQL Server began to crawl. I couldn't really explain it because the Windows Task Manager said that the System Idle Process was using all of the CPU and the system was only using about half of my 3 GB of RAM, but nevertheless, scrolling a single line of code took &lt;em&gt;seconds&lt;/em&gt;! Something had to be done.&lt;br /&gt;
&lt;br /&gt;
I had finally had enough and insisted to j0rb that I was going to install Linux. Now, I currently still require Windows because most of our projects are .NET-based (and bleeding edge, .NET 4.0 stuff), and it seems Mono just isn't quite there yet. I was very pleased to hear that they do support many (or most?) .NET 4.0 features, but apparently not LINQ to SQL, which we make extensive use of. To solve this contradiction, I would install Windows in a virtual machine. I ended up installing Fedora 13 (x86) as the host, with Windows 7 (Seven) Ultimate in a VirtualBox guest. I was quite nervous about whether or not it would actually perform adequately (especially considering how poorly Visual Studio and SQL Server were performing on the raw metal), but so far so good.&lt;br /&gt;
&lt;br /&gt;
On to the topic at hand: building and installing sqsh in Fedora 13. sqsh is basically a shell interface for Sybase ASE (I guess?) or SQL Server. It allows you to do powerful things that you expect from your system shell, like redirecting streams, and environment variables, etc.; as well as interact with a database in SQL. I'm not overly familiar yet with what it can or can't do (I just heard about it and went ahead and installed it), but my hopes are high. Hopefully it will at least reduce the need for me to wait for SQL Server to startup and close, or &lt;em&gt;ugh&lt;/em&gt; connect.&lt;br /&gt;
&lt;br /&gt;
Naturally, when I first heard of it I instinctively asked YUM for it. Sadly, I was disappointed to learn that it is not in the default repositories. "No matter! I will just install from source," I told myself.&lt;br /&gt;
&lt;br /&gt;
The first step to installing from source is obviously to get the source. So head on over to SourceForge, where it's currently being hosted, and download the latest version (I'm using 2.1.7 at the time of writing).&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://sourceforge.net/projects/sqsh/"&gt;http://sourceforge.net/projects/sqsh/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Assuming your results are similar to mine, you should now have a &lt;tt&gt;sqsh-&lt;em&gt;&amp;lt;version&amp;gt;&lt;/em&gt;.tar.gz&lt;/tt&gt; file somewhere on your file system. Firefox downloaded it straight to my Downloads folder (&lt;tt&gt;dl&lt;/tt&gt; for short). I keep all source trees in &lt;tt&gt;~/src&lt;/tt&gt; for organization, so lets go ahead and extract it to there now (you can extract it where ever you like).&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton ~]$ tar -xzf dl/sqsh-2.1.7.tar.gz -C src
[bamccaig@krypton ~]$ cd src/sqsh-2.1.7
[bamccaig@krypton sqsh-2.1.7]$ &lt;/pre&gt;Great. So far so good. Now lets take a look for "readme" files that we should read before proceeding (which are typically named in all uppercase):&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ ls -1 | egrep '^[[:upper:]]+$'
AUTHORS
COPYING
INSTALL
README
[bamccaig@krypton sqsh-2.1.7]$ &lt;/pre&gt;Excellent! There is a &lt;tt&gt;README&lt;/tt&gt; and most importantly an &lt;tt&gt;INSTALL&lt;/tt&gt;! Go ahead and read through both of those files now. I'll wait.&lt;br /&gt;
&lt;br /&gt;
... ... ...&lt;br /&gt;
&lt;br /&gt;
OK, now that we've read those two files, we should all be aware of the dependencies. Basically you need Sybase CT-Library &lt;em&gt;OR&lt;/em&gt; you need FreeTDS. This threw me for a loop at first because when I reached the Sybase CT-Library dependency it was worded in such a way that made it sound absolutely required. If you go on to read the next dependency, you'll see that you can alternatively use FreeTDS. It sounds like there is actually a couple of freely available Sybase downloads for Linux (free as in beer, not as in speech), but I'm not entirely sure which, if any, contain the Sybase CT-Library and worse you're required to register with Sybase to download them (&lt;em&gt;ugh&lt;/em&gt;). I was pleased to discover that doing so is unnecessary.&lt;br /&gt;
&lt;br /&gt;
Instead, I say again, you can use FreeTDS; and so that's what we're going to do. Lets see if we can find it.&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ yum search freetds | grep '^freetds'
freetds-devel.i686 : Header files and development libraries for freetds
freetds-doc.i686 : Development documentation for freetds
freetds.i686 : Implementation of the TDS (Tabular DataStream) protocol
[bamccaig@krypton sqsh-2.1.7]$ 
&lt;/pre&gt;That &lt;em&gt;is&lt;/em&gt; in the default repositories so lets go ahead and install it (including the development files; and WTH, the documentation too).&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ su -c 'yum install freetds freetds-devel freetds-doc'
Password:
[Hopefully YUM spits out happy here]
[bamccaig@krypton sqsh-2.1.7]$ 
&lt;/pre&gt;With that out of the way, we're almost ready to build. Taking another look at the &lt;tt&gt;INSTALL&lt;/tt&gt; file, we need to specify the base SYBASE directory in the &lt;tt&gt;SYBASE&lt;/tt&gt; environment variable. I know what you're thinking. I know I was. I don't &lt;i&gt;have&lt;/i&gt; Sybase! This doesn't apply to me! Not so fast. It turns out that it does apply to you (and I).&lt;a href="#sqshell-building-and-installing-sqsh-in-ref1"&gt;[1]&lt;/a&gt; We instead need to point the &lt;tt&gt;SYBASE&lt;/tt&gt; environment variable at the base directory for FreeTDS.&lt;br /&gt;
&lt;br /&gt;
What this means is that &lt;tt&gt;$SYBASE/include&lt;/tt&gt; and &lt;tt&gt;$SYBASE/lib&lt;/tt&gt; should tell our build process where to find the necessary header and library files. So where are those files installed for FreeTDS? You can make an educated guess or you can ask &lt;tt&gt;rpm&lt;/tt&gt; itself. I actually didn't know about this until now. It's hopefully going to make things a lot easier in the future.&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ rpm -ql freetds freetds-devel | \
        egrep '/(include|lib)' | sort
/usr/include/bkpublic.h
/usr/include/cspublic.h
/usr/include/cstypes.h
/usr/include/ctpublic.h
/usr/include/sqldb.h
/usr/include/sqlfront.h
/usr/include/sybdb.h
/usr/include/syberror.h
/usr/include/sybfront.h
/usr/include/tdsconvert.h
/usr/include/tds.h
/usr/include/tds_sysdep_public_32.h
/usr/include/tds_sysdep_public.h
/usr/include/tdsver.h
/usr/lib/libct.so
/usr/lib/libct.so.4
/usr/lib/libct.so.4.0.0
/usr/lib/libsybdb.so
/usr/lib/libsybdb.so.5
/usr/lib/libsybdb.so.5.0.0
/usr/lib/libtds-0.82.so
/usr/lib/libtdsodbc.so
/usr/lib/libtdsodbc.so.0
/usr/lib/libtdsodbc.so.0.0.0
/usr/lib/libtds.so
[bamccaig@krypton sqsh-2.1.7]$ 
&lt;/pre&gt;You can check the &lt;tt&gt;man&lt;/tt&gt; or &lt;tt&gt;info&lt;/tt&gt; page(s) for details, but essentially what we did is query the package manager to list the files that were installed; then we filtered out the results we weren't interested in (since we're only interested in files within an &lt;code&gt;include&lt;/code&gt; or &lt;tt&gt;lib&lt;/tt&gt; directory, I greped for that within the path). For good measure, I also sorted the results.&lt;br /&gt;
&lt;br /&gt;
As you can see, FreeTDS is installed in &lt;tt&gt;/usr&lt;/tt&gt;. No real surprise there. OK, so we need to set &lt;tt&gt;SYBASE&lt;/tt&gt; to &lt;tt&gt;/usr&lt;/tt&gt;.&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ export SYBASE=/usr
[bamccaig@krypton sqsh-2.1.7]$ echo $SYBASE
/usr
[bamccaig@krypton sqsh-2.1.7]$ &lt;/pre&gt;And now we're finally ready to start the build process. sqsh uses the "GNU auto-configuration package" as so many open source projects seem to do, so it uses the rather standard &lt;tt&gt;./configure&lt;/tt&gt;, &lt;tt&gt;make&lt;/tt&gt;, &lt;tt&gt;make install&lt;/tt&gt; build instructions. If you want readline support (and most people probably do) then you'll probably want to add the &lt;tt&gt;--with-readline&lt;/tt&gt; option while configuring (you'll obviously need to have readline installed to do this though). Review the &lt;tt&gt;INSTALL&lt;/tt&gt; file for more options.&lt;br /&gt;
&lt;pre style="height: 500px; overflow: scroll;"&gt;[bamccaig@krypton sqsh-2.1.7]$ ./configure --with-readline
[Lots of output... Should be happy if your environment is complete...]
[bamccaig@krypton sqsh-2.1.7]$ make
[Everything is going good, the project compiles, but then all of a sudden...the linker is angry!]
gcc    -L/usr/lib  cmd_alias.o cmd_bcp.o cmd_buf.o cmd_connect.o cmd_do.o cmd_echo.o cmd_exit.o cmd_for.o cmd_func.o cmd_go.o cmd_help.o cmd_history.o cmd_if.o cmd_input.o cmd_jobs.o cmd_kill.o cmd_lock.o cmd_loop.o cmd_misc.o cmd_read.o cmd_reconnect.o cmd_redraw.o cmd_reset.o cmd_return.o cmd_rpc.o cmd_set.o cmd_shell.o cmd_show.o cmd_sleep.o cmd_wait.o cmd_warranty.o cmd_while.o var_ctlib.o var_date.o var_debug.o var_dsp.o var_hist.o var_misc.o var_passwd.o var_readline.o var_thresh.o dsp.o dsp_bcp.o dsp_csv.o dsp_conv.o dsp_desc.o dsp_horiz.o dsp_html.o dsp_meta.o dsp_none.o dsp_out.o dsp_pretty.o dsp_vert.o dsp_x.o sqsh_alias.o sqsh_args.o sqsh_avl.o sqsh_buf.o sqsh_cmd.o sqsh_compat.o sqsh_ctx.o sqsh_debug.o sqsh_env.o sqsh_error.o sqsh_expand.o sqsh_fd.o sqsh_filter.o sqsh_fork.o sqsh_func.o sqsh_getopt.o sqsh_global.o sqsh_history.o sqsh_init.o sqsh_job.o sqsh_readline.o sqsh_sig.o sqsh_sigcld.o sqsh_stdin.o sqsh_strchr.o sqsh_tok.o sqsh_varbuf.o sqsh_main.o -ldl -lm   -lreadline -lcurses  -o sqsh
cmd_bcp.o: In function `bcp_signal':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:1164: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:1166: undefined reference to `ct_cancel'
cmd_bcp.o: In function `cmd_bcp':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:337: undefined reference to `ct_cmd_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:877: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:894: undefined reference to `blk_done'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:898: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:901: undefined reference to `ct_close'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:904: undefined reference to `ct_con_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:910: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:924: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:942: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:947: undefined reference to `blk_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:952: undefined reference to `ct_close'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:953: undefined reference to `ct_con_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:957: undefined reference to `cs_loc_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:395: undefined reference to `ct_command'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:406: undefined reference to `ct_send'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:865: undefined reference to `blk_done'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:866: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:867: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:422: undefined reference to `ct_con_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:429: undefined reference to `ct_callback'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:438: undefined reference to `ct_callback'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:447: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:460: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:475: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:488: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:521: undefined reference to `ct_con_props'
cmd_bcp.o:/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:503: more undefined references to `ct_con_props' follow
cmd_bcp.o: In function `cmd_bcp':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:558: undefined reference to `cs_loc_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:539: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:566: undefined reference to `cs_locale'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:600: undefined reference to `cs_locale'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:582: undefined reference to `cs_locale'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:617: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:630: undefined reference to `ct_connect'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:672: undefined reference to `blk_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:703: undefined reference to `blk_init'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:685: undefined reference to `blk_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:722: undefined reference to `ct_results'
cmd_bcp.o: In function `bcp_data_bind':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:973: undefined reference to `ct_res_info'
cmd_bcp.o: In function `cmd_bcp':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:818: undefined reference to `blk_done'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:837: undefined reference to `blk_done'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:797: undefined reference to `ct_fetch'
cmd_bcp.o: In function `bcp_data_xfer':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:1060: undefined reference to `ct_fetch'
cmd_bcp.o: In function `cmd_bcp':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:769: undefined reference to `blk_done'
cmd_bcp.o: In function `bcp_data_bind':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:1013: undefined reference to `ct_describe'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:1031: undefined reference to `ct_bind'
cmd_bcp.o: In function `bcp_data_xfer':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:1096: undefined reference to `blk_bind'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_bcp.c:1112: undefined reference to `blk_rowxfer'
cmd_connect.o: In function `cmd_connect':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:719: undefined reference to `ct_con_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1172: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1187: undefined reference to `ct_close'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1196: undefined reference to `ct_con_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1202: undefined reference to `cs_loc_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1208: undefined reference to `ct_exit'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1209: undefined reference to `cs_ctx_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:769: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:814: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:865: undefined reference to `cs_loc_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:869: undefined reference to `cs_locale'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:908: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:925: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:949: undefined reference to `ct_connect'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:978: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:567: undefined reference to `cs_ctx_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:597: undefined reference to `cs_config'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:725: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:573: undefined reference to `cs_ctx_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:579: undefined reference to `cs_ctx_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:585: undefined reference to `cs_ctx_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:591: undefined reference to `cs_ctx_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:782: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:610: undefined reference to `ct_init'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:613: undefined reference to `ct_callback'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:621: undefined reference to `ct_callback'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:635: undefined reference to `ct_config'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:702: undefined reference to `ct_config'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:651: undefined reference to `ct_config'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:668: undefined reference to `ct_config'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:822: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:836: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:850: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:882: undefined reference to `cs_locale'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1129: undefined reference to `cs_loc_drop'
cmd_connect.o: In function `check_opt_capability':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1251: undefined reference to `ct_capability'
cmd_connect.o: In function `cmd_connect':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1137: undefined reference to `ct_options'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:896: undefined reference to `cs_locale'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1037: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1089: undefined reference to `ct_cmd_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1094: undefined reference to `ct_command'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1115: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1105: undefined reference to `ct_send'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1111: undefined reference to `ct_results'
cmd_connect.o: In function `syb_client_cb':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1571: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_connect.c:1580: undefined reference to `ct_cancel'
cmd_do.o: In function `cmd_do_sigint_cancel':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:677: undefined reference to `ct_cancel'
cmd_do.o: In function `cmd_do_exec':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:304: undefined reference to `ct_cmd_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:312: undefined reference to `ct_command'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:327: undefined reference to `ct_cmd_drop'
cmd_do.o: In function `cmd_do':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:260: undefined reference to `ct_close'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:261: undefined reference to `ct_con_drop'
cmd_do.o: In function `cmd_do_exec':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:322: undefined reference to `ct_send'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:334: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:434: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:435: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:351: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:446: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:447: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:423: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:424: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:464: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:465: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:381: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:382: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:342: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:343: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_do.c:469: undefined reference to `ct_cmd_drop'
cmd_go.o: In function `cmd_go':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_go.c:468: undefined reference to `ct_cmd_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_go.c:475: undefined reference to `ct_command'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_go.c:511: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_go.c:481: undefined reference to `ct_cmd_drop'
cmd_reconnect.o: In function `cmd_reconnect':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_reconnect.c:58: undefined reference to `ct_close'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_reconnect.c:59: undefined reference to `ct_con_drop'
cmd_rpc.o: In function `rpc_param':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_rpc.c:559: undefined reference to `cs_convert'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_rpc.c:573: undefined reference to `ct_param'
cmd_rpc.o: In function `cmd_rpc':
/home/bamccaig/src/sqsh-2.1.7/src/cmd_rpc.c:228: undefined reference to `ct_cmd_alloc'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_rpc.c:441: undefined reference to `ct_cancel'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_rpc.c:476: undefined reference to `ct_cmd_drop'
/home/bamccaig/src/sqsh-2.1.7/src/cmd_rpc.c:279: undefined reference to `ct_command'
var_ctlib.o: In function `var_set_interfaces':
/home/bamccaig/src/sqsh-2.1.7/src/var_ctlib.c:82: undefined reference to `ct_config'
dsp.o: In function `dsp_cmd':
/home/bamccaig/src/sqsh-2.1.7/src/dsp.c:140: undefined reference to `ct_cmd_props'
/home/bamccaig/src/sqsh-2.1.7/src/dsp.c:181: undefined reference to `ct_send'
/home/bamccaig/src/sqsh-2.1.7/src/dsp.c:248: undefined reference to `ct_cancel'
dsp.o: In function `dsp_signal':
/home/bamccaig/src/sqsh-2.1.7/src/dsp.c:770: undefined reference to `ct_cancel'
dsp_bcp.o: In function `dsp_bcp':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_bcp.c:65: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_bcp.c:94: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_bcp.c:76: undefined reference to `ct_fetch'
dsp_csv.o: In function `dsp_csv':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_csv.c:66: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_csv.c:95: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_csv.c:77: undefined reference to `ct_fetch'
dsp_conv.o: In function `dsp_type_len':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_conv.c:505: undefined reference to `cs_convert'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_conv.c:520: undefined reference to `cs_convert'
dsp_conv.o: In function `dsp_datetime_conv':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_conv.c:361: undefined reference to `cs_dt_crack'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_conv.c:341: undefined reference to `cs_convert'
dsp_desc.o: In function `dsp_desc_fetch':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:434: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:553: undefined reference to `cs_convert'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:498: undefined reference to `cs_convert'
dsp_desc.o: In function `dsp_desc_bind':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:86: undefined reference to `ct_res_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:186: undefined reference to `ct_describe'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:369: undefined reference to `ct_bind'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:296: undefined reference to `ct_bind'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:392: undefined reference to `ct_compute_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:406: undefined reference to `ct_compute_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:136: undefined reference to `ct_compute_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_desc.c:163: undefined reference to `ct_compute_info'
dsp_horiz.o: In function `dsp_horiz':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_horiz.c:97: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_horiz.c:140: undefined reference to `ct_res_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_horiz.c:154: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_horiz.c:163: undefined reference to `ct_get_data'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_horiz.c:217: undefined reference to `ct_fetch'
dsp_html.o: In function `dsp_html':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_html.c:84: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_html.c:115: undefined reference to `ct_res_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_html.c:135: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_html.c:144: undefined reference to `ct_get_data'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_html.c:187: undefined reference to `ct_fetch'
dsp_meta.o: In function `dsp_meta_int_prop':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_meta.c:613: undefined reference to `ct_res_info'
dsp_meta.o: In function `dsp_meta_transtate':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_meta.c:640: undefined reference to `ct_res_info'
dsp_meta.o: In function `dsp_meta_desc':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_meta.c:206: undefined reference to `ct_res_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_meta.c:216: undefined reference to `ct_describe'
dsp_meta.o: In function `dsp_meta_fetch':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_meta.c:695: undefined reference to `ct_fetch'
dsp_meta.o: In function `dsp_meta':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_meta.c:65: undefined reference to `ct_results'
dsp_meta.o: In function `dsp_meta_bool_prop':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_meta.c:584: undefined reference to `ct_res_info'
dsp_none.o: In function `dsp_none':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_none.c:54: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_none.c:65: undefined reference to `ct_fetch'
dsp_pretty.o: In function `dsp_pretty':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_pretty.c:87: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_pretty.c:127: undefined reference to `ct_res_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_pretty.c:141: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_pretty.c:154: undefined reference to `ct_get_data'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_pretty.c:208: undefined reference to `ct_fetch'
dsp_vert.o: In function `dsp_vert':
/home/bamccaig/src/sqsh-2.1.7/src/dsp_vert.c:79: undefined reference to `ct_results'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_vert.c:106: undefined reference to `ct_res_info'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_vert.c:121: undefined reference to `ct_fetch'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_vert.c:127: undefined reference to `ct_get_data'
/home/bamccaig/src/sqsh-2.1.7/src/dsp_vert.c:305: undefined reference to `ct_fetch'
sqsh_ctx.o: In function `sqsh_ctx_pop':
/home/bamccaig/src/sqsh-2.1.7/src/sqsh_ctx.c:104: undefined reference to `ct_con_props'
/home/bamccaig/src/sqsh-2.1.7/src/sqsh_ctx.c:113: undefined reference to `ct_close'
/home/bamccaig/src/sqsh-2.1.7/src/sqsh_ctx.c:116: undefined reference to `ct_con_drop'
sqsh_init.o: In function `sqsh_exit':
/home/bamccaig/src/sqsh-2.1.7/src/sqsh_init.c:315: undefined reference to `ct_close'
/home/bamccaig/src/sqsh-2.1.7/src/sqsh_init.c:316: undefined reference to `ct_con_drop'
/home/bamccaig/src/sqsh-2.1.7/src/sqsh_init.c:322: undefined reference to `ct_exit'
/home/bamccaig/src/sqsh-2.1.7/src/sqsh_init.c:337: undefined reference to `cs_ctx_drop'
collect2: ld returned 1 exit status
make[1]: *** [sqsh] Error 1
make[1]: Leaving directory `/home/bamccaig/src/sqsh-2.1.7/src'
make: *** [build-subdirs] Error 2
[bamccaig@krypton sqsh-2.1.7]$ &lt;/pre&gt;&lt;em&gt;VERY&lt;/em&gt; angry. Obviously, we're missing a library. &lt;tt&gt;ct_&lt;em&gt;blah&lt;/em&gt;&lt;/tt&gt; suggested to me that it was one or more Sybase CT-Library libraries that it couldn't find. Since we're using FreeTDS in place of it, I went back to our friend rpm and with the help of grep and sed got the linking options that I was looking for.&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ rpm -ql freetds freetds-devel | \
        grep '^/usr/lib/' | \
        sed -re 's#/usr/lib/lib##' -e 's/([^\.-]*).*/\1/' -e 's/(.*)/-l\1/' | \
        sort | xargs
-lct -lct -lct -lsybdb -lsybdb -lsybdb -ltds -ltds -ltdsodbc -ltdsodbc -ltdsodbc
[bamccaig@krypton sqsh-2.1.7]$ &lt;/pre&gt;For those of you that don't speak UNIX so well yet, that basically means:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;List all the files that were installed by the freetds and freetds-devel packages (it almost certainly would suffice to just check freetds-devel, but it doesn't hurt to include freetds in the search).&lt;/li&gt;
&lt;li&gt;Filter out all options that don't begin with &lt;tt&gt;/usr/lib&lt;/tt&gt; (since we're looking for libraries and we know they are installed in &lt;tt&gt;/usr/lib&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;Remove the directory (&lt;tt&gt;/usr/lib&lt;/tt&gt;) and prefix (&lt;tt&gt;lib&lt;/tt&gt;) from each library (most shared libraries are named something like &lt;tt&gt;libfoo.so&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;Remove any trailing characters (characters that are after a dot or dash, since most shared libraries have version information appended to their name, resulting in something like &lt;tt&gt;libfoo.so.1.2.3&lt;/tt&gt;, for example; those are often symlinked by friendlier names, however, and the linker will do the work of figuring out what our pretty names mean).&lt;/li&gt;
&lt;li&gt;Prefix each library with &lt;tt&gt;-l&lt;/tt&gt;, since that is the option that GCC uses to specify libraries.&lt;/li&gt;
&lt;li&gt;Finally, sort them for easy reading and join them onto a single line.&lt;/li&gt;
&lt;/ol&gt;Reviewing the command line in the above error message, we can see that sqsh isn't being linked with any of them! That has to be problem! The h4x solution then is to edit the Makefile in question and add those to the command.&lt;br /&gt;
&lt;br /&gt;
Upon opening that file, I found a handy &lt;tt&gt;SYBASE_LIBS&lt;/tt&gt; variable that seemed rather relevant. Above it is even a handy comment suggesting that users of some platforms might need to manually edit that line. Open that file in your favorite editor...&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ vim src/Makefile&lt;/pre&gt;...and replace that line with the results from our earlier query:&lt;br /&gt;
&lt;pre&gt;SYBASE_LIBS = -lct -lct -lct -lsybdb -lsybdb -lsybdb -ltds -ltds -ltdsodbc -ltdsodbc -ltdsodbc&lt;/pre&gt;Save the file and try to build again.&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton sqsh-2.1.7]$ make
[Now it should almost certainly be happy unless the world just hates you...]
[bamccaig@krypton sqsh-2.1.7]$ su -c 'make install &amp;&amp; make install.man'
Password:
[Installation should go happily too]
[bamccaig@krypton sqsh-2.1.7]$ &lt;/pre&gt;If you can run sqsh now (for example, &lt;tt&gt;sqsh --help&lt;/tt&gt; to test) then the build and installation should have been successful! Congratulations! Now you (and I) just have to learn to &lt;em&gt;use&lt;/em&gt; it. &lt;tt&gt;:-X&lt;/tt&gt;&lt;br /&gt;
&lt;br /&gt;
To get you started, the following allows me to connect to the SQL Server located on our LAN, running on a Windows server (&lt;em&gt;**shudder**&lt;/em&gt;).&lt;br /&gt;
&lt;pre&gt;[bamccaig@krypton ~]$ sqsh -S &amp;lt;server_ip&amp;gt; -U &amp;lt;username&amp;gt;
[Program information]
Password: 
[I am now inside of a sqsh shell!]
&gt; quit
[bamccaig@krypton ~]$ &lt;/pre&gt;Obviously, not the most convenient way to connect, typing the server, username, and password (!) each time. Surely there must be handy configuration options to make things smoother. For now, I have created shortcut scripts (readable/writeable/executable only by me) that do the connecting for me to save me the trouble.&lt;br /&gt;
&lt;h5&gt;References&lt;/h5&gt;&lt;p&gt;&lt;span id="sqshell-building-and-installing-sqsh-in-ref1"&gt;1.&lt;/span&gt; Thanks to the maintainer for clearing this up via E-mail.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/qwKdBcIizo0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/5184791547759553273/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/07/sqshell-building-and-installing-sqsh-in.html#comment-form" title="14 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/5184791547759553273?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/5184791547759553273?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/qwKdBcIizo0/sqshell-building-and-installing-sqsh-in.html" title="SQshelL - Building and Installing sqsh In Linux (Fedora 13) Without The Sybase CT-Library" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>14</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/07/sqshell-building-and-installing-sqsh-in.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08HSHs6cSp7ImA9WxFWE0Q.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-4034024263994999804</id><published>2010-05-31T11:26:00.003-04:00</published><updated>2010-06-01T09:43:59.519-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-01T09:43:59.519-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="thistle" /><category scheme="http://www.blogger.com/atom/ns#" term="vbscript" /><category scheme="http://www.blogger.com/atom/ns#" term="wasabi" /><category scheme="http://www.blogger.com/atom/ns#" term="fogbugz" /><category scheme="http://www.blogger.com/atom/ns#" term="compiler" /><category scheme="http://www.blogger.com/atom/ns#" term="fog creek software" /><category scheme="http://www.blogger.com/atom/ns#" term="evil" /><category scheme="http://www.blogger.com/atom/ns#" term="wtf" /><category scheme="http://www.blogger.com/atom/ns#" term="asp" /><title>Wasabi - Apparently ASP/VBScript Isn't Evil Enough</title><content type="html">I've been looking for "project management" software (I know, &lt;em&gt;yuck&lt;/em&gt;) for some time. Mainly because I want to incorporate bug/issue tracking, discussion boards, and wikis into j0rb (and in personal projects, etc.). Firstly, it's very difficult to keep track of bugs by hand and often you forget about them before you fix them. When it comes to software development there's just too many issues, whether bugs, design decisions, or potential hiccups; to keep track of. I also want some persistent medium to discuss software design with colleagues. At j0rb we currently discuss things either through instant messaging or we talk in person.&lt;br /&gt;
&lt;br /&gt;
The problem with instant messaging is that there's no organization to it. If you forget what was discussed or what conclusions everyone came to then you have to search through chat logs and hope to find it. If you do find it, you have to sort through intertwined discussions to find the relevant material. It's also hard to have meaningful discussions because instant messengers are generally designed to send single sentences or short paragraphs. It's hard to communicate code ideas or complex ideas that require more space to type. The problem with talking in person is that there's no log at all. There's no searching it to refresh your memory, etc.&lt;br /&gt;
&lt;br /&gt;
Open source projects have gotten by with mailing lists for a long time and it seems the most experienced still prefer them to discussion boards. I figured there must be good reason for this so I've personally taken a liking to them. However, I don't think mailing lists would work well at j0rb. It's a Windows-based shop and I generally hate Windows-based mail clients for good reason. I generally use Gmail's Web interface for personal mail, which works quite well for most E-mail needs, but I wouldn't be allowed to discuss company topics through a remotely hosted service. At least, not a free one with no guarantees about security or privacy.&lt;br /&gt;
&lt;br /&gt;
I'd be happy to try &lt;tt&gt;mutt&lt;/tt&gt; from Cygwin against a locally hosted server, but odds are that my colleagues would be sending HTML E-mails and wouldn't understand why that was a problem (nor why I'd choose to use a plain-text client). For these reasons, I don't think a mailing list would work particularly well at j0rb. Colleague incompatibility. &lt;tt&gt;:P&lt;/tt&gt; A discussion board seems to be the next best thing (with the advantage of edits to correct mistakes, etc.).&lt;br /&gt;
&lt;br /&gt;
Wikis are great for keeping track of a growing and dynamic knowledge base of information. I think they'd work well for documenting certain gotchas discovered in languages, APIs, and platforms, etc.; as well as our own software's gotchas.&lt;br /&gt;
&lt;br /&gt;
Anyway, I've recently been reading &lt;a href="http://www.joelonsoftware.com/"&gt;Joel on Software&lt;/a&gt;. There are a lot of really good articles that make a lot of sense. It seems Joel Spolsky is very experienced in project management and the like. The allure of fully defined specifications and feasible schedules got me interested in taking a look at &lt;a href="http://www.fogcreek.com/fogbugz"&gt;FogBugz&lt;/a&gt;, some project management software developed by Joel's company, &lt;a href="http://www.fogcreek.com/"&gt;Fog Creek Software&lt;/a&gt;. I generally avoid commercial software, since as a general rule it's garbage, but I am willing to appreciate some commercial offerings (everything from Microsoft not being among them &lt;em&gt;*cough*&lt;/em&gt;).&lt;br /&gt;
&lt;br /&gt;
Anyway, it sounded like FogBugz was a pretty complete solution and I had hoped that it was better than what we have now (which can't do any of the things I mentioned above). I suggested it as an option to management, but the price seems kind of steep. Fortunately, they offer the hosted service for free to individuals so I decided to sign up for personal projects so I could get a look at just what it offered. I was pretty disappointed. At least at first glance, the UI seems rather bloated and disorganized. I'm having a hard time trying to figure out where different types of information are organized. It seems all it really tracks are "cases", which can be bugs, issues, or what have you. Then they use "filters" to determine which cases you see, by tons of criteria. This seems to be the way to show cases for a particular project. It seems awkward that way, but maybe that's just because I'm not used to it.&lt;br /&gt;
&lt;br /&gt;
I decided to ask the Interwebz what it thought of FogBugz. I started where I usually start: at &lt;a href="http://en.wikipedia.org/wiki/FogBugz"&gt;Wikipedia&lt;/a&gt;. It was there that I discovered, much to my dismay, &lt;a href="http://en.wikipedia.org/wiki/FogBugz#Thistle"&gt;Thistle&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/FogBugz#Wasabi"&gt;Wasabi&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
It seems that FogBugz was originally written in Classic ASP/VBScript. I can sympathize because our main software project is mostly written in the same. What appears like an &lt;em&gt;OK&lt;/em&gt; language from the surface is surely not. Visual Basic is bad enough, but VBScript is like a stripped down Visual Basic with half of the features missing or mutilated. Those that have worked with VBScript for any considerable period of time know that it is the decay-er of sanity. It has numerous limitations and has a weak "standard library" and most non-trivial functionality comes from server "components" that do not appear to be native VBScript at all (I've always assumed they were DLLs written in C or C++, but I suppose it's possible that they're actually written in pure evil). VBScript can't do much without these components. You apparently need these components for things like handling file uploads or connecting to databases. Things that other languages, like PHP, Python, and Perl can do "themselves". Probably because those languages are "open".&lt;br /&gt;
&lt;br /&gt;
Apparently, Fog Creek Software wanted FogBugz to run on Linux servers, but ASP/VBScript is (officially) an abomination of IIS and the Windows operating system. I think at this point most would realize their mistake (developing software with ASP in the first place) and work towards correcting it by rewriting the application in a better language (and one that was cross-platform), but it seems that Fog Creek decided instead to develop a "compiler" (converter, or what ever you want to call it) that could convert ASP into PHP so that they could run their application on Linux boxes without having to completely rewrite it.&lt;br /&gt;
&lt;br /&gt;
Now I don't know how many lines of code FogBugz was at the time or how complicated this "compiler", dubbed Thistle, was to write (it's over my head, I know that); but I know that it only took a few days of maintaining an ASP/VBScript application for me to begin begging almost daily to rewrite the application in something more sane. They didn't stop there, however. They apparently realized that VBScript combined with Thistle was too limited in what it could do (&lt;em&gt;light bulb&lt;/em&gt;, anyone?). They missed their second opportunity to change platforms and instead wrote another "compiler" that extends VBScript's functionality, adding modern features that probably should have been there in the beginning, and spits out either PHP or .NET. They call this monstrosity Wasabi. All of that trouble to extend evil that never should have existed in the first place. I can't imagine that developing Thistle and Wasabi was faster than just rewriting their application in a cross-platform language, preferably an open one that will be around for a while, and leaving it at that.&lt;br /&gt;
&lt;br /&gt;
Apparently they keep both of these tools internal. So not only did they create such evils, encouraging the persistence of VBScript-ness, but they didn't even release it to the world for them to benefit (free or otherwise). &lt;em&gt;WTF.&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
I'm somehow less enthusiastic about FogBugz now..&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/SpNdOiY11WY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/4034024263994999804/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/05/wasabi-apparently-aspvbscript-isnt-evil.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/4034024263994999804?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/4034024263994999804?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/SpNdOiY11WY/wasabi-apparently-aspvbscript-isnt-evil.html" title="Wasabi - Apparently ASP/VBScript Isn't Evil Enough" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/05/wasabi-apparently-aspvbscript-isnt-evil.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE4NRXw8eip7ImA9WxFbFEk.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-8652243544512467490</id><published>2010-05-18T19:24:00.049-04:00</published><updated>2010-07-06T16:09:54.272-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-07-06T16:09:54.272-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="personal" /><category scheme="http://www.blogger.com/atom/ns#" term="incompetence" /><category scheme="http://www.blogger.com/atom/ns#" term="driving" /><category scheme="http://www.blogger.com/atom/ns#" term="traffic" /><category scheme="http://www.blogger.com/atom/ns#" term="cars" /><category scheme="http://www.blogger.com/atom/ns#" term="tailgating" /><category scheme="http://www.blogger.com/atom/ns#" term="story" /><category scheme="http://www.blogger.com/atom/ns#" term="opinionated" /><title>Incompetent Drivers - Tailgating</title><content type="html">I live 20 - 30 minutes outside of the city I work in so I commute each day. I have a heavy foot and like to drive fast, but I do it safely. I never exceed the speed that I feel is safe for the road conditions and I'm not afraid to drive well under the speed limit if conditions require it. I aced the &lt;a href="http://www.yd.com/"&gt;Young Drivers of Canada&lt;/a&gt; driving school program when I was 17 and very much appreciated the things they taught me (to my dismay, most of my classmates joked about how stupid the lessons were...). Driving just came naturally to me, but some of the things I learned from Young Drivers of Canada really opened my eyes. They made the more tedious tasks of driving become routine and logical (things like where your mirrors should be pointed, how to effectively see all around your vehicle, and how to easily back into a parking space) and really just gave me the confidence to do the safe thing instead of the easy thing. It's a real load off of your shoulders to hear someone in a position of "authority" say that &lt;em&gt;it's OK&lt;/em&gt; to hold up traffic et. al. It's common sense, but "peer pressure" has a way of clouding your judgment. I think we're all victims of this at times.&lt;br /&gt;
&lt;br /&gt;
Anyway, I love to drive and I think that I'm a very good driver. I do like to go fast though. When I was ~20 I got two speeding tickets within a month of each other. Fortunately, the police officers were kind both times, but my insurance company still hit the fan when it found out. They had me signing documents agreeing to never drive my parents' cars and forced me to insure myself under my own name instead of under my father's (the latter is much cheaper than the former for those that don't know). Anyway, that didn't last too long and I eventually broke through the threshold period, but it was enough of an eye opener to see just how expensive it could be if you get caught. I would like to argue unfairly so since I'm such a safe driver, but there's obviously no way for the police nor insurance companies to know who is competent vs. who is reckless.&lt;br /&gt;
&lt;br /&gt;
After that I always stay below the speed limit (the actual speed limit though, not necessarily the posted one), at least when there's a chance that police could be ahead (or following me; or in the air!). For my second speeding ticket I was caught on radar from a few kilometers (and miles) away. Think of a straight stretch of highway so long that you can't even see the corner. The police must have been sitting on the shoulder right at the corner. It blew me away just how far they could see. There was absolutely no chance anyone could have seen them, at least not without illegal scanning technology. I was impressed, albeit beaten. In my defense, I was passing slow drivers on a completely open highway so I was somewhat justified and it was completely safe. I digress...&lt;br /&gt;
&lt;br /&gt;
Anyway, speeding or not, I always focus on safety. I never drive faster than is safe and always try to drive defensively. I'm not going to get anywhere faster being two car lengths ahead and still trapped behind traffic so there's no benefit to tailgating anyway. It's just &lt;em&gt;stupid&lt;/em&gt;. Not everyone is so enlightened as I am though.&lt;br /&gt;
&lt;br /&gt;
Usually when I encounter tailgaters its on the highway, which is silly because for the past 4 years or so I've been driving on a 4-lane highway. If I'm not going fast enough for you then &lt;em&gt;pass me&lt;/em&gt;. There's an open lane for just such a purpose! &lt;em&gt;SURPRISE&lt;/em&gt;. This doesn't seem to occur to people. I can only assume then that they think they're saving fuel by &lt;a href="http://en.wikipedia.org/wiki/Drafting_(aerodynamics)"&gt;"drafting"&lt;/a&gt; me or something. Not enough fuel, if any, to outweigh the risks, I promise you that.&lt;br /&gt;
&lt;h4&gt;Defensive Driving&lt;/h4&gt;My usual defense against tailgaters is to let off the accelerator and just coast. It isn't so drastic that they don't have time to react, like they would if I braked (since they're following too goddamn close), but it's enough to get a reaction out of them. The typical reaction is for them to jump into the left lane to pass me. Little do they know that I'm not slowing down to stop or turn. I'm slowing down to get them off my ass. Once they are, I pedal-to-the-metal back up to the speed limit. If they want to pass me then by all means pass me, but I'm not going to reward their incompetence by passing me for them. My accelerating is basically "body" language to communicate that I know they're tailgating me and I won't allow it. &lt;em&gt;GET. OFF. MY. ASS.&lt;/em&gt; There really isn't much they can do about it. They can either comply, get repeatedly slowed down because I keep doing it until they stop (wasting fuel in the process to get back up to speed), or hit me (in which case either the police will be called and they'll be charged or I'll follow them home and &lt;em&gt;[censored]&lt;/em&gt;). There's also the possibility that they could follow me home, but I feel I'm not doing anything worthy of such vengeful acts and if they're that upset by it then sooner or later they're going to snap and it might as well be on just me than on an entire family or something. I also tend to watch for people following me (some people say I can be paranoid; &lt;em&gt;pffft&lt;/em&gt;) so I'd hopefully notice them and just not go home. I'd just call the police from my cell phone and let them catch up to us. Of course, they could try to run me off the road too, but this still fits under the &lt;em&gt;it-might-as-well-be-me&lt;/em&gt; category. Besides, as I said I'm fairly confident driving and love to push the limits so I'd say I'm better prepared for such maniacs than the average driver on the road would be. If they kill me, &lt;em&gt;shucks&lt;/em&gt;. The world will not stop turning. Hopefully the police catch them and lock them up.&lt;br /&gt;
&lt;h4&gt;The Event&lt;/h4&gt;Anyway, on to today. I'm driving home as I usually do. I'm still within city limits driving on Trunk Road in Sault Ste. Marie, Ontario, Canada.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/?q=Trunk+Rd,+Sault+Ste.+Marie,+Algoma+District,+Ontario,+Canada&amp;amp;ie=UTF8&amp;amp;hl=en&amp;amp;cd=1&amp;amp;geocode=FWvtxQIdJTj6-g&amp;amp;split=0&amp;amp;hq=&amp;amp;hnear=Trunk+Rd,+Sault+Ste.+Marie,+Algoma+District,+Ontario,+Canada&amp;amp;t=h&amp;amp;z=14&amp;amp;layer=c&amp;amp;cbll=46.531185,-84.252621&amp;amp;panoid=ikS72Pset7fV8pRrYA-c0A&amp;amp;cbp=12,54.45,,0,3.77&amp;amp;ll=46.525803,-84.264923&amp;amp;source=embed&amp;amp;output=svembed"&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;small&gt;&lt;a href="http://maps.google.com/?q=Trunk+Rd,+Sault+Ste.+Marie,+Algoma+District,+Ontario,+Canada&amp;amp;ie=UTF8&amp;amp;hl=en&amp;amp;cd=1&amp;amp;geocode=FWvtxQIdJTj6-g&amp;amp;split=0&amp;amp;hq=&amp;amp;hnear=Trunk+Rd,+Sault+Ste.+Marie,+Algoma+District,+Ontario,+Canada&amp;amp;t=h&amp;amp;z=14&amp;amp;layer=c&amp;amp;cbll=46.531185,-84.252621&amp;amp;panoid=ikS72Pset7fV8pRrYA-c0A&amp;amp;cbp=12,54.45,,0,3.77&amp;amp;ll=46.525803,-84.264923&amp;amp;source=embed" style="color:#0000FF;text-align:left"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;
The speed limit is 70 km/h, though you can generally get away with about 85 km/h. If you do get ticketed, and you technically can, you shouldn't lose any demerit points so it's not too much of a risk. Not all traffic speeds though and I respect that. As long as they're doing the speed limit (or have a valid excuse not to) I'm fine with it. I'll go around them if it's safe to (Trunk Road is a 5-lane) or else I'll wait until we get to the highway just ahead where things usually open up more and &lt;em&gt;then&lt;/em&gt; I'll pass them. Today there was a small line of slow moving traffic in the left lane so I pulled into the right lane to pass them (not something I like to do on the highway unless absolutely warranted&lt;a href="#incompetent-drivers-tailgating-ref1"&gt;[1]&lt;/a&gt;, but within city limits traffic is much more disorganized). As I checked my mirrors and blind spot, I noticed there was a silver Volkswagen Jetta (a relatively new model) coming up on me quickly. Now we'd been cruising at the same speed for a few kilometers so it wasn't like he was blind-sided. He could have changed lanes before catching up to us and moved right on past. Instead, he came right up behind me. At this point, the right lane was clear so I pulled into it to pass the slower moving traffic.&lt;br /&gt;
&lt;br /&gt;
I accelerated to 85 km/h and set my cruise control. I watched in my rear view mirror as the Volkswagen eventually pulled around the slower moving traffic and pulled in behind me. Albeit, it was a delayed reaction. Someone in such a hurry shouldn't have been so slow unless they're incompetent (and therefore shouldn't be speeding), but I digress. This Volkswagen accelerates right up on my ass to the point where I can't even see his bumper in my rear-view mirror. I would guess maybe a car length or so behind me, but it's obviously hard to judge. A car length is not enough at 85 km/h. I'm driving a '95 Pontiac Grand Prix so it's not like it's the greatest stopping vehicle in the world, but a car length isn't even enough time for a human to react at 85 km/h if I happen to need to lock up my brakes. He was too goddamn close. As usual, I let off my accelerator (actually disengaged my cruise control) and started coasting. The Volkswagen hesitated to slow down, but then aggressively switched lanes moving in behind the slower moving traffic to our left. As usual, the threat was diverted so I accelerated hard back up to the "real" speed limit of 85 km/h. I saw in my rear view mirror that the Volkswagen jumped back in behind me, which is to be expected since the other lane is blocked, but what pisses me off is that he goes right back to tailgating me. He didn't take the hint or is just too much of an asshole to care. I let off my accelerator again (again technically disengaging my cruise control), but saw that he was wise (I use that term loosely) to me now and wasn't phased by it. I flipped him the bird to let him know bluntly that I was displeased with him and decided to just gun it past the slow moving cars so I could get him off my ass.&lt;a href="#incompetent-drivers-tailgating-ref2"&gt;[2]&lt;/a&gt; Speeding ticket be damned. I would have loved to see a cop right then. This asshole hammers down too (he's in a newer car in better condition so he has no trouble keeping up) and rides my ass all the way past the slower line of cars. Up ahead I was making a left turn onto the highway so as I came around the slower moving cars I smoothly signaled and began to lane change. As I did, I saw in my rear-view mirror that the Volkswagen driver changed lanes with me. I did it as soon as it was safe to in front of the slower moving traffic so he must have been cutting them off very closely. Once he saw that I had occupied the other lane he changed lanes and aggressively passed me. I held up the bird as he passed, trying to get a look at the driver, but he had tinted windows and I couldn't see anything.&lt;br /&gt;
&lt;br /&gt;
The Volkswagen driver eventually pulled into the left lane a ways ahead of me, but at literally the last second jumped back into the right lane, taking the old highway out of town instead of the new one. Now he was nowhere near me so he couldn't have been toying with me. And if he had planned to take the old highway all along why would he have switched lanes? My best guess is that he realized that I was taking the new highway and didn't want to be himself followed home. Smart move because with him in front of me I would very likely have gotten his license plate and reported him to the police (it was far too tense to do so earlier). It's also possible that he spotted a "cop?" just up ahead down the new stretch of highway and got scared, but it wasn't your typical cop. I think it was the kind of cop that pulls over transports and inspects their loads and stuff. They have a different car anyway.&lt;br /&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;The Volkswagen driver and I parted ways there, but I kept an eye out for him ahead hoping we would cross paths again since both highways essentially lead to the same place (so I could call the police; somebody that aggressive should be taken off the streets). Unfortunately, I didn't see him again. Maybe my bald head and baggy clothes scared him (I know it's enough for the police to profile me... &lt;tt&gt;:P&lt;/tt&gt;). I can only hope.&lt;br /&gt;
&lt;br /&gt;
I was pretty shaken up after the fact, as you get when you're both angry and afraid of what might happen. It occurred to me when I got home and had calmed down that having a camera mounted in the back of my car would be really useful. Snap a photo of these idiots' license plates without taking my eyes off the road and calmly pull over to report them. That would be a wonderful luxury.&lt;br /&gt;
&lt;br /&gt;
Now I know that I've been referring to the Volkswagen driver as a "he", but that's just out of habit since I didn't get a look at the driver (tinted windows) and guys tend to be the more aggressive drivers. However, I don't want to eliminate the possibility that this was indeed a woman. Men tend to be dangerous because of testosterone. They're chemically charged for it. Women tend to be dangerous drivers because they're incompetent. I don't mean any offense to the women, but it's nothing new that women are far more likely to consider a car nothing more than a means of transportation whereas men often consider them fun and dedicate time to learning all about them. I regularly see women speeding on the highway (and I mean &lt;em&gt;speeding&lt;/em&gt; because they're blowing past me and I'm already "speeding") and the impression I get is that they aren't really aware of how the vehicle's characteristics change as velocity changes. Just this morning a woman passed me on the highway in a shallow downhill S-curve. She came around me in the middle of it and on the way out of it she drifted a couple of feet over the center line. You may have thought she was changing lanes having overtaken me (albeit she started drifting a little early for that), but no, she wasn't. She continued on her path a couple of feet over the center line and then moved back in the left lane before finally changing lanes another 200 yards down the highway (without signaling). &lt;em&gt;Sigh&lt;/em&gt;.&lt;br /&gt;
&lt;br /&gt;
I can't wait until we have computers capable of driving cars in traffic so that everyone &lt;em&gt;else&lt;/em&gt; can surrender their license. &lt;tt&gt;&amp;gt;:(&lt;/tt&gt; I, however, love to drive, so I'll continue to do so myself, thank you very much.&lt;br /&gt;
&lt;br /&gt;
&lt;h5&gt;References&lt;/h5&gt;&lt;p&gt;&lt;span id="incompetent-drivers-tailgating-ref1"&gt;1.&lt;/span&gt; I've seen a pattern of incompetent drivers driving in the left lane. The highway is &lt;i&gt;never&lt;/i&gt; so busy that you need two lanes of traffic. The left lane is basically just a passing lane. Somebody that drives in it constantly appears incredibly incompetent to me. Occasionally you encounter slow moving traffic that is driving in the left lane, forcing you to pass them on the right (or awkwardly follow behind them).&lt;/p&gt;&lt;p&gt;&lt;span id="incompetent-drivers-tailgating-ref2"&gt;2.&lt;/span&gt; I regretted this decision almost instantaneously. I essentially let him win, but I felt like I had little choice. If I slowed down enough the slower moving traffic would pass me and he'd move between us. If I stayed with the slower moving traffic he would stay on my ass which just wasn't safe. If this was Los Angeles I would have put a few shots through his windshield, but again I digress.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/imGvrrNc9rc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/8652243544512467490/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/05/incompetent-drivers-tailgating.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/8652243544512467490?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/8652243544512467490?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/imGvrrNc9rc/incompetent-drivers-tailgating.html" title="Incompetent Drivers - Tailgating" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/05/incompetent-drivers-tailgating.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0cFRn4yfyp7ImA9Wx9UGEw.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-7594698066916141550</id><published>2010-05-18T16:08:00.023-04:00</published><updated>2011-02-15T18:03:37.097-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-15T18:03:37.097-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="scm" /><category scheme="http://www.blogger.com/atom/ns#" term="bash" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="subversion" /><category scheme="http://www.blogger.com/atom/ns#" term="shell" /><category scheme="http://www.blogger.com/atom/ns#" term="branching" /><category scheme="http://www.blogger.com/atom/ns#" term=".net4" /><category scheme="http://www.blogger.com/atom/ns#" term="git" /><category scheme="http://www.blogger.com/atom/ns#" term="linq to sql" /><category scheme="http://www.blogger.com/atom/ns#" term="code" /><category scheme="http://www.blogger.com/atom/ns#" term="linq" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="script" /><category scheme="http://www.blogger.com/atom/ns#" term="csharp" /><title>Source Code Management -- A Minor Success Story</title><content type="html">In my quest to become an open source [UNIX-y] programmer (or hacker :-o), I try to learn about development tools and practices that make development easier. One such category of tool is source code management (SCM) tools (also called version control software). Essentially they keep track of the history of changes that you make to your code&lt;a href="#source-code-management-minor-success-ref1"&gt;[1]&lt;/a&gt;, as well as who made each change, when they made the change, and even comments from the author of the change explaining the change. This greatly helps developers manage a project because it allows us to keep track of what we've done and even what we're doing right now. It allows you to undo changes that you've made easily and share changes easily between developers. There are plenty of benefits so don't consider this a complete list.&lt;br /&gt;
&lt;br /&gt;
For the record I've found Git to be the best SCM. At j0rb, however, we use Subversion. I myself learned about Subversion a year or so after college and taught myself to use it. Then I introduced j0rb to it and eventually managed to get it adopted. More recently (~past six months) I started using Git after watching Google Tech Talks on YouTube of Linus Torvalds and Randal Schwartz explaining why Git is the superior SCM and why everything else sucks. I didn't want to be "stupid and ugly" so I naturally adopted Git. Now I'd like to switch j0rb over to Git, but I mostly work with Windows-y, GUI-y programmers that are afraid of something like Git. Needless to say, they are refusing to change for now. We all know what that makes them.&lt;br /&gt;
&lt;br /&gt;
Anyway, I've been working on something at j0rb for the past ~week. With Git, I could easily branch and/or commit locally as I go to separate changes, but with Subversion branching and merging is expensive and painful and there's no local repository to commit to. That's because everything is centralized so committing would put my changes in the central repository that everyone uses, which would mean that the application that my colleagues and I are working on would be broken until I'm done, preventing others from doing any work of their own.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Branching (Side Tracked)&lt;/h4&gt;One of the nicest things about Git's branching mechanism is that I don't need to go anywhere in the file system. When I change branches in Git, Git automatically makes my working directory that I'm already in the branch that I'm switching to (checking out, technically). With Subversion, a branch is really just a copy of some tree; it's a duplicate of a subtree. In order to work on the new branch, I need to check it out somewhere else on my file system (or I could remove my working directory and overwrite it with the new branch). To demonstrate:&lt;br /&gt;
&lt;pre&gt;# With Git, it's simple. Create and checkout a new branch named 'newbranch'
# based on the master branch at the current HEAD of the branch (last commit).
bamccaig@castopulence:~/src/example$ git status
# On branch &lt;u&gt;master&lt;/u&gt;
nothing to commit (working directory clean)
bamccaig@castopulence:~/src/example$ git checkout -b newbranch master
Switched to a new branch "newbranch"
bamccaig@castopulence:~/src/example$ git status
# On branch &lt;u&gt;newbranch&lt;/u&gt;
nothing to commit (working directory clean)
bamccaig@castopulence:~/src/example$&lt;/pre&gt;As can be seen above, with one simple command Git has created a branch and I'm already in it! I didn't have to do anything else. I can just start working. And Git is &lt;i&gt;fast&lt;/i&gt; when it comes to branching so I didn't have to wait for anything. The new branch just points to the master branch so there was no need for expensive duplication of data. Subversion tells a different story, however:&lt;br /&gt;
&lt;pre&gt;# With Subversion, it's quite painful and it's also pretty slow. Create
# and checkout a new branch named 'newbranch' based on the repository
# trunk in the HEAD revision. Note that in my experience it's best to do
# branching in Subversion server-side. At least if you ever intend to
# merge back into the original branch...
bamccaig@castopulence:~/src/example/trunk$ svn cp -m 'Example...' \
        file:///home/bamccaig/src/example.repo/trunk \
        file:///home/bamccaig/src/example.repo/branches/newbranch

Committed revision 2.
bamccaig@castopulence:~/src/example/trunk$ svn up .. &amp;&amp; \
        cd ../branches/newbranch
A    ../branches/newbranch
A    ../branches/newbranch/foo
A    ../branches/newbranch/bar
A    ../branches/newbranch/baz
Updated to revision 2.
bamccaig@castopulence:~/src/example/branches/newbranch$ 
&lt;/pre&gt;Notice that Subversion basically requires me to not only type out a semi-lengthy URL (&lt;em&gt;twice!&lt;/em&gt;) and download another complete copy of the original branch (in this case &lt;tt&gt;branches/newbranch&lt;/tt&gt;, which is a copy of &lt;tt&gt;trunk&lt;/tt&gt;), but also requires me to move around in the file system. In this simple example, I had the entire tree in my working copy (from the root of the repository, including &lt;tt&gt;branches&lt;/tt&gt;, &lt;tt&gt;tags&lt;/tt&gt;, and &lt;tt&gt;trunk&lt;/tt&gt;). Often though you aren't interested in all of the many branches and tags that exist so you'll only checkout the branch(es) that you're interested in. In that case, you have to type out a checkout command with what's probably a semi-lengthy URL and then type out a command to change to the new branch's working directory. In short, branching in Subversion is just no fun. And don't get me started on merging... &lt;tt&gt;:'(&lt;/tt&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Back To The Story&lt;/h4&gt;OK, so here I am with a lot of changes to my working copy (give or take, 15 added or modified files). I come back in to work on Monday after the weekend and start working on a separate, though related project. When I finally get that done in mid-afternoon I get back to my original project, rebuild it and run it (something I generally do to get an idea of what state things are in and remind myself what I was working on last; again, Subversion doesn't help much when there's 15 added or modified files). To my dismay, this ASP.NET project throws a &lt;code&gt;StackOverflowException&lt;/code&gt; immediately upon launching in Visual Studio's development Web server, which subsequently "crashes" the server since there's really no recovering from that. Unfortunately, with a &lt;code&gt;StackOverflowException&lt;/code&gt;, there is apparently no stack trace (something I discovered right then) because the stack&lt;a href="#source-code-management-minor-success-ref2"&gt;[2]&lt;/a&gt; itself is in an unholy state. On to tracking down what was causing the problem. But how? A stack overflow usually means you're either calling too many nested functions (often a result of recursion) or you've allocated too much memory on the stack.&lt;br /&gt;
&lt;br /&gt;
Here's where the SCM comes in handy (albeit, this particular SCM still comes up short). I tried looking at all of the changes I had made since my last commit to see if I could spot anything suspicious.&lt;br /&gt;
&lt;pre&gt;[bamccaig@j0rb:foo]$ svn diff | less -S&lt;/pre&gt;Unfortunately, nothing stood out. I had added some new LINQ to SQL entities to the project and added some code to work with them. Much of the new code was generated for me by Visual Studio. The code I had worked on didn't stand out as a culprit.&lt;br /&gt;
&lt;br /&gt;
This is where having Cygwin installed, a UNIX-like environment for Windows, comes in handy&lt;a href="#source-code-management-minor-success-ref3"&gt;[3]&lt;/a&gt;. I decided the most efficient way for me to find the problem was to undo the changes I had made, confirm that it worked, and then redo the changes bit by bit until I encountered the &lt;code&gt;StackOverflowException&lt;/code&gt;. This way I would know where to look for problems: the last applied changes. UNIX and UNIX-like operating systems (and Cygwin, as mentioned above) have tools that make this easy. First, I generate patches&lt;a href="#source-code-management-minor-success-ref4"&gt;[4]&lt;/a&gt; with the SCM, Subversion, and a little shell scripting.&lt;br /&gt;
&lt;pre&gt;[bamccaig@j0rb:foo]$ for f in `svn st | grep '^M' | \
        sed -r 's/M *(.*)/\1/'`;
do
    svn diff "$f" 1&gt; "$f.patch" &amp;&amp; svn revert "$f";
done&lt;/pre&gt;For every file, &lt;tt&gt;foo/bar&lt;/tt&gt;, that was modified since the last commit, I get a file &lt;tt&gt;foo/bar.patch&lt;/tt&gt; that stores the changes made. Then I undo those changes (&lt;tt&gt;svn revert&lt;/tt&gt;). For added files, the changes are irrelevant because they're basically the entire file anyway so instead I just temporarily remove them from the Visual Studio project. I can easily get a list of which files to remove though using Subversion and the shell again.&lt;br /&gt;
&lt;pre&gt;[bamccaig@j0rb:foo]$ svn st | grep '^A'&lt;/pre&gt;With all the changes undone (there were no deleted files in my working copy) I was able to retest the code. Lo and behold, it runs fine now. This confirmed that it was indeed me that broke it (&lt;em&gt;damn&lt;/em&gt;). That came as no surprise though because I had been working on it for close to a week without problems and without pulling changes from the central repository. I was the only one making changes.&lt;br /&gt;
&lt;br /&gt;
Now comes the fun part. Applying each patch one at a time and testing. It might sound tedious, but imagine how much &lt;em&gt;more&lt;/em&gt; tedious it would be without the SCM or UNIX tools. To apply the patches from before, we use (&lt;em&gt;surprise&lt;/em&gt;) the &lt;tt&gt;patch&lt;/tt&gt; program.&lt;br /&gt;
&lt;pre&gt;[bamccaig@j0rb:foo]$ patch -p0 -ui path/to/the.patch&lt;/pre&gt;The &lt;tt&gt;-p0&lt;/tt&gt; option is required to leave paths in the patches alone. The default behavior for &lt;tt&gt;patch&lt;/tt&gt; is to strip off the directory part, leaving only the filename. That only works if the file you're patching is in the current working directory. Mine are all over the working tree. The paths just happen to be correct from where I'm working though so the &lt;tt&gt;0&lt;/tt&gt; says to strip nothing from them. The &lt;tt&gt;-u&lt;/tt&gt; option tells &lt;tt&gt;patch&lt;/tt&gt; that the patch file is in unified format, which is what Subversion's &lt;tt&gt;diff&lt;/tt&gt; sub-command outputs by default. &lt;tt&gt;patch&lt;/tt&gt; will likely figure this out on its own, but why waste the resources? &lt;tt&gt;:P&lt;/tt&gt; The &lt;tt&gt;-i&lt;/tt&gt; option specifies the patch file to use (which is followed by the path to the file).&lt;br /&gt;
&lt;br /&gt;
Since I'm using Cygwin, with UNIX newlines&lt;a href="#source-code-management-minor-success-ref5"&gt;[5]&lt;/a&gt; on a Windows-based project, however, &lt;tt&gt;patch&lt;/tt&gt; is going to somewhat mangle my source code by filling my files with the wrong newline type. It's not a problem. The code will still work, but Subversion will see every line as a change, which will make reviewing the changes later on rather difficult. To fix this, we can use the &lt;tt&gt;unix2dos&lt;/tt&gt; tool to convert them back. To save myself a lot of tedious typing, I created a bash function for all of this.&lt;br /&gt;
&lt;pre&gt;p() {
    patch="$1"
    file="`dirname $patch`/`basename $patch .patch`";
    patch -p0 -ui "$patch" &amp;&amp; unix2dos "$file" &amp;&amp; rm -i "$patch";
}&lt;/pre&gt;This way instead of typing out that long &lt;tt&gt;patch ... unix2dos&lt;/tt&gt; command line, I can just say &lt;tt&gt;`p path/to/file.patch'&lt;/tt&gt;. After each patch is applied, I'd confirm that it applied properly and remove the patch file to mark which ones I had done. Then I'd refresh the Visual Studio solution, rebuild it, and run it. If there was no &lt;code&gt;StackOverflowException&lt;/code&gt; then I'd move on to the next patch. Once again, the SCM and UNIX tools allow me to easily track my progress. The following function listed which patches I had yet to apply:&lt;br /&gt;
&lt;pre&gt;[bamccaig@j0rb:foo]$ c() {
    svn st | grep patch;
}&lt;/pre&gt;I used that list to try to apply patches in order of dependencies to avoid unrelated problems.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;The Suspense Is Killing Me!!!11&lt;/h4&gt;So what was the bug in the code causing the &lt;code&gt;StackOverflowException&lt;/code&gt;? I have no clue... &lt;tt&gt;:(&lt;/tt&gt; After going through the above, I seemingly have applied all patches and added all new files back to the project and it works fine. The only bug I encountered in the code had to do with a data-layer interface that I recently tried (&lt;em&gt;again&lt;/em&gt;, I now realize) to get fancy with. Essentially, LINQ to SQL is handled through a &lt;code&gt;DataContext&lt;/code&gt; class that is generated for you by Visual Studio. When you query for a set of entities, they are linked to the &lt;code&gt;DataContext&lt;/code&gt; that retrieves them. When you make changes to them, the &lt;code&gt;DataContext&lt;/code&gt; knows and uses those changes to generate SQL that ultimately updates the database. However, often times the changes are coming from the client, or take place over a few layers of the application. It's hard in some of these instances to maintain the original &lt;code&gt;DataContext&lt;/code&gt; and the entities that are attached to it. It's particularly difficult when using serialization to communicate with a user agent. Fortunately, there is an interface to attach detached entities. Unfortunately, it requires both the modified object and the original unmodified object to know what record it's dealing with. This means that something as simple as saving an entity can require an entity-specific query and it just generally results in code bloat. To get around this, I created an interface that returns a LINQ &lt;code&gt;Expression&lt;tdelegate&gt;&lt;/code&gt; that identifies the entity and implement it for each entity. This way, the framework that I've developed can automatically fetch the original object, reducing the bloat to a simple call:&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; LinqManager&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;Save&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;entity&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt; &lt;span class="co1"&gt;// INSERT or UPDATE.&lt;/span&gt;
&lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; LinqManager&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;Delete&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;entity&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt; &lt;span class="co1"&gt;// DELETE.&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Anyway, getting even more lazy, I also added an interface that returns the record identifier (all tables of this database have &lt;code&gt;INT&lt;/code&gt; record identifiers). This allowed me to generate a typed LINQ &lt;code&gt;Expression&lt;tdelegate&gt;&lt;/code&gt; using generics and that interface.&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;span class="kw1"&gt;public&lt;/span&gt; Expression&lt;span class="sy0"&gt;&amp;lt;&lt;/span&gt;Func&lt;span class="sy0"&gt;&amp;lt;&lt;/span&gt;IEntity, &lt;span class="kw4"&gt;bool&lt;/span&gt;&lt;span class="sy0"&gt;&amp;gt;&amp;gt;&lt;/span&gt; DefaultIdPredicate&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; IEntity e&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;return&lt;/span&gt; o &lt;span class="sy0"&gt;=&amp;gt;&lt;/span&gt; o&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;GetRecordId&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="sy0"&gt;==&lt;/span&gt; e&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;GetRecordId&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
It sounds good and compiles happy, but it fails hard at run-time because LINQ to SQL can't translate it into SQL. During the process of applying the above patches I eventually ran into an exception whose message spoke of this. It was then that I remembered trying it previously (which is why the above method existed already), but it failed and I reverted, leaving the method intact for a future revelation. Instead I'm stuck resorting to ugly type-casting and explicit property access, which I manually re-coded throughout the project. That is the only fix that I made to the code as I applied the changes.&lt;br /&gt;
&lt;br /&gt;
I can only &lt;em&gt;hope&lt;/em&gt; that was the problem, though I'm not sure how that could cause a &lt;code&gt;StackOverflowException&lt;/code&gt;, and be thankful that I had an SCM, even a poor excuse for one, and a UNIX-like environment to help me through this mess..&lt;br /&gt;
&lt;br /&gt;
&lt;h5&gt;References&lt;/h5&gt;&lt;span id="source-code-management-minor-success-ref1"&gt;1.&lt;/span&gt; Source code management (version control software) tools aren't limited to tracking source code. They can actually track changes to any set of files (though it may depend on the particular tool), but as a general rule they don't work as well with binary files as they do with text files.&lt;br /&gt;
&lt;br /&gt;
&lt;span id="source-code-management-minor-success-ref2"&gt;2.&lt;/span&gt; If you're unfamiliar with the call stack or stack vs. heap then ask Wikipedia. I'm normally happy to explain it, but I feel exceptionally lazy right now. I'll give you some &lt;a href="http://en.wikipedia.org/wiki/Call_stack"&gt;hints&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Dynamic_memory_allocation"&gt;though&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span id="source-code-management-minor-success-ref3"&gt;3.&lt;/span&gt; Though not as handy as running a UNIX-like operating system, such as Linux, would be. Unfortunately, I'm stuck with Windows at j0rb, but I digress..&lt;br /&gt;
&lt;br /&gt;
&lt;span id="source-code-management-minor-success-ref4"&gt;4.&lt;/span&gt; Patches are essentially instructions for how to change a file automatically. They show the difference between two files, which can be used to automatically modify the original and produce the new one. I feel like I'm doing a horrible job explaining this today so I'm trying to refer to material that will do a better job explaining than I can right now. See &lt;a href="http://en.wikipedia.org/wiki/Patch_(computing)#In_software_development"&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span id="source-code-management-minor-success-ref5"&gt;5.&lt;/span&gt; &lt;a href="http://en.wikipedia.org/wiki/Newline"&gt;http://en.wikipedia.org/wiki/Newline&lt;/a&gt;.&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/KvoMeSHB-SQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/7594698066916141550/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/05/source-code-management-minor-success.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/7594698066916141550?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/7594698066916141550?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/KvoMeSHB-SQ/source-code-management-minor-success.html" title="Source Code Management -- A Minor Success Story" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/05/source-code-management-minor-success.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAHQHs8fip7ImA9WxFQEk0.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-6533345894388280022</id><published>2010-05-05T13:46:00.019-04:00</published><updated>2010-05-07T00:32:11.576-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-07T00:32:11.576-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="xpath namespaces" /><category scheme="http://www.blogger.com/atom/ns#" term=".net4" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="xpath" /><category scheme="http://www.blogger.com/atom/ns#" term="xml" /><category scheme="http://www.blogger.com/atom/ns#" term="code" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><category scheme="http://www.blogger.com/atom/ns#" term="xslt" /><title>.NET + XPath + Namespaces (Conclusion)</title><content type="html">In &lt;a href="http://www.bamccaig.com/2010/04/net-xpath-namespaces.html"&gt;my last post&lt;/a&gt;, I discussed how XML namespaces were interfering with XPath expressions that were being used by an application to map XML data. Thanks to kind people on &lt;a href="irc://irc.freenode.net/#xml"&gt;#xml on irc.freenode.net&lt;/a&gt; I finally made sense of it.&lt;br /&gt;
&lt;br /&gt;
Within an XSLT document, which you of course know is an XML document, XPath expressions can apparently use the namespaces defined in the XSLT document (I assume then that they can't use namespaces defined in the transforming XML; &lt;i&gt;confirmed&lt;/i&gt;). I think that means that if I have the following XML document:&lt;br /&gt;
&lt;br /&gt;
&lt;table class="xml geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;?xml&lt;/span&gt; &lt;span class="re0"&gt;version&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span class="re0"&gt;encoding&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="re2"&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;root&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;level1&lt;/span&gt; &lt;span class="re0"&gt;xmlns:foo&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;http://www.bamccaig.com/foo&amp;quot;&lt;/span&gt;&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;level2&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Text&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/level2&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/level1&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/root&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Then I could use an XSLT document like this to transform it:&lt;br /&gt;
&lt;br /&gt;
&lt;table class="xml geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;?xml&lt;/span&gt; &lt;span class="re0"&gt;version&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span class="re0"&gt;encoding&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="re2"&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;xsl:stylesheet&lt;/span&gt; &lt;span class="re0"&gt;version&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span class="re0"&gt;xmlns:xsl&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span class="re0"&gt;xmlns:bar&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;http://www.bamccaig.com/foo&amp;quot;&lt;/span&gt;&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;xsl:template&lt;/span&gt; &lt;span class="re0"&gt;match&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;//bar:level2&amp;quot;&lt;/span&gt;&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;xsl:value-of&lt;/span&gt; &lt;span class="re0"&gt;select&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;text()&amp;quot;&lt;/span&gt; &lt;span class="re2"&gt;/&amp;gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/xsl:template&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/xsl:stylesheet&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Note that the XPath expression used for the &lt;code&gt;&amp;lt;xsl:template&amp;gt;&lt;/code&gt; element's &lt;code&gt;match&lt;/code&gt; attribute...&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;//bar:level2&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
...uses the namespace prefix defined in the XSLT document (i.e., &lt;code&gt;bar&lt;/code&gt;), not the namespace prefix defined in the XML document (i.e., &lt;code&gt;foo&lt;/code&gt;).&lt;br /&gt;
&lt;br /&gt;
What this helped me to realize is that it doesn't matter that the XML source document that I'm querying for data with XPath has child elements with default namespaces (i.e, no prefixes) because the prefixes can come from elsewhere. It was then that the .NET API began to make sense. In a way, my .NET is taking the place of the XSLT document, and it then makes sense to define namespaces in .NET. I tried to avoid this originally because it felt like hard-coding what should be soft-coded, but now it makes sense.&lt;br /&gt;
&lt;br /&gt;
In &lt;a href="http://www.bamccaig.com/2010/04/net-xpath-namespaces.html"&gt;my last post&lt;/a&gt;, I mentioned that the &lt;code&gt;XPathNavigator.Evaluate&lt;/code&gt; method accepted an &lt;code&gt;IXmlNamespaceResolver&lt;/code&gt;. While the &lt;code&gt;XPathNavigator&lt;/code&gt; is itself an &lt;code&gt;IXmlNamespaceResolver&lt;/code&gt;, it doesn't make sense that it could resolve our namespace (since the namespace in the given example is a default namespace, with no prefix).&lt;br /&gt;
&lt;br /&gt;
So just like in the XSLT, we're going to define a namespace prefix for the namespace used in the XML. We can do that with an &lt;code&gt;XmlNamespaceManager&lt;/code&gt; (which implements &lt;code&gt;IXmlNamespaceResolver&lt;/code&gt;). Unfortunately, and it escapes me why, we need to pass an &lt;code&gt;XmlNameTable&lt;/code&gt; to the &lt;code&gt;XmlNamespaceManager&lt;/code&gt;'s constructor. Both &lt;code&gt;XmlDocument&lt;/code&gt; and &lt;code&gt;XPathNavigator&lt;/code&gt; have &lt;code&gt;NameTable&lt;/code&gt; properties of that type, but we've already concluded that those won't help us here. &lt;i&gt;Why is it required?&lt;/i&gt; I don't know, but it is. With that information, we can finally resolve our namespace:&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;var nav &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;span class="sy0"&gt;...;&lt;/span&gt; &lt;span class="co1"&gt;// Our XPathNavigator.&lt;/span&gt;
var nsMan &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; XmlNamespaceManager&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;nav&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;NameTable&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp;
nsMan&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;AddNamespace&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;, &lt;span class="st0"&gt;&amp;quot;http://www.bamccaig.com/foo&amp;quot;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp;
&lt;span class="sy0"&gt;...&lt;/span&gt;
&amp;nbsp;
&lt;span class="me1"&gt;var&lt;/span&gt; it &lt;span class="sy0"&gt;=&lt;/span&gt; nav&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;Evaluate&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;&amp;quot;/root/bar:level1/bar:level2/text()&amp;quot;&lt;/span&gt;,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; nsMan&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="kw1"&gt;as&lt;/span&gt; XPathNodeIterator&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
There is one other thing you might be interested in. The data contract API that we're working with (from the post, &lt;a href="http://www.bamccaig.com/2010/04/linq-to-sql-serialization.html"&gt;LINQ to SQL + Serialization&lt;/a&gt;) adds a default namespace to the serialization based on the .NET namespace of the serialized type. This happens to be of the form&lt;a href="#net-xpath-namespaces-conclusion-ref1"&gt;[1]&lt;/a&gt;,...&lt;br /&gt;
&lt;br /&gt;
http://schemas.datacontract.org/2004/07/&lt;i&gt;Clr.Namespace&lt;/i&gt;&lt;/tt&gt;&lt;br /&gt;
&lt;br /&gt;
...where the namespace is &lt;code&gt;Clr.Namespace&lt;/code&gt;. If you don't like that then there are various ways to control it. You can pass a named parameter into the &lt;code&gt;DataContract&lt;/code&gt; attribute, however, since in my case the types (and attributes) are generated for me, I don't have control of what parameters are passed to the attribute. The alternative solution that I preferred (because it was easy) was to set an assembly attribute.&lt;a href="#net-xpath-namespaces-conclusion-ref1"&gt;[1]&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Essentially, I opened up the &lt;tt&gt;Properties/AssemblyInfo.cs&lt;/tt&gt; file that Visual Studio had generated for me automatically and added the following line(s):&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;span class="co1"&gt;// Setting the data contract namespace for the assembly...&lt;/span&gt;
&lt;span class="co1"&gt;// See http://msdn.microsoft.com/en-us/library/ms731045.aspx.&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#91;&lt;/span&gt;assembly&lt;span class="sy0"&gt;:&lt;/span&gt; ContractNamespace&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;MyNamespace&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;ContractNamespace&lt;/span&gt;&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;Uri&lt;/span&gt;,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ClrNamespace &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;span class="st0"&gt;&amp;quot;MyNamespace&amp;quot;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#93;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
&lt;i&gt;Note: You'll also need a &lt;code&gt;using&lt;/code&gt; directive for the &lt;code&gt;System.Runtime.Serialization&lt;/code&gt; namespace, but that should be a given. ;) The latest Visual Studio should resolve that for you if you ask it to politely. Alternatively, you could fully-qualify the attribute.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Note that instead of using a string literal, which I could have done, I elsewhere declared a constant field that could be referenced (both here, and earlier, when creating the &lt;code&gt;XmlNamespaceManager&lt;/code&gt;), which makes it a lot easier to change the namespace URI in the future.&lt;br /&gt;
&lt;br /&gt;
And that's all there is to it. Let me know if this does or doesn't work for you, or if you have any questions, concerns, or advice. :P&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;References&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;a id="net-xpath-namespaces-conclusion-ref1" href="http://msdn.microsoft.com/en-us/library/ms731045.aspx"&gt;http://msdn.microsoft.com/en-us/library/ms731045.aspx&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/cQ-8ssHkTYM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/6533345894388280022/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/05/net-xpath-namespaces-conclusion.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/6533345894388280022?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/6533345894388280022?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/cQ-8ssHkTYM/net-xpath-namespaces-conclusion.html" title=".NET + XPath + Namespaces (Conclusion)" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/05/net-xpath-namespaces-conclusion.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DE8FR387eCp7ImA9WxFQEk0.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-7651191318546068768</id><published>2010-04-30T16:25:00.024-04:00</published><updated>2010-05-07T00:33:36.100-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-07T00:33:36.100-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="xpath namespaces" /><category scheme="http://www.blogger.com/atom/ns#" term=".net4" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="xpath" /><category scheme="http://www.blogger.com/atom/ns#" term="xml" /><category scheme="http://www.blogger.com/atom/ns#" term="code" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>.NET + XPath + Namespaces</title><content type="html">&lt;i&gt;Solved in &lt;a href="http://www.bamccaig.com/2010/05/net-xpath-namespaces-conclusion.html"&gt;a subsequent post&lt;/a&gt;.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
This post is somewhat of a continuation to &lt;a href="http://www.bamccaig.com/2010/04/linq-to-sql-serialization.html"&gt;my previous post&lt;/a&gt;. I managed to get LINQ to SQL entities with custom properties serialized fine, however, the output contains XML namespaces which leads me to my next brick wall.&lt;br /&gt;
&lt;br /&gt;
The application that I'm working on uses XPath expressions to match data within the serialization XML. XPath is namespace aware, which means that if you have a structure like this:&lt;br /&gt;
&lt;br /&gt;
&lt;table class="xml geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;?xml&lt;/span&gt; &lt;span class="re0"&gt;version&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span class="re0"&gt;encoding&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;utf-8&amp;quot;&lt;/span&gt;&lt;span class="re2"&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;root&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;level1&lt;/span&gt; &lt;span class="re0"&gt;xmlns&lt;/span&gt;=&lt;span class="st0"&gt;&amp;quot;http://www.bamccaig.com/foo&amp;quot;&lt;/span&gt;&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;level2&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Useful data!&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/level2&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/level1&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="sc3"&gt;&lt;span class="re1"&gt;&amp;lt;/root&lt;span class="re2"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Then the following XPath expression will &lt;i&gt;&lt;u&gt;NOT&lt;/u&gt;&lt;/i&gt; grab level2's text node:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;/root/level1/level2/text()&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
The reason? &lt;tt&gt;level1&lt;/tt&gt; and &lt;tt&gt;level2&lt;/tt&gt; are in a different namespace than root. I have thus far failed to figure out how to work with this in .NET. In a past experience with this very problem not long ago, I was fortunate enough to be suggested a hack from the kind people on &lt;a href="irc://irc.freenode.net/#xml"&gt;#xml on irc.freenode.net&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;/*[local-name()='root']/*[local-name()='level1']/*[local-name()='level2']/text()&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
The asterisk (&lt;code&gt;*&lt;/code&gt;) says to match all nodes (irrespective of namespaces) and the &lt;code&gt;local-name()&lt;/code&gt; function returns the tag name (i.e., &lt;code&gt;"root"&lt;/code&gt; for &lt;code&gt;&amp;lt;root /&amp;gt;&lt;/code&gt;). This works, or at least it did in the past, however, as you can imagine it's the Wrong Way(tm) and it's not particularly pretty either. The application I'm working on has a series of XPath expressions to maintain and I don't care to let this hack infect those.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, my attempts to solve this problem have so far failed. The &lt;code&gt;XPathNavigator.Evaluate&lt;/code&gt; method accepts an &lt;code&gt;IXmlNamespaceResolver&lt;/code&gt;, presumably for this very situation, but I'm unable to coerce it into resolving the namespace. &lt;tt&gt;:(&lt;/tt&gt;&lt;br /&gt;
&lt;br /&gt;
The most frustrating part of this is that the XML that it's [supposed to be] "navigating" is from the serialization process documented in &lt;a href="http://www.bamccaig.com/2010/04/linq-to-sql-serialization.html"&gt;my previous post&lt;/a&gt;. The XML was loaded with &lt;code&gt;XmlDocument&lt;/code&gt; and the &lt;code&gt;XmlNavigator&lt;/code&gt; was created with the &lt;code&gt;XmlDocument&lt;/code&gt;'s &lt;code&gt;CreateNavigator&lt;/code&gt; method. The &lt;code&gt;XmlDocument&lt;/code&gt; and &lt;code&gt;XPathNavigator&lt;/code&gt; both have a &lt;code&gt;NameTable&lt;/code&gt; property, which presumably maps out the namespaces within (it can be used to source an &lt;code&gt;XmlNamespaceManager&lt;/code&gt; right in the constructor!), but somehow the &lt;code&gt;XPathNavigator&lt;/code&gt; still fails to match to XPath expressions I'm feeding it. And get this: &lt;code&gt;XPathNavigator&lt;/code&gt; implements the &lt;code&gt;IXmlNamespaceResolver&lt;/code&gt; interface (and no, passing the navigator to the navigator does not work, I tried)!&lt;br /&gt;
&lt;br /&gt;
I'm obviously missing something, but it's going to be nightmare to sort through all of the documentation, especially when I don't even know where to start...&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Why, Microsoft?! Why?&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Solved in &lt;a href="http://www.bamccaig.com/2010/05/net-xpath-namespaces-conclusion.html"&gt;a subsequent post&lt;/a&gt;.&lt;/i&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/xy7OT-LaGXA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/7651191318546068768/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/04/net-xpath-namespaces.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/7651191318546068768?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/7651191318546068768?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/xy7OT-LaGXA/net-xpath-namespaces.html" title=".NET + XPath + Namespaces" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/04/net-xpath-namespaces.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEMRnw_fyp7ImA9WxFQEk0.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-9019313919755070295</id><published>2010-04-30T11:08:00.007-04:00</published><updated>2010-05-07T00:31:27.247-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-05-07T00:31:27.247-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net4" /><category scheme="http://www.blogger.com/atom/ns#" term="serialization" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="asp.net" /><category scheme="http://www.blogger.com/atom/ns#" term="xml" /><category scheme="http://www.blogger.com/atom/ns#" term="code" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="c#" /><title>LINQ to SQL + Serialization</title><content type="html">So here I am for my first /&lt;i&gt;real&lt;/i&gt;/ post. I recently faced off with serializing LINQ to SQL generated types with custom properties. "I" have done this before using &lt;code&gt;WebMethod&lt;/code&gt;s by simply returning them:&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;span class="br0"&gt;&amp;#91;&lt;/span&gt;WebMethod&lt;span class="br0"&gt;&amp;#93;&lt;/span&gt;
&lt;span class="kw1"&gt;public&lt;/span&gt; EntityA GetEntityA&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw4"&gt;int&lt;/span&gt; id&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;using&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;var db &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; MyDataContext&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="kw1"&gt;return&lt;/span&gt; db&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;EntityAs&lt;/span&gt;&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;SingleOrDefault&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; o &lt;span class="sy0"&gt;=&amp;gt;&lt;/span&gt; o&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;id&lt;/span&gt; &lt;span class="sy0"&gt;==&lt;/span&gt; id&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;Custom properties serialize fine that way and show up on the client-side (in JavaScript). The serialization is all handled for me by ASP.NET though. The way I originally was doing serialization is like this:&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;var sb &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; StringBuilder&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
var xs &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; XmlSerializerFactory&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;CreateSerializer&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; entityToBeSerialized&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;GetType&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw1"&gt;using&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;var xw &lt;span class="sy0"&gt;=&lt;/span&gt; XmlWriter&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;Create&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;sb&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; xs&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;Serialize&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;xw, entityToBeSerialized&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
var serialized &lt;span class="sy0"&gt;=&lt;/span&gt; sb&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;ToString&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
This was working sufficiently for what I was using it for because the entities had no custom properties that were required in the serialization. Now I have entities that /&lt;i&gt;do&lt;/i&gt;/ require their custom properties to be serialized. It took a little bit of Googling to find the solution (who has time to R all TFMs?). I found a solution that works (on MSDN, no less).&lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://msdn.microsoft.com/en-us/library/bb546184.aspx"&gt;http://msdn.microsoft.com/en-us/library/bb546184.aspx&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
In short, you enable serialization for the LINQ to SQL model by opening it (the .dbml) in Visual Studio and setting the Serialization Mode (the only options I see are None and Unidirectional; guess which one to set it to). This tells LINQ to SQL to markup the generated types with serialization attributes. Next, I needed to markup my own custom properties with these same attributes.&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;&lt;span class="br0"&gt;&amp;#91;&lt;/span&gt;&lt;span class="me1"&gt;DataMember&lt;/span&gt;&lt;span class="br0"&gt;&amp;#93;&lt;/span&gt;
&lt;span class="kw1"&gt;public&lt;/span&gt; &lt;span class="kw4"&gt;int&lt;/span&gt; SomeCustomProperty
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; get
&amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="sy0"&gt;...&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp; &amp;nbsp; set
&amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class="sy0"&gt;...&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
Finally, I had to change the way I was serializing (my original way isn't aware of, or at least doesn't seem to care about, the attributes we've applied).&lt;br /&gt;
&lt;br /&gt;
&lt;table class="csharp geshi"&gt;&lt;tbody&gt;
&lt;tr class="li1"&gt;&lt;td class="ln"&gt;&lt;pre class="de1"&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class="de1"&gt;&lt;pre class="de1"&gt;var dcs &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; DataContractSerializer&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; entityToBeSerialized&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;GetType&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
var sb &lt;span class="sy0"&gt;=&lt;/span&gt; &lt;a href="http://www.google.com/search?q=new+msdn.microsoft.com"&gt;&lt;span class="kw3"&gt;new&lt;/span&gt;&lt;/a&gt; StringBuilder&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw1"&gt;using&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;var xw &lt;span class="sy0"&gt;=&lt;/span&gt; XmlWriter&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;Create&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;sb&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&amp;nbsp; &amp;nbsp; dcs&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;WriteObject&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;xw, entityToBeSerialized&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
var serialized &lt;span class="sy0"&gt;=&lt;/span&gt; sb&lt;span class="sy0"&gt;.&lt;/span&gt;&lt;span class="me1"&gt;ToString&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="sy0"&gt;;&lt;/span&gt;
&amp;nbsp;
&amp;nbsp;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;
This is what is currently working for me, at least. ;) Best practices and "'Ur Doin' It Wrong, n00b!" are welcomed. :P&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;** EDIT **&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
Note that the output is slightly different from the output of the original serialization classes, so code that you've written based on the old XML structure may need to be modified. I now have to remap a set of XPath expressions... &lt;i&gt;Sigh.&lt;/i&gt; In truth, it's resulting in slightly better code though, IMHO.&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/vVtqK4ZO2Zw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/9019313919755070295/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/04/linq-to-sql-serialization.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/9019313919755070295?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/9019313919755070295?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/vVtqK4ZO2Zw/linq-to-sql-serialization.html" title="LINQ to SQL + Serialization" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/04/linq-to-sql-serialization.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkUMSH4zfyp7ImA9WxFRFUo.&quot;"><id>tag:blogger.com,1999:blog-4017200020892862804.post-5961472872367449470</id><published>2010-04-29T13:24:00.001-04:00</published><updated>2010-04-29T15:44:49.087-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-29T15:44:49.087-04:00</app:edited><title>Introduction</title><content type="html">This is my first blog post evar (well, outside of stupid social networking sites)! About a week ago I was trying to solve a code related problem at j0rb and realized that I could be writing blog posts about the problems and solutions I encounter. It seems more and more I'm finding the answers that I'm looking for in blogs. Often, however, I stumble across poorly written blogs that only partially answer questions or contain errors. I generally enjoy writing and try to be meticulous so hopefully I'll be able to bring some value to the "blogosphere" (I use that term loosely).&lt;br /&gt;
&lt;br /&gt;
To introduce myself, I'm a computer programmer from Ontario, Canada. I live very near to the Michigan, USA border. I predominantly work with Microsoft-based Web technologies at j0rb, but I prefer free/libre/open source and text-based user interfaces! In my spare time, I play lots of video games and experiment with various software development concepts (platforms, languages, APIs, etc.).&lt;br /&gt;
&lt;br /&gt;
I have self-diagnosed obsessive-compulsive personality disorder; which is just how I like to say that I try to be a perfectionist. ;) I'm obviously not infallible so I won't claim to never make mistakes. "Grammar Nazis" are welcome here. If you spot a mistake in my spelling or grammar I would appreciate if you'd point it out (try not to go overboard though). Also, I tend to edit posts a lot on forums (to correct mistakes) so expect the same behavior here.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;:wq&lt;/code&gt;&lt;img src="http://feeds.feedburner.com/~r/BrandonMccaigsBlog/~4/BUeI12eqMBc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bamccaig.com/feeds/5961472872367449470/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.bamccaig.com/2010/04/introduction.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/5961472872367449470?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4017200020892862804/posts/default/5961472872367449470?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/BrandonMccaigsBlog/~3/BUeI12eqMBc/introduction.html" title="Introduction" /><author><name>Brandon McCaig</name><uri>http://www.blogger.com/profile/16999915870838539143</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="24" src="http://4.bp.blogspot.com/-oHUJLq9awOg/TkC1W5TqceI/AAAAAAAAAEk/25mADShkMHU/s220/IMGA0015.JPG" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bamccaig.com/2010/04/introduction.html</feedburner:origLink></entry></feed>
