<?xml version="1.0" encoding="utf-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://url.org/dc/elements/1.1/" xml:lang="en">
   <title>Kyle</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/" />
   <link rel="self" type="application/atom+xml" href="http://www.memoryhole.net/kyle/atom.xml" />
   <id>tag:www.memoryhole.net,2009:/kyle/6</id>
   <modified>2009-11-13T23:05:55Z</modified>
   <tagline>High catecholamine levels</tagline>
   <generator uri="http://www.movabletype.org/" version="3.34">Movable Type 3.34</generator>
    <copyright>Copyright (c) 2009, kyle</copyright>

<entry>
   <title>qsort_r</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2009/11/qsort_r.html" />
   <id>tag:www.memoryhole.net,2009:/kyle/6.1382</id>
   <created>2009-11-13T22:41:22Z</created>
   <issued>2009-11-13T22:41:22Z</issued>
   <modified>2009-11-13T23:05:55Z</modified>
   
   <summary type="text/plain">Once upon a time, in 2002, the BSD folks had this genius plan: make the standard C qsort() function safe to use in reentrant code by creating qsort_r() and adding an argument (a pointer to pass to the comparison function)....</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Commentary" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>Once upon a time, in 2002, the <span class="caps">BSD </span>folks had this genius plan: make the standard C qsort() function safe to use in reentrant code by creating qsort_r() and adding an argument (a pointer to pass to the comparison function). So <a href="http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdlib/qsort.c?annotate=1.12">they did</a>, and it was good.</p>

<p>Five years later, in 2007, the <span class="caps">GNU </span>libc folks said to themselves &#8220;dang, those <span class="caps">BSD </span>guys are <strong>smart</strong>, I wish we had qsort_r()&#8221;. Then some idiot said: <span class="caps">WAIT</span>! We cannot simply use the same prototype as the <span class="caps">BSD </span>folks; they use an evil license! We can&#8217;t put that into <span class="caps">GPL&#8217;</span>d code! So the <span class="caps">GNU </span>libc folks solved the problem by <a href="http://sources.redhat.com/cgi-bin/cvsweb.cgi/libc/include/stdlib.h?rev=1.42&amp;content-type=text%2Fx-cvsweb-markup&amp;cvsroot=glibc">reordering the arguments</a>.</p>

<p>And now we have two, incompatible, widely published versions of qsort_r(), which both do the exact same thing: crash horribly if you use the wrong argument order.</p>

<p><em>&lt;sigh&gt;</em></p>

<p>Okay, here&#8217;s an alternate history:</p>

<p>&#8230; Then some lazy idiot said: <span class="caps">WAIT</span>! The existing qsort_r() is a poor design that requires a second implementation of qsort()! If we throw out compatibility with existing qsort_r() code, we can implement qsort() as a call to qsort_r() and no one will ever know!</p>

<p><em>&lt;sigh&gt;</em></p>

<p>Either way, we all lose.</p>

<p>(I have no argument with the alternate history point&#8230; but why&#8217;d you have to call it the exact same thing??? Call it qsort_gnu() or something! Make it easy to detect the difference!)</p>]]>
      
   </content>
</entry>
<entry>
   <title>More Compiler Complaints: PGI Edition</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2009/06/more_compiler_complaints_pgi_e.html" />
   <id>tag:www.memoryhole.net,2009:/kyle/6.1376</id>
   <created>2009-06-10T21:39:43Z</created>
   <issued>2009-06-10T21:39:43Z</issued>
   <modified>2009-06-11T03:08:04Z</modified>
   
   <summary type="text/plain">Continuing my series of pointless complaining about compiler behavior (see here and here for the previous entries), I recently downloaded a trial version of PGI&amp;#8217;s compiler to put in my Linux virtual machine to see how that does compiling qthreads....</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Commentary" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>Continuing my series of pointless complaining about compiler behavior (see <a href="http://www.memoryhole.net/kyle/2008/08/more_compiler_complaints_sparc.html">here</a> and <a href="http://www.memoryhole.net/kyle/2008/01/apples_compiler_idiocy.html">here</a> for the previous entries), I recently downloaded a trial version of <span class="caps">PGI&#8217;</span>s compiler to put in my Linux virtual machine to see how that does compiling qthreads. There were a few minor things that it choked on that I could correct pretty easily, and some real bizarre behavior that seems completely broken to me.</p>

<h3>Subtle Bugs in <em>My</em> Code</h3>

<p>Let&#8217;s start with the minor mistakes it found in my code that other compilers hadn&#8217;t complained about:</p>

<pre><code language="cc">static inline uint64_t qthread_incr64(
           volatile uint64_t *operand, const int incr)
{
  union {
    uint64_t i;
    struct {
      uint32_t l, h;
    } s;
  } oldval, newval;
  register char test;
  do {
    oldval.i = *operand;
    newval.i = oldval.i + incr;
    __asm__ __volatile__ (&quot;lock; cmpxchg8b %1\n\t setne %0&quot;
        :&quot;=r&quot;(test)
        :&quot;m&quot;(*operand),
         &quot;a&quot;(oldval.s.l),
         &quot;d&quot;(oldval.s.h),
         &quot;b&quot;(newval.s.l),
         &quot;c&quot;(newval.s.h)
        :&quot;memory&quot;);
  } while (test);
  return oldval.i;
}</code></pre>

<p>Seems fairly straightforward, right? Works fine on most compilers, but the <span class="caps">PGI </span>compiler complains that &#8220;%sli&#8221; is an invalid register. Really obvious error, right? Right? (I don&#8217;t really know what the %sli register is for either). Turns out that because <code>setne</code> requires a byte-sized register, I need to tell the compiler that I want a register that can be byte-sized. In other words, that <code>&quot;=r&quot;</code> needs to become <code>&quot;=q&quot;</code>. Fair enough. It&#8217;s a confusing error, and thus annoying, but I am technically wrong (or at least I&#8217;m providing an incomplete description of my requirements) here so I concede the ground to <span class="caps">PGI.</span></p>

<h3>Unnecessary Pedantry</h3>

<p>And then there are places where <span class="caps">PGI </span>is simply a bit more pedantic than it really needs to be. For example, it generates an error when you implicitly cast a <code>volatile struct foo *</code> into a <code>void *</code> when calling into a function. Okay, yes, the pointers are different, but&#8230; most compilers allow you to implicitly convert just about any pointer type into a <code>void *</code> without kvetching, because you aren&#8217;t allowed to <strong>dereference</strong> a void pointer (unless you cast again, and if you&#8217;re casting, all bets are off anyway), thus it&#8217;s a safe bet that you want to work on the <strong>pointer</strong> rather than what it points to. Yes, technically <span class="caps">PGI </span>has made a valid observation, but I disagree that their observation rises to the level of &#8220;warning-worthy&#8221; (I have no argument if they demote it to the sort of thing that shows up with the <code>-Minform=inform</code> flag).</p>

<h3>Flat-out Broken</h3>

<p>But there are other places where <span class="caps">PGI </span>is simply wrong/broken. For example, if I have (and use) a <code>#define</code> like this:</p>

<pre><code language="cc">#define PARALLEL_FUNC(initials, type, shorttype, category) \
type qt_##shorttype##_##category (type *array, size_t length, int checkfeb) \
{ \
  struct qt##initials arg = { array, checkfeb }; \
  type ret; \
  qt_realfunc(0, length, sizeof(type), &amp;ret, \
    qt##initials##_worker, \
    &amp;arg, qt##initials##_acc, 0); \
  return ret; \
}
PARALLEL_FUNC(uis, aligned_t, uint, sum);</code></pre>

<p><span class="caps">PGI </span>will die! Specifically, it complains that <code>struct qtuisarg</code> does not exist, and that an identifier is missing. In other words, it blows away the whitespace following initials so that this line:</p>

<pre><code language="cc">struct qt##initials arg = { array, checkfeb }; \</code></pre>

<p>is interpreted as if it looked like this:</p>

<pre><code language="cc">struct qt##initials##arg = { array, checkfeb }; \</code></pre>

<p>But at least that&#8217;s easy to work around: rename the struct so that it has a <code>_s</code> at the end! Apparently <span class="caps">PGI </span>is okay with this:</p>

<pre><code language="cc">struct qt##initials##_s arg = { array, checkfeb }; \</code></pre>

<p>::sigh:: Stupid, stupid compiler. At least it can be worked around.</p>

<h3>Thwarting The Debugger</h3>

<p><span class="caps">PGI </span>also bad at handling static inline functions in headers. How bad? Well, first of all, the <span class="caps">DWARF2 </span>symbols it generates (the default) are incorrect. It gets the line-numbers right but the file name wrong. For example, if I have an inline function in <code>qthread_atomics.h</code> on line 75, and include that header in <code>qt_mpool.c</code>, and then use that function on line 302, the <span class="caps">DWARF2 </span>symbols generated will claim that the function is on line 75 of <code>qt_mpool.c</code> (which isn&#8217;t even correct if we assume that it&#8217;s generating <span class="caps">DWARF2 </span>symbols based on the pre-processed source! and besides which, all the other line numbers are from non-pre-processed source). You <span class="caps">CAN </span>tell it to generate <span class="caps">DWARF1 </span>or <span class="caps">DWARF3 </span>symbols, but then it simply leaves out the line numbers and file names completely. Handy, no?</p>

<h3>Everyone Else is Doing It&#8230;</h3>

<p>Here&#8217;s another bug in <span class="caps">PGI&#8230; </span>though I suppose it&#8217;s my fault for outsmarting myself. So, once upon a time, I (think I) found that some compilers require assembly memory references to be within parentheses, while others require them to be within brackets. Unfortunately I didn&#8217;t write down which ones did what, so I don&#8217;t remember if I was merely being over-cautious in my code, or if it really was a compatibility problem. Nevertheless, I frequently do things like this:</p>

<pre><code language="cc">atomic_incr(volatile uint32_t *op, const int incr) {
  uint32_t retval = incr;
  __asm__ __volatile__ (&quot;lock; xaddl %0, %1&quot;
    :&quot;=r&quot;(retval)
    :&quot;m&quot;(*op), &quot;0&quot;(retval)
    :&quot;memory&quot;);
  return retval;
}</code></pre>

<p>Note that weird <code>&quot;m&quot;(*op)</code> construction? That was my way of ensuring that the right memory reference syntax was automatically used, no matter what the compiler thought it was. So, what does <span class="caps">PGI </span>do in this instance? It actually performs the dereference! In other words, it behaves as if I had written:</p>

<pre><code language="cc">atomic_incr(volatile uint32_t *op, const int incr) {
  uint32_t retval = incr;
  __asm__ __volatile__ (&quot;lock; xaddl %0, (%1)&quot;
    :&quot;=r&quot;(retval)
    :&quot;r&quot;(*op), &quot;0&quot;(retval)
    :&quot;memory&quot;);
  return retval;
}</code></pre>

<p>when what I really wanted was:</p>

<pre><code language="cc">atomic_incr(volatile uint32_t *op, const int incr) {
  uint32_t retval = incr;
  __asm__ __volatile__ (&quot;lock; xaddl %0, (%1)&quot;
    :&quot;=r&quot;(retval)
    :&quot;r&quot;(op), &quot;0&quot;(retval)
    :&quot;memory&quot;);
  return retval;
}</code></pre>

<p>See the difference? <sigh> Again, it&#8217;s not hard to fix so that <span class="caps">PGI </span>does the right thing. And maybe I was being too clever in the first place. But dagnabit, my trick should work! And, more pointedly, it <span class="caps">DOES </span>work on other compilers (gcc and icc at the bare minimum, and I&#8217;ve tested similar things with xlc).</p>]]>
      
   </content>
</entry>
<entry>
   <title>Muslim Demographics</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2009/05/muslim_demographics.html" />
   <id>tag:www.memoryhole.net,2009:/kyle/6.1374</id>
   <created>2009-05-05T17:16:28Z</created>
   <issued>2009-05-05T17:16:28Z</issued>
   <modified>2009-05-27T21:16:57Z</modified>
   
   <summary type="text/plain">I recently got sent a link to the Muslim Demographics video on YouTube. It&amp;#8217;s pretty alarmist, so I composed an email response. Since it might be interesting to have available to Google, here&amp;#8217;s my thoughts, as an antidote to the...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Commentary" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>I recently got sent a link to the <a href="http://www.youtube.com/watch?v=6-3X5hIFXYU">Muslim Demographics</a> video on YouTube. It&#8217;s pretty alarmist, so I composed an email response. Since it might be interesting to have available to Google, here&#8217;s my thoughts, as an antidote to the panic the video is peddling.</p>

<p>Note that <a href="http://www.snopes.com/politics/religion/demographics.asp">snopes.com</a> has their own page discussing this video. They  don&#8217;t address the accuracy of the facts presented, but its an interesting read nevertheless.</p>

<p><hr /></p>

<p>It is probably true that there is an aspect of evolution to religion. If we think of religion as a gene, the dominant religion will be defined (over the long run) by the extent to which it benefits or promotes reproduction, just as any piece of <span class="caps">DNA </span>does. Of course, given evangelism, it may be more apt to think of religion as similar to a virus that gets passed from person to person, but I doubt that&#8217;d be a popular viewpoint. In any case, of course, religion <strong>isn&#8217;t</strong> a gene, because we&#8217;d have to assume that people stick with the religion they&#8217;re born with. Christianity started with just 12 Christians, after all, and every last one of them was male (and thus couldn&#8217;t pass on any genes without help from an originally non-Christian woman).</p>

<p>That said, I&#8217;m skeptical of this video&#8217;s claims. For one thing, practically no sources are cited (2.11 is the bare minimum to maintain a society? Says who? How do we know?). For another, it&#8217;s interesting to note what gets left out. For example, the video says the Muslim population skyrocketed from 82,000 in the UK to several million, but according to the <span class="caps">CIA</span> World Factbook, Muslims are a whopping <a href="https://www.cia.gov/library/publications/the-world-factbook/geos/uk.html#People">2.7% of the UK population</a>. In the Netherlands, the <span class="caps">CIA</span> World FactBook says that Muslims are a crushing <a href="https://www.cia.gov/library/publications/the-world-factbook/geos/nl.html#People">5.8% of the population</a>. Not only that, but it says that the Netherlands have a fertility rate of 1.66. Woo! Scary!</p>

<p>Now that I come to think of it&#8230; let me look this up.</p>

<table style="border-collapse:collapse" cellspacing="0"><tr><th style="border-right:1px solid black;border-bottom:1px solid black;padding:2px">Country</th><th style="border-right:1px solid black;border-bottom:1px solid black;padding:2px">Video Claimed Fertility Rate</th><th style="border-bottom:1px solid black;padding:2px"><span class="caps">CIA</span> FactBook Fertility Rate</th></tr><tr><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">France</td><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">1.8</td><td style="border-bottom:1px dotted grey;padding:2px"><a href="https://www.cia.gov/library/publications/the-world-factbook/geos/fr.html#People">1.98</a></td></tr><tr><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">England</td><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">1.6</td><td style="border-bottom:1px dotted grey;padding:2px"><a href="https://www.cia.gov/library/publications/the-world-factbook/geos/uk.html#People">1.66</a></td></tr><tr><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">Greece</td><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">1.3</td><td style="border-bottom:1px dotted grey;padding:2px"><a href="https://www.cia.gov/library/publications/the-world-factbook/geos/gr.html#People">1.37</a></td></tr><tr><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">Germany</td><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">1.3</td><td style="border-bottom:1px dotted grey;padding:2px"><a href="https://www.cia.gov/library/publications/the-world-factbook/geos/gm.html#People">1.41</a></td></tr><tr><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">Italy</td><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">1.2</td><td style="border-bottom:1px dotted grey;padding:2px"><a href="https://www.cia.gov/library/publications/the-world-factbook/geos/it.html#People">1.31</a></td></tr><tr><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">Spain</td><td style="border-right:1px solid black;border-bottom:1px dotted grey;padding:2px">1.1</td><td style="border-bottom:1px dotted grey;padding:2px"><a href="https://www.cia.gov/library/publications/the-world-factbook/geos/sp.html#People">1.31</a></td></tr><tr><td style="border-right:1px solid black;padding:2px">EU</td><td style="border-right:1px solid black;padding:2px">1.38</td><td style="padding:2px"><a href="https://www.cia.gov/library/publications/the-world-factbook/geos/ee.html#People">1.51</a></td></tr></table>

<p>Yikes - they didn&#8217;t get a single one correct! The closest was England, and even there they rounded in the wrong direction.</p>

<p>So should we really believe that French Muslims have a birth rate of 8.1? Think about that, <span class="caps">EIGHT </span>kids on average, which means that for every childless Muslim woman, there&#8217;s another out there with <span class="caps">SIXTEEN KIDS.</span> That sounds totally plausible, right?</p>

<p>The <a href="http://www.prb.org/Articles/2008/muslimsineurope.aspx">Population Reference Bureau</a> (who I&#8217;ve never heard of, but they were linked to by <a href="http://geography.about.com/b/2008/03/04/muslim-fertility-rate-in-europe.htm">about.com</a>) says that in Austria, Muslim women had a fertility rate of 3.1 in 1981, but that by 2001 the rate was a mere 2.3 (apparently they didn&#8217;t get the memo from their French kindred). That reflects the falling fertility rates in Muslim countries the immigrants came from. For example, in Turkey the fertility rate dropped from 3.3 in 1985 to 2.2 in 2003. According to the <span class="caps">CIA</span> World FactBook, Turkey&#8217;s current fertility rate is <a href="https://www.cia.gov/library/publications/the-world-factbook/geos/tu.html#People">2.21</a>. In Morocco it fell from 4.5 to 2.5 in the same time period (CIA says <a href="https://www.cia.gov/library/publications/the-world-factbook/geos/mo.html#People">2.51</a>). And get this: in Iran, it fell from 5.6 to 2.1 in 2003. The <span class="caps">CIA</span> World FactBook currently pegs the Iranian fertility rate at a paltry <a href="https://www.cia.gov/library/publications/the-world-factbook/geos/ir.html#People">1.71</a>!</p>

<p>1.71! 1.71! The Iranian culture cannot survive! The US fertility rate is <a href="https://www.cia.gov/library/publications/the-world-factbook/geos/us.html#People">2.05</a>! We will crush them with our progeny! <span class="caps">MUAHAHAHAHA</span>!</p>

<p>Oh, wait, does that not serve the purpose of getting people riled up?</p>

<p>I&#8217;m thinking of a word&#8230; <em>fearmongering</em>! That&#8217;s the one.</p>

<p>It reminds me a lot of grade school. I remember playing with a bunch of computer &#8220;simulations&#8221; that showed that the world population was exploding and that we&#8217;d run out of food by the turn of the millenium. The reason they were wrong is that they made assumptions without realizing it. For example, they assumed that food production would stay constant, and that fertility rates would stay constant. Surprise! They didn&#8217;t.</p>

<p>And <span class="caps">THAT </span>reminds me of another quote:</p>

<blockquote><p>Scientists have shown that the moon is moving away at a tiny, although measurable distance from the earth every year. If you do the math, you can calculate that 65 million years ago, the moon was orbiting at a distance of about 35 feet from the earth&#8217;s surface. This would explain the death of the dinosaurs &#8230; the tallest ones, anyway.</p></blockquote>

<p>Don&#8217;t believe everything you see on YouTube. ;)</p>]]>
      
   </content>
</entry>
<entry>
   <title>SlingLink Killed My Network!</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2009/04/slinglink_killed_my_network.html" />
   <id>tag:www.memoryhole.net,2009:/kyle/6.1369</id>
   <created>2009-04-27T23:55:24Z</created>
   <issued>2009-04-27T23:55:24Z</issued>
   <modified>2009-05-27T21:19:14Z</modified>
   
   <summary type="text/plain">This isn&amp;#8217;t the most accurate title, but&amp;#8230; I&amp;#8217;ve got a SlingLink Turbo that I use for connecting my Macs upstairs to my cable modem downstairs. I went with a network-over-powerline option, because I&amp;#8217;ve been having all kinds of intermittent interference...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="People Suck" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>This isn&#8217;t the most accurate title, but&#8230;</p>

<p>I&#8217;ve got a <a href="http://www.slingmedia.com/go/slt">SlingLink Turbo</a> that I use for connecting my Macs upstairs to my cable modem downstairs. I went with a network-over-powerline option, because I&#8217;ve been having all kinds of intermittent interference problems with my wireless connectivity. So, rather than running an extra-long patch cable up the stairs and taping it down to the carpet, I went the SlingLink route. It seems to be designed specifically for SlingBox applications, but it forwards plain ol&#8217; ethernet signals, and it&#8217;s about $40 cheaper than the NetGear equivalents. Huzzah for getting a bargain!</p>

<p>First impression: fabulous! I went along happily for <strong>weeks</strong>, enjoying my newfound reliable network connection. Then I tried downloading the latest Ubuntu <span class="caps">ISO </span>images via BitTorrent, and within five to ten minutes, the internet connection went offline. If I went downstairs and turned the cable modem off-and-on again, the internet would come back. For five to ten minutes. Then it&#8217;d go down again.</p>

<p>Surely, I say to myself, that&#8217;s a cable-modem problem, right?</p>

<p>I had to have the tech guys from Time Warner&#8217;s Cable group come out (twice!) before I finally figured out that it wasn&#8217;t their fault (the first time they said they replaced the splitter, and presto, the network was fine! I didn&#8217;t go after the <span class="caps">ISO </span>again for a while so&#8230;). Turns out I didn&#8217;t need to restart the cable modem, all I had to do was restart the SlingLink node and I&#8217;d get another five to ten minutes out of it. But it <span class="caps">ONLY </span>happens when BitTorrent is running; otherwise, the network connection is rock solid!</p>

<p>Weird, no?</p>

<p>So, to experiment, I tried limiting the BitTorrent connections: no dice. Then I tried limiting the BitTorrent <strong>bandwidth</strong> and all of a sudden the network would stay up. Somewhere between 100Kb/s and 150Kb/s is the cutoff. Something about BitTorrent&#8217;s bandwidth seems to either confuse the SlingLink node OR triggers some sort of antiviral cutoff in the SlingLink hardware (either way is annoying). For the record, it&#8217;s not a pure bandwidth issue: I can transfer files over the SlingLink network at speeds of over 400Kb/s. As soon as I introduce BitTorrent, though&#8230; down she goes.</p>

<p>Maybe it&#8217;s a packet-size issue. Maybe it&#8217;s a connection-tracking issue. I have no idea. But at least now I know that SlingLink has its limitations. And now, so do you.</p>]]>
      
   </content>
</entry>
<entry>
   <title>Congress&apos;s CRA Caused the Crash?</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/10/congresss_cra_caused_the_crash.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1347</id>
   <created>2008-10-07T05:46:57Z</created>
   <issued>2008-10-07T05:46:57Z</issued>
   <modified>2008-10-08T15:40:27Z</modified>
   
   <summary type="text/plain">It&amp;#8217;s recently been suggested to me that the true cause of the subprime mess is that Congress forced banks to loan to poor people (also known as &amp;#8220;people who cannot afford to repay the mortgage&amp;#8221;), perhaps under the guise of...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Politics" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>It&#8217;s recently been suggested to me that the true cause of the subprime mess is that Congress forced banks to loan to poor people (also known as &#8220;people who cannot afford to repay the mortgage&#8221;), perhaps under the guise of fairness or avoiding racism or classism or something like that.</p>

<p>There would appear to be some truth to this idea, but not like you&#8217;d think (surprise! it&#8217;s a complicated subject). Congress has indeed encouraged banks to loan money to poor folks, starting with the <a href="http://www.federalreserve.gov/dcca/cra/">Community Reinvestment Act</a> (CRA), enacted in 1977, under Carter&#8217;s administration. The original idea was to ban a practice known as &#8220;redlining&#8221;, which essentially was a practice of defining financial no-fly-zones. Anyone who wanted a loan who happened to live within the redlined area wouldn&#8217;t be considered by any bank. This resulted in de-facto slums, among other things. Removing the problem is a touchy one, and you&#8217;d think would require very specific rules. But rather than be very specific, the law as enacted used very vague language (apparently to pacify critics of the bill who complained that it would be a train-wreck if it required banks to lend to people who couldn&#8217;t afford it). According to <a href="http://en.wikipedia.org/wiki/Community_Reinvestment_Act">Wikipedia</a>:</p>

<blockquote><p>Congress included little prescriptive detail and simply directs the banking regulatory agencies to ensure that banks and savings associations serve the credit needs of their local communities in a safe and sound manner.</p></blockquote>

<p>With the <a href="http://thomas.loc.gov/cgi-bin/query/z?c101:H.R.1278:">Financial Institutions Reform Recovery and Enforcement Act</a>, in 1984, the credit ratings that banks used to evaluate people&#8217;s creditworthiness was made public (using a four-tier  system) and was required to contain supporting details, so that their decisions could be audited if necessary. The <a href="http://assembler.law.cornell.edu/usc-cgi/get_external.cgi?type=pubL&amp;target=102-550">Federal Housing Enterprises Financial Safety and Soundness Act of 1992</a> supposedly created the Housing and Urban Development (HUD) organization, and according to <a href="http://www.fanniemae.com/aboutfm/charter.jhtml">Fannie Mae&#8217;s website</a>:</p>

<blockquote><p> The Federal Housing Enterprises Financial Safety and Soundness Act (&#8220;FHEFSSA&#8221;) of 1992 modernized the regulatory oversight of Fannie Mae and Freddie Mac. It created the Office of Federal Housing Enterprise Oversight (&#8220;OFHEO&#8221;) as a new regulatory office within <span class="caps">HUD </span>with the responsibility to &#8220;ensure that Fannie Mae and Freddie Mac are adequately capitalized and operating safely.&#8221; OFHEO is funded by assessments on Fannie Mae and Freddie Mac and is authorized to act without <span class="caps">HUD </span>oversight on a range of regulatory issues enumerated in the statute. <span class="caps">FHEFSSA </span>established risk-based and minimum capital standards for Fannie Mae and Freddie Mac. <strong>And, it established <span class="caps">HUD</span>-imposed housing goals for financing of affordable housing and housing in central cities and other rural and underserved areas.</strong></p></blockquote>

<p>(emphasis mine) I couldn&#8217;t find all the specifics of those housing goals in a quick look through the text of the Act, but it&#8217;s long and complicated (maybe someone else will spend the time). Anyway, in an effort to finance these things effectively, Fannie and Freddie created and sold mortgage-backed securities. <span class="caps">THIS </span>is where the door was built (aka: the <span class="caps">HUD</span>), but the door isn&#8217;t yet &#8220;open.&#8221; More on this in a moment.</p>

<p>Because of the <span class="caps">CRA </span>requirements of transparency (imposed in 1984), banks were having trouble selling themselves, especially to out-of-state buyers. Not that the sales wouldn&#8217;t happen, but the sales would be slowed by <span class="caps">CRA</span>-based protests (I&#8217;m not sure what exactly they were protesting). So, Congress passed, in 1994, the Riegle-Neal Interstate Banking and Branching Efficiency Act, which removed the requirements that the banks had to publish the reasons why they made lending decisions under many circumstances, in order to facilitate sale of the banks. The publishing requirements were still in place if you weren&#8217;t selling your bank, though.</p>

<p>In 1995, Bill Clinton&#8217;s <span class="caps">HUD </span>made the policy decision to allow Fannie and Freddie to purchase subprime mortgage securities. Supposedly, <span class="caps">HUD </span>expected that Freddie and Fannie would impose their usual high standards upon subprime lenders. They did&#8230; mostly (hey, it&#8217;s still a subprime loan, after all), for a while, anyway. But this was still a bad idea (more on this in a moment).</p>

<p>In 1999, Congress passed the <a href="http://frwebgate.access.gpo.gov/cgi-bin/getdoc.cgi?dbname=106_cong_public_laws&amp;docid=f%3Apubl102.106">Gramm-Leach-Bliley Act</a>, which repealed the Depression-era Glass-Steagall Act&#8217;s prohibition against banks acting as any combination of investment bank, commercial bank, and/or insurance company. Having more than one of those under one roof was a bad idea in 1929 because it creates conflict-of-interest on the part of the bankers/brokers, but apparently it&#8217;s a good idea in 1999. This paved the way for the formation of Citigroup and other investment/commercial/insurance banks. The <span class="caps">GLB</span> Act also reduced the number of banks to which the <span class="caps">CRA </span>applied, and altered their requirements so that they&#8217;d have to report on their <span class="caps">CRA </span>compliance less often.</p>

<p>In 2003, the <a href="http://www.newyorkfed.org/research/epr/03v09n2/0306apga.pdf">Federal Reserve Bank of New York</a> said:</p>

<blockquote><p>Today, less than 30 percent of all home purchase loans are subject to intensive review under the <span class="caps">CRA.</span> In some metropolitan areas, this share is less than 10 percent.</p></blockquote>

<p>This was considered bad news because it meant that the remaining 70 percent of loans were virtually un-auditable, and the reasons that loan decisions were made were untraceable. And Congressman Ron Paul made a speech predicting that these policies of increased lending to poor people would result in a credit collapse and a bailout. Spooky, eh?</p>

<p>What about predatory lending practices? In 2002, according to <a href="http://en.wikipedia.org/wiki/Community_Reinvestment_Act">Wikipedia</a>:</p>

<blockquote><p>Kathleen C. Engel and Patricia A. McCoy published a study of the predatory lending implications of the <span class="caps">CRA, </span>noting that by the late 1990s, predatory high cost mortgages to “gullible borrowers” were leading to foreclosures against low-income people of color and the elderly.</p></blockquote>

<p>As a result, the <span class="caps">FDIC </span>decided to strongly discourage the practice of &#8220;predatory lending&#8221;. And any bank found guilty of predatory lending was considered a higher risk, resulting in a lower <span class="caps">CRA </span>performance rating for that bank, both of which made <span class="caps">FDIC </span>insurance significantly more expensive. Interestingly, this move seems to have been quite effective. A study sponsored by a New York law firm (Traiger &amp; Hinckley <span class="caps">LLP, </span>whoever they are), in January 2008 <a href="http://www.traigerlaw.com/publications/traiger_hinckley_llp_cra_foreclosure_study_1-7-08.pdf">found</a>:</p>

<blockquote><p>Our study suggests that without the <span class="caps">CRA, </span>the subprime crisis and related spike in foreclosures might have negatively impacted even more borrowers and neighborhoods.  Compared to other lenders in their assessment areas, <span class="caps">CRA</span> Banks were less likely to make a high cost loan, charged less for the high cost loans that were made, and were substantially more likely to eschew the secondary market and hold high cost and other loans in portfolio.  Moreover, branch availability is a key element of <span class="caps">CRA </span>compliance, and foreclosure rates were lower in metropolitan areas with proportionately greater numbers of bank branches.</p></blockquote>

<p>The <span class="caps">FDIC&#8217;</span>s measures to prevent irresponsible and predatory lending worked, and worked really well. So, if <span class="caps">CRA </span>wasn&#8217;t the problem, what was? What brought down Fannie Mae?</p>

<p>Remember the <span class="caps">HUD</span>? They expected that Fannie and Freddie would buy only the best quality subprime loans. But that backfired: what that ended up doing was making the market for these loans, including the even-crappier ones, bigger. According to Patricia McCoy, teacher of securities law at the University of Connecticut, cited in a <a href="http://www.washingtonpost.com/wp-dyn/content/article/2008/06/09/AR2008060902626_2.html">Washington Post</a> article, <em>&#8220;That just pumped more capital into a very unregulated market that has turned out to be a disaster.&#8221;</em> The <span class="caps">HUD&#8217;</span>s policies and encouragement are probably largely to blame for that market being particularly popular. It isn&#8217;t clear where the <span class="caps">HUD&#8217;</span>s policies started in 1992, but by 2000 the <span class="caps">HUD </span>required Fannie and Freddie to use 50% of their budget for &#8220;affordable&#8221; housing (sub-prime housing is one category that is considered &#8220;affordable&#8221;). Bush&#8217;s <span class="caps">HUD </span>upped that requirement to 56% in 2004. Clinton&#8217;s <span class="caps">HUD </span>opened the door by allowing subprime investing. But for some reason, it didn&#8217;t really take hold as a preferred strategy for investing in &#8220;affordable&#8221; housing.</p>

<p>Consider this: between 2005 and 2008, Fannie made loans to risky (sub-prime) borrowers of more than $270 billion dollars. That staggering sum is more than <strong>three times</strong> as much as all earlier years <strong>combined</strong>. But if it was <em>all</em> the <span class="caps">HUD&#8217;</span>s fault, then between 2005 and 2008, Fannie should have only increased subprime investment by about 6%, not 300%. And this would seem to have only a tangential relationship with the 30-year-old <span class="caps">CRA.</span> Something happened in 2004 or 2005! (One allegation I&#8217;ve heard is that Countrywide Mortgage started strong-arming Fannie into buying more of their riskiest loans at about that point.)</p>

<p>According to a <a href="http://www.nytimes.com/2008/10/05/business/05fannie.html?_r=1&amp;oref=slogin">New York Times article</a>, Fannie Mae&#8217;s <span class="caps">CEO,</span> Daniel H. Mudd, under pressure from competing banks stealing his market and from Congress members demanding that they loan more to low-income folks, was between a rock and a hard place:</p>

<blockquote><p>So Mr. Mudd made a fateful choice. Disregarding warnings from his managers that lenders were making too many loans that would never be repaid, he steered Fannie into more treacherous corners of the mortgage market, according to executives.</p>

<p>For a time, that decision proved profitable. In the end, it nearly destroyed the company and threatened to drag down the housing market and the economy.</p></blockquote>

<p>Nevertheless, folks like the venerable <a href="http://online.wsj.com/article/SB122204078161261183.html?mod=special_page_campaign2008_mostpop">Wall Street Journal</a> blame the <span class="caps">CRA </span>for encouraging loose lending standards:</p>

<blockquote><p>This 1977 law compels banks to make loans to poor borrowers who often cannot repay them.</p></blockquote>

<p>And yet somehow, the <span class="caps">CRA&#8217;</span>s actual effect appears to have been the exact opposite: banks that were covered by the <span class="caps">CRA </span>were <span class="caps">LESS </span>likely to make sub-prime loans than banks that had were unaffected by the <span class="caps">CRA.</span> Those independent mortgage companies (unaffected by the <span class="caps">CRA</span>) made &#8220;high-priced loans&#8221; at more than twice the rate of banks governed by the <span class="caps">CRA, </span><a href="http://www.frbsf.org/news/speeches/2008/0331.html">according to Janet L. Yellen</a>, President of the Federal Reserve Bank of San Francisco. According to the <a href="http://www.house.gov/apps/list/hearing/financialsvcs_dem/barr021308.pdf">congressional testimony of Michael Barr</a> (law professor at University of Michigan), 50% of the subprime loans were made by non-CRA mortgage companies, and another 25-30% came from mortgage companies only partially regulated by the <span class="caps">CRA</span>&#8212;-fully <span class="caps">CRA</span>-regulated institutions are responsible for &#8220;perhaps&#8221; one-in-four sub-prime loans. To quote the professor: <em>&#8220;the worst and most widespread abuses occurred in the institutions with the least federal oversight.&#8221;</em> And even more definitively, the <a href="http://en.wikipedia.org/wiki/Bank_for_International_Settlements">Bank for International Settlements</a> (an international organization of central banks) <a href="http://www.bis.org/publ/work259.pdf?noframes=1">concluded</a>:</p>

<blockquote><p>Contrary to some media commentary, there is no evidence that the Community Reinvestment Act was responsible for encouraging the subprime lending boom and subsequent housing bust. This Act only applies to depositories, and did not cover most of the important subprime lenders. Depositories showed a lesser tendency to write subprime loans than lenders not subject to the Act (Yellen 2008).</p></blockquote>

<p>Weird&#8230; you mean banks behave more responsibly when someone is peering over their shoulder? Say it ain&#8217;t so!</p>

<p>It seems to me that a large part of the Fannie problem was the <span class="caps">HUD, </span>enabling large scale sub-prime investing, and encouraging Fannie and Freddie to invest heavily in &#8220;affordable&#8221; housing. But that&#8217;s not the entirety of the problem; the <span class="caps">HUD </span>opened the door, but cannot have been responsible for the huge increase in sub-prime investment starting in 2005. However, even minimal investment in sub-prime mortgages (that <span class="caps">HUD </span>was trying to create) created demand in the sub-prime mortgage-backed securities market, which drove up the cost and encouraged other folks to invest as well.</p>

<p>I think the real lesson to be learned here is that there is no simple answer to questions like &#8220;what caused this economic mess?&#8221;, no matter how much politicians would love to say &#8220;it&#8217;s the other guy and his party&#8217;s fault&#8221;.</p>]]>
      
   </content>
</entry>
<entry>
   <title>The Bailout</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/09/the_bailout.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1344</id>
   <created>2008-09-30T06:45:26Z</created>
   <issued>2008-09-30T06:45:26Z</issued>
   <modified>2008-09-30T06:46:18Z</modified>
   
   <summary type="text/plain"></summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Politics" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p><img src="http://www.memoryhole.net/kyle/archives/images/debt_bailout.jpg" alt="" class="scaled" /></p>]]>
      
   </content>
</entry>
<entry>
   <title>That&apos;s MY $700 Billion</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/09/thats_my_700_billion.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1343</id>
   <created>2008-09-22T19:52:57Z</created>
   <issued>2008-09-22T19:52:57Z</issued>
   <modified>2008-10-05T05:00:36Z</modified>
   
   <summary type="text/plain">I just want to share a little of what I&amp;#8217;ve been reading, because I find it interesting. For example, consider Bloomberg&amp;#8217;s take on the candidates&amp;#8217; reactions to the latest financial news (namely, the $700 billion proposed bailout of the entire...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Politics" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>I just want to share a little of what I&#8217;ve been reading, because I find it interesting.</p>

<p>For example, consider <a href="http://www.bloomberg.com/apps/news?pid=20601070&amp;sid=alfUj1r0Z10o&amp;refer=politics">Bloomberg&#8217;s take</a> on the candidates&#8217; reactions to the latest financial news (namely, the $700 billion proposed bailout of the entire financial sector). Here&#8217;s a representative snippet:</p>

<blockquote><p>[Bill] Donaldson, who was tapped by Bush to head the <span class="caps">SEC, </span>says Obama called him last year about the financial-regulatory problems. He has never heard from McCain.</p>

<p>&#8220;Obama has been talking about the need for better financial regulation well before this crisis hit and has done some real thinking about it,&#8221; says Donaldson, a lifelong Republican. &#8220;McCain comes across as someone who suddenly realized changes have to be made.&#8221;</p></blockquote>

<p>Additionally, they compare the people that Obama and McCain turned to for advice. Both of them turned to three people for advice. Obama called three former Treasury Secretaries. Two are described as particularly effective (Rubin and Summers), and the third became a well-regarded chairman of the Federal Reserve (Paul Volcker &#8212; described as &#8220;towering&#8221;). McCain, on the other hand, called a Reagan administration adviser (Martin Feldstein) who was regularly cut out of the decision making process by Reagan&#8217;s chief-of-staff, a former undersecretary of the Treasury not known for doing much (John Taylor), and the former <span class="caps">CEO </span>of HP who was fired rather publicly and received $21 million from the golden parachute clause in her contract (Carly Fiorina). Bloomberg clearly prefers Obama&#8217;s choices.</p>

<p>And just when you thought fiscal conservatives would be mad enough about the government buying <span class="caps">AIG </span>($75 billion) and bailing out Fannie Mae and Freddie Mac ($25 billion) and bailing out Bear Stearns so that <span class="caps">JPM</span>organ would buy it ($29 billion), now comes the Big Bailout. Let me reformat that: the <strong><span class="caps">BIG BAILOUT</span></strong>. An order of magnitude bigger. The taxpayer is now on the hook for an additional <em>seven hundred billion dollars</em>. Let&#8217;s put that in perspective: according to <a href="http://www.ssa.gov/budget/2009bud.pdf">President Bush&#8217;s budget</a>, we&#8217;re expected to spend $659 billion on the oh-so-odious Social Security in 2008. The US Government total 2008 budget tips the scale at 2.931 <em>trillion</em> dollars, according to the <a href="http://www.heritage.org/Research/Taxes/wm1829.cfm">Heritage Foundation</a>, which means this bailout would be <b><span class="caps">TWENTY THREE PERCENT</span> OF <span class="caps">OUR ANNUAL BUDGET</span>!!!</b> Imagine if a guy that makes $100k/yr (which amounts to $77k/yr after taxes, i.e. $6,416/month) suddenly had to spend $17,710. That&#8217;s what we&#8217;re talking about. Are you screaming yet?</p>

<p>Jared Bernstein has an <a href="http://www.huffingtonpost.com/jared-bernstein/watching-history-unfold_b_128116.html">article in the Huffington Post</a>. Here we go:</p>

<blockquote><p>You hear that implosion reverberating through financial markets? It&#8217;s the sound of decades of conservative ideology collapsing. &#8230; The week that just ended revealed the myth of market fundamentalism: the notion associated with mainstream, Milton Freidman&#8217;esque economics, and amplified by anti-government conservatives that unfettered markets will provide society with the best outcomes. Such simplicity, such elegance&#8230;such nonsense.</p></blockquote>

<p>He has a list of what he calls &#8220;glaringly obvious truths&#8221; that I think is very worth reading.</p>

<p>Since you probably won&#8217;t go read that article, here&#8217;s a few key ones (and my reactions):</p>


<ol>
<li>Deregulated markets cannot police themselves; they tend toward speculation, vastly underpriced risk, and deeply damaging bubbles; <em>(sounds accurate to me, given what we&#8217;ve seen; or perhaps it&#8217;s more accurate to say that deregulated markets DO <span class="caps">NOT </span>police themselves, even if they <strong>can</strong>)</em></li>
<li>An economy that generates growth while leaving most families behind is a broken economy <em>(ok)</em></li>
<li>We can neither achieve broad prosperity nor compete globally without robust growth in key sectors which we have ignored or underfunded, including manufacturing, green production, and cradle-to-retirement public education; crafting evermore clever financial instruments will not pave the way to dependable, broadly shared growth <em>(translation: creative accounting is no substitute for good fundamentals)</em></li>
<li>No private sector firm should be too big to fail; any firm of that magnitude must be nationalized <em>(I would say, rather than nationalized, &#8220;broken up&#8221;, but the question becomes: at what point do we know that a company is too big? And what kind of a disincentive for smart business practices does this become? It&#8217;s fun to say that too big is bad, but is the cure worse than the disease?)</em></li>
<li>Capital markets are dysfunctional; borrowing and lending standards are ignored; lax capital requirements lead to constant over-leveraging; shadow accounts thwart transparency <em>(that&#8217;s not a truism, that&#8217;s describing the current situation)</em></li>
<li>We apparently can quickly find (or borrow) the money to do the stuff the authorities deem necessary, be it war or bailout; thus, we can also find the money we need for investment in people, from health care to education to infrastructure, etc. <em>(typical complaint&#8230; and saying &#8220;we don&#8217;t have the money&#8221; is obviously a poor excuse. These discussions should go back to &#8220;should we do this&#8221;)</em></li>
</ol>



<p>There&#8217;s more, but those are the most central to his point.</p>

<p>Is he saying that capitalism can&#8217;t work? Maybe. I disagree with such an extremist statement. But consider this: the current Treasury Secretary Paulson (whatever you think of him) said about this crisis: <a href="http://www.time.com/time/printout/0,8816,1842123,00.html"><b>Raw capitalism is dead.</b></a></p>

<p>Another fellow, named Robert Borosage, <a href="http://www.huffingtonpost.com/robert-l-borosage/financial-crisis-time-for_b_128114.html">puts it slightly differently in his article</a>:</p>

<blockquote><p>Call it extortion. Every American is now being told to ante up $2000 &#8212; an estimated $700 billion in all &#8212; to bail out the banks from their bad bets, or they&#8217;ll bring down the entire economy.</p>


<p>In the speculative frenzy that allowed the Masters of the Universe to pocket millions personally, the banks filled their coffers with toxic paper that no one wants to buy. Now they sensibly don&#8217;t want to lend money to each other, since no one knows if the other is solvent. So they go on strike, and threaten to trigger a global depression, if they don&#8217;t get rescued.</p></blockquote>

<p>He then lists seven of his own suggestions, which are also worth reading. Granted, I don&#8217;t see how his fourth suggestion (curb excessive <span class="caps">CEO </span>pay) would do much more than make us all feel better, but the heart of the matter is: these guys ran their companies, along with anyone who depended on them, into the ground. They shouldn&#8217;t be allowed to profit. The government&#8217;s bailout money should, if used at all, be only used to keep the <em>economy</em> afloat. <span class="caps">CEO </span>golden parachutes should be considered just as forfeit as if the company went bankrupt. Because that&#8217;s what these companies are: <strong><span class="caps">BANKRUPT</span></strong>.</p>

<p>Does nobody else in the world get chills down their spine from just uttering that word?</p>

<p>I still want to know just what would happen if these companies were simply forced to sleep in the beds they&#8217;ve made for themselves. If <span class="caps">AIG </span>did such a bad job, what would happen if they simply went belly-up? Wouldn&#8217;t other folks in the market be forced to realize that they really DO need to perform due diligence? That there&#8217;s a <span class="caps">REASON </span>things are called risky? So people get fearful, so credit starts to be harder to get, so people stop investing in things they don&#8217;t understand&#8230; isn&#8217;t that a <em>good</em> thing? The fundamental problem here, that started this whole mess, is that they were being too free with the credit and loaned money to anyone with a pulse and never bothered checking whether it was a good idea to loan them money! Credit <em>should</em> be harder to get! People who can&#8217;t afford a house <em>shouldn&#8217;t be buying one</em>! And if you don&#8217;t know how risky an investment is or if you don&#8217;t understand where the return comes from <em><span class="caps">DON&#8217;T BUY</span> IT</em>! There&#8217;s a shocking idea for you&#8230;</p>

<p>These people deserve to lose lots of money for making stupid decisions, and anyone who loaned those folks money (aka investors) won&#8217;t be getting it back. But we don&#8217;t want some sort of cascade to happen where nobody invests in anybody ever again. How can we make people learn their lessons without taking the stock market down with them? There are a lot of ordinary folks in the stock market these days, including ones that may not even realize it, due to their retirement accounts: the day of the pension has passed, and 401k&#8217;s rule the land. When the market goes down, they take the little guys with them.</p>

<p>Buying all of these bad investments, such that they now become <span class="caps">OUR </span>bad investments, seems like a bad idea (no matter whether it&#8217;s the government doing it or our mutual fund managers). What do we-the-people want with bad investments? We don&#8217;t! And yet, this bailout seems more like, as Bernstein puts it: privatizing the profits and socializing the losses. Oh goody, sign me up for that! You notice all the pro-deregulation folks are lining up behind this bailout, don&#8217;t you? It&#8217;s called voting yourself money. It&#8217;s also called &#8220;saving your own hide&#8221;&#8230; at everyone else&#8217;s expense.</p>]]>
      
   </content>
</entry>
<entry>
   <title>More Compiler Complaints: Sparc Edition</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/08/more_compiler_complaints_sparc.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1335</id>
   <created>2008-08-19T00:01:12Z</created>
   <issued>2008-08-19T00:01:12Z</issued>
   <modified>2008-08-21T15:10:25Z</modified>
   
   <summary type="text/plain">Unlike my previous whining about compilers, this one I have no explanation for. It&amp;#8217;s not me specifying things incorrectly, it&amp;#8217;s just the compiler being broken. So, here&amp;#8217;s the goal: atomically increment a variable. On a Sparc (specifically, SparcV9), the function...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Research" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>Unlike my <a href="http://www.memoryhole.net/kyle/2008/01/apples_compiler_idiocy.html">previous whining about compilers</a>, this one I have no explanation for. It&#8217;s not me specifying things incorrectly, it&#8217;s just the compiler being broken.</p>

<p>So, here&#8217;s the goal: atomically increment a variable. On a Sparc (specifically, SparcV9), the function looks something like this:</p>

<pre><code language="cc">static inline int atomic_inc(int * operand)
{
    register uint32_t oldval, newval;
    newval = *operand;
    do {
        oldval = newval;
        newval++;
        __asm__ __volatile__ (&quot;cas [%1], %2, %0&quot;
            : &quot;=&amp;r&quot; (newval)
            : &quot;r&quot; (operand), &quot;r&quot;(oldval)
            : &quot;cc&quot;, &quot;memory&quot;);
    } while (oldval != newval);
    return oldval+1;
}</code></pre>

<p>Seems trivial, right? We use the <span class="caps">CAS </span>instruction (compare and swap). Conveniently, whenever the comparison fails, it stores the value of <code>*operand</code> in the second register (i.e. %0 aka newval), so there are no extraneous memory operations in this little loop. Right? Right. Does it work? <em><span class="caps">NO.</span></em></p>

<p>Let&#8217;s take a look at the assembly that the compiler (gcc) generates with -O2 optimization:</p>

<pre><code language="asm">save    %sp, -0x60, %sp
ld      [%i0], %i5      /* newval = *operand; */
mov     %i0, %o1        /* operand is copied into %o1 */
mov     %i5, %o2        /* oldval = newval; */
cas     [%o1], %o2, %o0 /* o1 = operand, o2 = newval, o0 = ? */
ret
restore %i5, 0x1, %o0</code></pre>

<p>Say what? Does that have <span class="caps">ANYTHING </span>to do with what I told it? Nope! <code>%o0</code> is never even initialized, but somehow it gets used anyway! What about the increment? Nope! It was optimized out, apparently (which, in fairness, is probably because we didn&#8217;t explicitly list it as an input). Of course, gcc is awful, you say! Use <span class="caps">SUN&#8217;</span>s compiler! Sorry, it produces the <strong>exact same</strong> output.</p>

<p>But let&#8217;s be a bit more explicit about the fact that the <code>newval</code> register is an input to the assembly block:</p>

<pre><code language="cc">static inline int atomic_inc(int * operand)
{
    register uint32_t oldval, newval;
    newval = *operand;
    do {
        oldval = newval;
        newval++;
        __asm__ __volatile__ (&quot;cas [%1], %2, %0&quot;
            : &quot;=&amp;r&quot; (newval)
            : &quot;r&quot; (operand), &quot;r&quot;(oldval), &quot;0&quot;(newval)
            : &quot;cc&quot;, &quot;memory&quot;);
    } while (oldval != newval);
    return oldval+1;
}</code></pre>

<p>Now, Sun&#8217;s compiler complains: <code>warning: parameter in inline asm statement unused: %3</code>. Well gosh, isn&#8217;t that useful; way to recognize the fact that <code>&quot;0&quot;</code> declares the input to be an output! But at least, gcc leaves the <code>add</code> operation in:</p>

<pre><code language="asm">save    %sp, -0x60, %sp
ld      [%i0], %i5      /* oldval = *operand; */
mov     %i0, %o1        /* operand is copied to %o1 */
add     %i5, 0x1, %o0   /* newval = oldval + 1; */
mov     %i5, %o2        /* oldval is copied to %o2 */
cas     [%o1], %o2, %o0
ret
restore %i5, 0x1, %o0</code></pre>

<p>Yay! The increment made it in there, and <code>%o0</code> is now initialized to something! But what happened to the <code>do{ }while()</code> loop? Sorry, that was optimized away, because gcc doesn&#8217;t recognize that <code>newval</code> can change values, despite the fact that <em>it&#8217;s listed as an output!</em></p>

<p>Sun&#8217;s compiler will at least leave the while loop in, but will often use the <span class="caps">WRONG REGISTER </span>for comparison (such as <code>%i2</code> instead of <code>%o0</code>).</p>

<p>But check out this minor change:</p>

<pre><code language="cc">static inline int atomic_inc(int * operand)
{
    register uint32_t oldval, newval;
    do {
        newval = *operand;
        oldval = newval;
        newval++;
        __asm__ __volatile__ (&quot;cas [%1], %2, %0&quot;
            : &quot;=&amp;r&quot; (newval)
            : &quot;r&quot; (operand), &quot;r&quot;(oldval), &quot;0&quot;(newval)
            : &quot;cc&quot;, &quot;memory&quot;);
    } while (oldval != newval);
    return oldval+1;
}</code></pre>

<p>See the difference? Rather than using the output of the <code>cas</code> instruction (<code>newval</code>), we&#8217;re throwing it away and re-reading <code>*operand</code> no matter what. And guess what suddenly happens:</p>

<pre><code language="asm">save     %sp, -0x60, %sp
ld       [%i0], %i5           /* oldval = *operand; */
add      %i5, 0x1, %o0        /* newval = oldval + 1; */
mov      %i0, %o1             /* operand is copied to %o1 */
mov      %i5, %o2             /* oldval is copied to %o2 */
cas      [%o1], %o2, %o0
cmp      %i5, %o0             /* if (oldval != newval) */
bne,a,pt %icc, atomic_inc+0x8 /* then go back and try again */
ld       [%i0], %i5
ret
restore  %i5, 0x1, %o0</code></pre>

<p><span class="caps">AHA</span>! The while loop returns! And best of all, both <span class="caps">GCC </span>and Sun&#8217;s compiler suddenly, magically, (and best of all, <em>consistently</em>) use the correct registers for the loop comparison! It&#8217;s amazing! For some reason this change reminds the compilers that <code>newval</code> is an output!</p>

<p>It&#8217;s completely idiotic. So, we can get it to work&#8230; but we have to be inefficient in order to do it, because otherwise (inexplicably) the compiler refuses to acknowledge that our output register can change.</p>

<p>In case you&#8217;re curious, the gcc version is:<br />
<code>sparc-sun-solaris2.10-gcc (GCC) 4.0.4 (gccfss)</code><br />
and the Sun compiler is:<br />
<code>cc: Sun C 5.9 SunOS_sparc 2007/05/03</code></p>]]>
      
   </content>
</entry>
<entry>
   <title>I Hate Procmail</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/06/i_hate_procmail.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1328</id>
   <created>2008-06-27T16:33:38Z</created>
   <issued>2008-06-27T16:33:38Z</issued>
   <modified>2008-06-27T17:40:46Z</modified>
   
   <summary type="text/plain">Its error handling is CRAP. I am coming to this realization because I recently lost a BUNCH of messages because of a bad delivery path (I told procmail to pipe messages to a non-existent executable). So what did procmail do?...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Commentary" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>Its error handling is <strong><span class="caps">CRAP</span></strong>.</p>

<p>I am coming to this realization because I recently lost a <span class="caps">BUNCH </span>of messages because of a bad delivery path (I told procmail to pipe messages to a non-existent executable). So what did procmail do? According to its log:</p>

<pre><code>/bin/sh: /tmp/dovecot11/libexec/dovecot/deliver: No such file or directory
procmail: Error while writing to &quot;/tmp/dovecot11/libexec/dovecot/deliver&quot;</code></pre>

<p>Well, sure, that&#8217;s to be expected, right? So what happened to the email? <span class="caps">VANISHED.</span> Into the bloody ether.</p>

<p>Of course, determining that the message vanished is trickier than just saying &#8220;hey, it&#8217;s not in my mailbox.&#8221; Oh no, there&#8217;s a &#8220;feature&#8221;, called <code>ORGMAIL</code>. What is this? According to the procmailrc documentation (*that* collection of wisdom):</p>

<pre><code>ORGMAIL     Usually the system  mailbox  (ORiGinal  MAIL‐
            box).   If,  for  some  obscure  reason (like
            ‘filesystem full’)  the  mail  could  not  be
            delivered, then this mailbox will be the last
            resort.  If procmail fails to save  the  mail
            in  here  (deep,  deep  trouble :-), then the
            mail will bounce back to the sender.</code></pre>

<p>And so where is <span class="caps">THAT</span>? Why, <code>/var/mail/$LOGNAME</code> of course, where else? And if <code>LOGNAME</code> isn&#8217;t set for some reason? Or what if <code>ORGMAIL</code> is unset? Oh, well&#8230; nuts to you! Procmail will use <code>$SENDMAIL</code> to <em><span class="caps">BOUNCE THE EMAIL</span></em> rather than just try again later. That&#8217;s what they mean by &#8220;deep, deep trouble.&#8221; Notice the smiley face? Here&#8217;s why the manual has a smiley-face in it: to mock your pain.</p>

<p>But here&#8217;s the real crux of it: procmail doesn&#8217;t see delivery errors as <span class="caps">FATAL.</span> If one delivery instruction fails, it&#8217;ll just keep going through the procmailrc, looking for anything else that might match. In other words, the logic of your procmailrc has to take into account the fact that sometimes mail delivery can fail. If you fail to do this, your mail <span class="caps">CAN </span>end up in <strong><span class="caps">RANDOM LOCATIONS</span></strong>, depending on how messages that were supposed to match earlier rules fare against later rules.</p>

<p>If you want &#8220;first failure bail&#8221; behavior (which makes the most sense, in my mind), you have to add an extra rule after <span class="caps">EVERY </span>delivery instruction. For example:</p>

<pre><code>:0 H
* ^From: .*fred@there\.com
./from_fred

:0 e # handle failure
{
    EXITCODE=75 # set a non-zero exit code
    HOST # This causes procmail to stop, obviously
}</code></pre>

<p>You agree that <code>HOST</code> means &#8220;stop processing and exit&#8221;, right? Obviously. That&#8217;s procmail for you. <strong>Note that that second clause has gotta go after <span class="caps">EVERY </span>delivery instruction.</strong> I hope you enjoy copy-and-paste.</p>

<p>Another way to handle errors, since <strong>successful</strong> delivery <strong>does</strong> stop procmail, is to add something like that to the end of your procmailrc, like so:</p>

<pre><code>:0 # catch-all default delivery
${DEFAULT}

 # If we get this far, there must have been an error
EXITCODE=75
HOST</code></pre>

<p>Of course, you could also send the mail to <code>/dev/null</code> at that point, but unsetting the <code>HOST</code> variable (which is what listing it does) does the same thing faster. Intuitive, right? Here&#8217;s my smiley-face:</p>

<p><code>&gt;:-P</code></p>]]>
      
   </content>
</entry>
<entry>
   <title>YAASI: Yet Another Anti-Spam Idea</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/04/yaasi_yet_another_antispam_ide.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1326</id>
   <created>2008-04-25T00:22:26Z</created>
   <issued>2008-04-25T00:22:26Z</issued>
   <modified>2008-04-25T06:53:09Z</modified>
   
   <summary type="text/plain">Branden and I had an idea to help with the spam problem on our system, and it&amp;#8217;s proven particularly effective. How effective? Here&amp;#8217;s the graphs from the last year of email on my system. Can you tell when I started...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Cool Stuff" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>Branden and I had an idea to help with the spam problem on our system, and it&#8217;s proven particularly effective. How effective? Here&#8217;s the graphs from the last year of email on my system. Can you tell when I started using the system?</p>

<p><img src="http://www.memoryhole.net/kyle/archives/images/antispam.png" alt="" class="scaled" /></p>

<p><img src="http://www.memoryhole.net/kyle/archives/images/antispam2.png" alt="" class="scaled" /></p>

<p>If you want to see the live images, check <a href="http://www.we-be-smart.org/rrd/">here</a>.</p>

<p>The idea is based on the following observations: certain addresses on my domain <span class="caps">ONLY </span>get spam. This is generally because they either don&#8217;t exist or because I stopped using them; for example, spammers often send email to buy@memoryhole.net. Branden and I also  both use the user-tag@domain scheme, so we get a lot of disposable addresses that way. These addresses are such that we know for <strong>certain</strong> that anyone sending email to them is a spammer. Some of these addresses were already being rejected as invalid; some we hadn&#8217;t gotten around to invalidating yet.</p>

<p>By simply rejecting emails sent to those addresses, we were able to reduce the spam load of our domains by a fair bit, and the false-positive rate is nil. But we took things a step further: since spammers rarely send only one message, often they will send spam to both invalid <span class="caps">AND </span>valid addresses.</p>

<p>If I view those known-bad addresses as, essentially, honeypots, I can say: aha! Any IP sending to a known-bad address is a spammer, and I can refuse (with a permanent fail) any email from that IP for some short time. I started with 5 minutes, but have moved to an exponentially increasing timeout system. Each additional spam increased the length of the timeout (5 minutes for the first spam, 6 for the second, 8 for the third, and so on). Longer-term bans, as a result of the exponentially increasing timeout, are made more efficient via the equivalent of <code>/etc/hosts.deny</code>. I haven&#8217;t gotten into the maintaining-my-spammer-database much yet, but I think this may not be terribly important (I&#8217;ll explain in a moment).</p>

<p>One of the best parts of the system is that it is <strong>fast</strong>: new spammers that identify themselves by sending to honeypot addresses get blocked quickly and without my intervention. So far this has been particularly helpful in eliminating spam spikes. Another feature that I originally thought would be useful, but hasn&#8217;t really appeared to be (yet) is that it allows our multiple domains to share information about spam sources. Thus far, however, our domains seem to be plagued by different spammers.</p>

<p>Now, interestingly, about a week after we started using the system, our database of known spammers was wiped out (it&#8217;s kept in /tmp, and we rebooted the system). Result? No noticeable change in effectiveness. How&#8217;s that for a result? And, as you can see from the graph above, there&#8217;s no obvious change in spam blocking over the course of a month that would indicate that the long-term history is particularly useful. So, it may be sufficient to keep a much shorter history. Maybe only a week is necessary, maybe two weeks, I haven&#8217;t decided yet (and, as there hasn&#8217;t yet been much of a speed penalty for it, there&#8217;s no pressure to establish a cutoff). But, given that most spam is sent from botnets with dynamic IPs, this isn&#8217;t a particularly surprising behavior.</p>

<p>Forkit.org and memoryhole.net have been using this filter for a month so far. The week before we started using this filter, memoryhole.net averaged around 262 emails per hour. The week after instituting this filter, the average was around 96 per hour (a 60+% reduction!). Before using the filter, forkit.org averaged 70 emails per hour; since starting to use the filter, that number is down to 27.4 per hour (also a 60+% reduction). We have recorded spams from over 33,000 IPs, most of which only ever sent one or two spams. We typically have between 100 and 150 IPs that are “in jail” at any one time (at this moment: 143), and most of those (at this moment 134) are blocked for sending more than ten spams (114 of them have a timeout measured in days rather than minutes).</p>

<p>Now, granted, I know that by simply dropping 60% of all connections we’d get approximately the same results. But I think our particular technique is superior to that because it&#8217;s based on known-bad addresses. Anyone who doesn&#8217;t send to invalid addresses will never notice the filter.</p>

<p>The biggest potential problem that I can see with this system is that of spammers who have taken over a normally friendly host, such as Gmail spam. I&#8217;ve waffled on this potential problem: on the one hand, Gmail has so many outbound servers that it&#8217;s unlikely to get caught (a couple bad emails won&#8217;t have much of a penalty). Thus far, I&#8217;ve seen a few yahoo servers in Japan sending us spam, but no Gmail servers. On the other hand, as long as I simply use temporary failures (at least for good addresses), and as long as ND doesn&#8217;t retry in the same order every time, messages will get through.</p>

<p>I&#8217;ve also begun testing a &#8220;restricted sender&#8221; feature to work with this. For example, I have the address kyle-slashdot@memoryhole.net that I use exclusively for my slashdot.org account. The only people who are allowed to send to that email address is slashdot.org (i.e. if I forget my password). If anyone from any other domain attempts that address, well, then I know that sending IP is a spammer and I can treat it as if it was a known-bad address. Not applicable to <strong>every</strong> email address, obviously, but it&#8217;s a start.</p>

<p>It&#8217;s been pointed out that this system is, in some respects, a variant on greylisting. The major difference is that it&#8217;s a penalty-based system, rather than a &#8220;prove yourself worthy by following the <span class="caps">RFC</span>&#8221; system, and I like that a bit better. I&#8217;m somewhat tempted to define some bogus address (bogus@memoryhole.net) and sign it up for spam (via <a href="http://spamyourenemies.com">spamyourenemies.com</a> or something similar), but given that part of the benefit here is due to spammers trying both valid and invalid addresses, I think it would probably just generate lots of extra traffic and not achieve anything particularly useful.</p>

<p>Now, this technique is simply one of many; it&#8217;s not sufficient to guarantee a spam-free inbox. I use it in combination with several other antispam techniques, including a greet-delay system and a frequently updated SpamAssassin setup. But check out the difference it&#8217;s made in our <span class="caps">CPU </span>utilization:</p>

<p><img src="http://www.memoryhole.net/kyle/archives/images/antispam-cpu.png" alt="" class="scaled" /></p>

<p>Okay, so, grand scheme of things: knocking the <span class="caps">CPU </span>use down three percentage points isn&#8217;t huge, but knocking it down by 50%? That sounds better, anyway. And as long as it doesn&#8217;t cause problems by making <strong>valid</strong> email disappear (possible, but rather unlikely), it seems to me to be a great way to cut my spam load relatively easily.</p>]]>
      
   </content>
</entry>
<entry>
   <title>Leopard - Finally!</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/04/leopard_finally.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1325</id>
   <created>2008-04-08T21:45:29Z</created>
   <issued>2008-04-08T21:45:29Z</issued>
   <modified>2008-04-09T02:48:23Z</modified>
   
   <summary type="text/plain">So, I upgraded to MacOS 10.5 recently (from 10.4). Those of you who know me will doubtless be thinking &amp;#8220;my god, man, what took so long?!?&amp;#8221;, and that&amp;#8217;s a longer story than I want to get into right now. Suffice...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Commentary" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>So, I upgraded to MacOS 10.5 recently (from 10.4). Those of you who know me will doubtless be thinking &#8220;my god, man, what took so long?!?&#8221;, and that&#8217;s a longer story than I want to get into right now. Suffice to say: we&#8217;re rocking and rolling now!</p>

<p>My impressions of the new OS are pretty favorable. I&#8217;ve read all the complaints about the UI changes, and they have some merit. By the time I upgraded, Apple had already released 10.5.2, which addressed many of the more unfortunate problems for people like me who put /Applications into the Dock.</p>

<p>I really DO like the &#8220;Fan&#8221; icon display, though, particularly for the new &#8220;Downloads&#8221; folder. Creating a folder just for downloads is something I could have done years ago, of course, but I hadn&#8217;t - everything downloaded to the Desktop, which inevitably became incredibly cluttered. But I <strong>love</strong> the new approach, and part of what makes it especially useful is that things in the &#8220;Fan&#8221; display can be <strong>dragged to the trash.</strong> HA! I love it! It&#8217;s the little things that make me happy. :)</p>

<p>The new <span class="caps">X11 </span>is a bit of a pain in the butt. I&#8217;d become very used to using <code>xterm</code> - or more precisely, <code>uxterm</code> - for all my terminal needs (which is to say, for 90% of what I do with my computer). That&#8217;s not so tenable now, particularly since Apple has apparently decided that <code>uxterm</code> was just too useful a shell script to let stand. I am keeping a copy of that shell script (which just runs <code>xterm</code> with all the necessary utf-8 flags and sets the <span class="caps">LANG </span>appropriately) handy, just in case, but for the time being, I&#8217;ve decided to migrate to using Apple&#8217;s Terminal full time now. Undoubtedly, it&#8217;s still not as fast as <code>uxterm</code>, but since getting an Intel iMac, I don&#8217;t really notice anymore (on the old dual 500Mhz <span class="caps">G4, </span>it was definitely noticable).</p>

<p>For migrating, I&#8217;ve had to create my own <code>nsterm-16color</code> termcap file (which I keep in <code>~/.terminfo/n/nsterm-16color</code> ) in order to ensure that all the features I want work properly. I stole the file from ncurses 5.6, and modified it to add correct dual-mode swapping ( <code>smcup=\E7\E[?47h, rmcup=\E[2J\E[?47l\E8</code> ) and then to support the home and end keys ( <code>khome=\E[H, kend=\E[F</code> ). These are things that the native <span class="caps">OSX </span>dtterm/xterm/xterm-color/whatever terminfo settings don&#8217;t do correctly. (<sigh> WHY???) &#8230;And then, of course, I had to fix the key mapping of pageup/shift-pageup and pagedown/shift-pagedown and all the relative keys, but that was easy to do in the Terminal.app&#8217;s preferences. The defaults are sensible, just not for folks who are used to xterm&#8217;s behavior. I also re-discovered that I hate Terminal.app&#8217;s default blue (a dark, almost-midnight blue), and much prefer having a lighter one. Thankfully I&#8217;m not the only one - <a href="http://ciaranwal.sh/2007/11/01/customising-colours-in-leopard-terminal">Ciarán Walsh&#8217;s update to the TerminalColors plugin</a> is solid and works well.</p>

<p>Other than that, things have been pretty smooth. I haven&#8217;t experienced any really strange compatibility problems &#8212; in large part, I think, because I keep my system pretty up-to-date, so I already had the &#8220;Leopard-compatible&#8221; versions of all the software I use (and all the Unix applications seem to work flawlessly without even needing a recompile - huzzah for that!).</p>

<p>The one application that needed <span class="caps">SERIOUS </span>fiddling is <a href="http://www.virtualbox.org">VirtualBox</a>. They have an <span class="caps">OSX </span>version, but only in beta form. I use it mostly so I can provide sensible Windows XP support to relatives who have computer questions (and for doing browser compatibility tests). I had been using Beta 2 (1.4.6), which had worked flawlessly for my needs. Unfortunately, Beta 2 isn&#8217;t compatible with Leopard, so an upgrade to the latest (Beta 3) was necessary. <span class="caps">THIS </span>beta seems to have a few problems. For one thing, it can&#8217;t understand all the old machine definitions (so when upgrading, make sure you don&#8217;t have any important system snapshots or saved machine state that you need). However, it does understand the old disk files, so it&#8217;s a simple matter to create a new machine definition using the old disk. The new machine still won&#8217;t <strong><span class="caps">BOOT,</span></strong> though, and it took me an hour or so of fiddling to figure out how to fix it.</p>

<p>There are two major problems that crop up. First: <strong>they changed the default <span class="caps">IDE </span>controller</strong> for Windows XP guests. The old default was <span class="caps">PIIX3</span>; the new default is <span class="caps">PIIX4.</span> Either one will work, and if you install XP from scratch on a newly created XP host, it will work with the <span class="caps">PIIX4 </span>controller just fine. But if you&#8217;re booting from an XP that was created with Beta 2 (i.e. a WindowsXP installation that thinks you have a <span class="caps">PIIX3 </span>controller), it will blue-screen and reboot immediately after displaying the Microsoft logo: not good. Fixing it is easy, though: just change the <span class="caps">IDE </span>controller for your XP machine in the machine settings dialog.</p>

<p>The second problem is that <strong>the network doesn&#8217;t work.</strong> Actually, that&#8217;s not true, the network works just fine, it&#8217;s <span class="caps">DNS </span>resolution that doesn&#8217;t work (but one looks a lot like the other when you&#8217;re not paying close attention to error messages). For whatever reason, when your XP system uses <span class="caps">DHCP </span>to get its network information, the information it receives from VirtualBox is <em>wrong.</em> Specifically, VirtualBox tells it to resolve <span class="caps">DNS </span>names by contacting 10.0.2.3; it should be contacting 10.0.2.2 (i.e. the same as the router). Fixing this was just a matter of changing Windows&#8217; network configuration to use a custom <span class="caps">DNS </span>server (10.0.2.2) rather than the one supplied by <span class="caps">DHCP.</span> Annoying, but nothing terrible.</p>

<p>The only other stumbling block in Leopard that I&#8217;ve come across is the iChat-vs-Internet-Sharing problem that other people have discovered. Essentially, if you have enabled Internet Sharing, iChat can&#8217;t do video conferencing. Something to do with being able to remap ports&#8230; the explanations I&#8217;ve read are rather vague. It&#8217;s not especially important to me, but came up when I was trying to demonstrate the virtues of Leopard to Emily.</p>

<p>Which reminds me: the new iChat is <span class="caps">MUCH </span>better for talking to multiple people at the same time. The &#8220;tabbed&#8221; chatting interface is <strong>terrific.</strong> The vaunted &#8220;Spaces&#8221; (virtual desktops) are nice, and implemented well, but I gotta say that I&#8217;ve gotten used to having just one desktop these days (I use Exposé <strong>a lot</strong>). Getting used to having the extra desktops will probably take a while.</p>

<p>Two more features I noticed were the Quick View (in Finder, press the space bar to quickly view something) and Web Clips (in Safari, you can take a snippet of a webpage and turn it into a Dashboard widget). Quick View is pretty great, especially for folders full of <span class="caps">PDF</span>s, because you can leave it up and keep navigating around the Finder (the contents of the Quick View window will track whatever you select in the Finder), but since I don&#8217;t spend much time in the Finder, it&#8217;s of limited use. If I could integrate it with my <code>~/.mailcap</code> file, now <span class="caps">THAT </span>would be awesome. Web Clips are not <strong>quite</strong> as great as they could be. For one thing, they don&#8217;t refresh quickly (but they DO refresh&#8212;at first I didn&#8217;t think they did&#8212;and in the worst case, you can click on them and press Ctrl-R to force the issue), and for another, they can&#8217;t scale &#8212; many of the things I want to clip are large graphics that I wish to monitor. If <span class="caps">OSX </span>could scale clips down for me, that would make them much more useful.</p>

<p>Which reminds me &#8212; one new feature of Leopard that I <strong>adore</strong> is their new built-in <span class="caps">VNC </span>viewer. It may not actually be <span class="caps">VNC, </span>but that&#8217;s fine by me &#8212; it&#8217;s blazing fast, and best of all, it scales the screen down so that you can easily control a screen that&#8217;s larger than the one you have. Chicken of the <span class="caps">VNC </span>used to be a must-have application for me, but Leopard&#8217;s built-in screen viewer is <strong>much</strong> better for what I usually want to do (which is control the iMac upstairs from the laptop down on the couch).</p>]]>
      
   </content>
</entry>
<entry>
   <title>My Bashrc</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/03/my_bashrc.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1322</id>
   <created>2008-03-13T18:51:37Z</created>
   <issued>2008-03-13T18:51:37Z</issued>
   <modified>2009-06-26T22:05:16Z</modified>
   
   <summary type="text/plain">There are few things that, over my time using Unix-like systems, I have put more cumulative effort into than into my configuration files. I&apos;ve been tweaking them since the day I discovered them, attempting to make my environment more and...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Cool Stuff" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>There are few things that, over my time using Unix-like systems, I have put more cumulative effort into than into my configuration files. I've been tweaking them since the day I discovered them, attempting to make my environment more and more to my liking. I have posted them on my other website (<a href="http://www.memoryhole.net/~kyle/index.php?section=reference">here</a>), but it occurred to me that they've gotten sufficiently hoary and complex that a walkthrough might help someone other than myself.
</p><p>
Anyway, my <a href="http://www.memoryhole.net/~kyle/bashrc.html">bashrc</a> is first on the list. (Or, if you like, the pure <a href="http://www.memoryhole.net/~kyle/bashrc.txt">text</a> version.)
</p><p>
The file is divided into several (kinda fuzzy) sections:<br/>
- <a href="#init">Initialization & Other Setup</a><br/>
- <a href="#funcs">Useful Functions</a><br/>
- <a href="#sysbashrc">Loading System-wide Bashrc</a><br/>
- <a href="#behavioral">Behavioral Settings</a><br/>
- <a href="#envariable">Environment Variables</a><br/>
- <a href="#charset">Character Set Detection</a><br/>
- <a href="#aliases">Aliases</a><br/>
- <a href="#tab">Tab-completion Options</a><br/>
- <a href="#local">Machine-local settings</a><br/>
- <a href="#logout">Auto-logout</a></p>

<p>Let's take them one at a time.</p>

<h3 id="init">Initialization & Other Setup</h3>
<p>
Throughout my bashrc, I use a function I define here ( <code>dprint</code> ) to allow me to quickly turn on debugging information, which includes printing the seconds-since-bash-started variable ( <code>SECONDS</code> ) in case something is taking too long and you want to find the culprit. Yes, my bashrc has a debug mode. This is essentially controlled by the <code>KBWDEBUG</code> environment variable. Then, because this has come in useful once or twice, I allow myself to optionally create a <code>~/.bashrc.local.preload</code> file which is sourced now, before anything else. Here's the code:
</p>
<pre><code language="bash">KBWDEBUG=${KBWDEBUG:-no}

function dprint {
if [[ "$KBWDEBUG" == "yes" && "$-" == *i* ]]; then
    #date "+%H:%M:%S $*"
    echo $SECONDS $*
fi
}
dprint alive
if [ -r "${HOME}/.bashrc.local.preload" ]; then
    dprint "Loading bashrc preload"
    source "${HOME}/.bashrc.local.preload"
fi</code></pre>

<h3 id="#funcs">Useful Functions</h3>
<p>
This section started with some simple functions for PATH manipulation. Then those functions got a little more complicated, then I wanted some extra functions for keeping track of my config files (which were now in CVS), and then they got more complicated...</p>

<p>You'll notice something about these functions. Bash (these days) will accept function declarations in this form:</p>

<pre><code language="bash">function fname()
{
    do stuff
}</code></pre>

<p>But that wasn't always the case. To maintain compatability with older bash versions, I avoid using the uselessly cosmetic parens and I make sure that the curly-braces are on the same line, like so:</p>

<pre><code language="bash">function fname \
{
    do stuff
}</code></pre>

<p>Anyway, the path manipulation functions are pretty typical &mdash; they're similar to the ones that <a href="http://www.finkproject.org/">Fink</a> uses, but slightly more elegant. The idea is based on these rules of PATH variables:</p>

<ol>
<li>Paths must not have duplicate entries</li>
<li>Paths are faster if they don't have symlinks in them</li>
<li>Paths must not have "." in them</li>
<li>All entries in a path must exist (usually)</li>
</ol>

<p>There are two basic path manipulation functions: <code>add_to_path</code> and <code>add_to_path_first</code>. They do predictable things &mdash; the former appends something to a given path variable (e.g. <code>PATH</code> or <code>MANPATH</code> or <code>LD_LIBRARY_PATH</code> ) unless it's already in that path, and the latter function prepends something to the given PATH variable (or, if it's already in there, moves it to the beginning). Before they add a value to a path, they first check it to make sure it exists, is readable, that I can execute things that are inside it, and they resolve any symlinks in that path (more on that in a moment). Here's the code (ignore the reference to <code>add_to_path_force</code> in <code>add_to_path</code> for now; I'll explain shortly):</p>

<pre><code language="bash">function add_to_path \
{
    local folder="${2%%/}"
    [ -d "$folder" -a -x "$folder" ] || return
    folder=`( cd "$folder" ; \pwd -P )`
    add_to_path_force "$1" "$folder"
}

function add_to_path_first \
{
    local folder="${2%%/}"
    [ -d "$folder" -a -x "$folder" ] || return
    folder=`( cd "$folder" ; \pwd -P )`
    # in the middle, move to front
    if eval '[[' -z "\"\${$1##*:$folder:*}\"" ']]'; then
        eval "$1=\"$folder:\${$1//:\$folder:/:}\""
        # at the end
    elif eval '[[' -z "\"\${$1%%*:\$folder}\"" ']]'; then
        eval "$1=\"$folder:\${$1%%:\$folder}\""
        # no path
    elif eval '[[' -z "\"\$$1\"" ']]'; then
        eval "$1=\"$folder\""
        # not in the path
    elif ! eval '[[' -z "\"\${$1##\$folder:*}\"" '||' \
      "\"\$$1\"" '==' "\"$folder\"" ']]'; then
        eval "export $1=\"$folder:\$$1\""
    fi
}</code></pre>

<p>Then, because I was often logging into big multi-user Unix systems (particularly Solaris systems) with really UGLY <code>PATH</code> settings that had duplicate entries, often included ".", not to mention directories that either didn't exist or that I didn't have sufficient permissions to read, I added the function <code>verify_path</code>. All this function does is separates a path variable into its component pieces, eliminates ".", and then reconstructs the path using <code>add_to_path</code>, which handily takes care of duplicate and inaccessible entries. Here's that function:</p>

<pre><code language="bash">function verify_path \
{
    # separating cmd out is stupid, but is compatible
    # with older, buggy, bash versions (2.05b.0(1)-release)
    local cmd="echo \$$1"
    local arg="`eval $cmd`"
    eval "$1=\"\""
    while [[ $arg == *:* ]] ; do
        dir="${arg%:${arg#*:}}"
        arg="${arg#*:}"
        if [ "$dir" != "." -a -d "$dir" -a \
          -x "$dir" -a -r "$dir" ] ; then
            dir=`( \cd "$dir" ; \pwd -P )`
            add_to_path "$1" "$dir"
        fi
    done
    if [ "$arg" != "." -a -d "$arg" -a -x "$arg" -a -r "$arg" ] ;
    then
        arg=`( cd "$arg" ; \pwd -P )`
        add_to_path "$1" "$arg"
    fi
}</code></pre>

<p>Finally, I discovered <code>XFILESEARCHPATH</code> &mdash; a path variable that requires a strange sort of markup (it's for defining where your app-defaults files are for X applications). This wouldn't work for <code>add_to_path</code>, so I created <code>add_to_path_force</code> that still did duplicate checking but didn't do any verification of the things added to the path.</p>

<pre><code language="bash">function add_to_path_force \
{
    if eval '[[' -z "\$$1" ']]'; then
        eval "export $1='$2'"
    elif ! eval '[[' \
        -z "\"\${$1##*:\$2:*}\"" '||' \
        -z "\"\${$1%%*:\$2}\"" '||' \
        -z "\"\${$1##\$2:*}\"" '||' \
        "\"\${$1}\"" '==' "\"$2\"" ']]'; then
        eval "export $1=\"\$$1:$2\""
    fi
}</code></pre>

<p>I mentioned that I resolved symlinks before adding directories to path variables. This is a neat trick I discovered due to the existence of <code>pwd -P</code> and subshells. <code>pwd -P</code> will return the "real" path to the folder you're in, with all symlinks resolved. And it does so very efficiently (without actually resolving symlinks &mdash; it just follows all the ".." records). Since you can change directories in a subshell (i.e. between parentheses) without affecting the parent shell, a quick way to transform a folder's path into a resolved path is this: <code>( \cd "$folder"; pwd -P)</code>. I put the backslash in there to use the shell's builtin cd, just in case I'd somehow lost my mind and aliased cd to something else.</p>

<p>And then, just because it was convenient, I added another function: <code>have</code>, which detects whether a binary is accessible or not:</p>

<pre><code language="bash">function have { type "$1" &>/dev/null ; }</code></pre>

<p>Then I had to confront file paths, such as the <code>MAILCAP</code> variable. A lot of the same logic (i.e. <code>add_to_path_force</code>), but entry validation is different:</p>

<pre><code language="bash">function add_to_path_file \
{
    local file="${2}"
    [ -f "$file" -a -r "$file" ] || return
    # realpath alias may not be set up yet
    file=`realpath_func "$file"`
    add_to_path_force "$1" "$file"
}</code></pre>

<p>You'll note the <code>realpath_func</code> line in there. <code>realpath</code> is a program that takes a filename or directory name and resolves the symlinks in it. Unfortunately, <code>realpath</code> is a slightly unusual program; I've only ever found it on OSX (it may be on other BSDs). But, with the power of my <code>pwd -P</code> trick, I can fake most of it. The last little piece (resolving a file symlink) relies on a tool called <code>readlink</code> ... but I can fake that too. Here are the two functions:</p>

<pre><code language="bash">function readlink_func \
{
    if have readlink ; then
        readlink "$1"
    #elif have perl ; then # seems slower than alternative
    #    perl -e 'print readlink("'"$1"'") . "\n"'
    else
        \ls -l "$1" | sed 's/[^>]*-> //'
    fi
}

function realpath_func \
{
    local input="${1}"
    local output="/"
    if [ -d "$input" -a -x "$input" ] ; then
        # All too easy...
        output=`( cd "$input"; \pwd -P )`
    else
        # sane-itize the input to the containing folder
        input="${input%%/}"
        local fname="${input##*/}"
        input="${input%/*}"
        if [ ! -d "$input" -o ! -x "$input" ] ; then
            echo "$input is not an accessible directory" >&2
            return
        fi
        output="`( cd "$input" ; \pwd -P )`/"
        input="$fname"
        # output is now the realpath of the containing folder
        # so all we have to do is handle the fname (aka "input)
        if [ ! -L "$output$input" ] ; then
            output="$output$input"
        else
            input="`readlink_func "$output$input"`"
            while [ "$input" ] ; do
                if [[ $input == /* ]] ; then
                    output="$input"
                    input=""
                elif [[ $input == ../* ]] ; then
                    output="${output%/*/}/"
                    input="${input#../}"
                elif [[ $input == ./* ]] ; then
                    input="${input#./}"
                elif [[ $input == */* ]] ; then
                    output="$output${input%${input#*/}}"
                    input="${input#*/}"
                else
                    output="$output$input"
                    input=""
                fi
                if [ -L "${output%%/}" ] ; then
                    if [ "$input" ] ; then
                        input="`readlink_func "${output%%/}"`/$input"
                    else
                        input="`readlink_func "${output%%/}"`"
                    fi
                    output="${output%%/}"
                    output="${output%/*}/"
                fi
            done
        fi
    fi
    echo "${output%%/}"
}</code></pre>

<h3 id="#sysbashrc">Loading System-wide Bashrc</h3>

<p>This section isn't too exciting. According to the man page:</p>

<blockquote><p>When bash is invoked as an interactive login shell, or as a  non-interactive  shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists.   After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one  that exists  and  is  readable.</p></blockquote>

<p>SOME systems have a version of bash that appears not to obey this rule. And some systems put crucial configuration settings in <code>/etc/bashrc</code> (why?!?). And <i>some</i> systems even do something silly like use <code>/etc/bashrc</code> to source <code>~/.bashrc</code> (I did this myself, once upon a time, when I knew not-so-much). I've decided that this behavior cannot be relied upon, so I explicitly source these files myself. The only interesting bit is that I added a workaround so that systems that use <code>/etc/bashrc</code> to source <code>~/.bashrc</code> won't get into an infinite loop. There's probably a lot more potential trouble here that I'm ignoring. But here's the code:</p>

<pre><code language="bash">if [[ -r /etc/bashrc && $SYSTEM_BASHRC != 1 ]]; then
    dprint " - loading /etc/bashrc"
    . /etc/bashrc
    export SYSTEM_BASHRC=1
fi</code></pre>

<h3 id="#behavioral">Behavioral Settings</h3>

<p>This is basic stuff, but after you get used to certain behaviors (such as whether * matches . and ..), you often get surprised when they don't work that way on other systems. Some of this is because I found a system that did it another way by default; some is because I decided I like my defaults and I don't want to be surprised in the future.</p>

<p>The interactive-shell-detection here is nice. <code>$-</code> is a variable set by bash containing a set of letters indicating certain settings. It always contains the letter i if bash is running interactively. So far, this has been quite backwards-compatible.</p>

<pre><code language="bash">shopt -s extglob # Fancy patterns, e.g. +()
# only interactive
if [[ $- == *i* ]]; then
    dprint setting the really spiffy stuff
    shopt -s checkwinsize # don't get confused by resizing
    shopt -s checkhash # if hash is broken, doublecheck it
    shopt -s cdspell # be tolerant of cd spelling mistakes
fi</code></pre>

<h3 id="#envariables">Environment Variables</h3>

<p>There are a <i>slew</i> of standard environment variables that bash defines for you (such as <code>HOSTNAME</code>). There are even more standard environment variables that various programs pay attention to (such as <code>EDITOR</code> and <code>PAGER</code>). And there are a few others that are program-specific (such as <code>PARINIT</code> and <code>CVSROOT</code>).</p>

<p>Before I get going, though, let me show you a secret. Ssh doesn't like transmitting information from client to server shell... the only reliable way to do it that I've found is the <code>TERM</code> variable. So... I smuggle info through that way, delimited by colons. Before I set any other environment variables, first, I find my smuggled information:</p>

<pre><code language="bash">if [[ $TERM == *:* && ( $SSH_CLIENT || $SSH_TTY || $SSH_CLIENT2 ) ]] ; then
    dprint "Smuggled information through the TERM variable!"
    term_smuggling=( ${TERM//:/ } )
    export SSH_LANG=${term_smuggling[1]}
    TERM=${term_smuggling[0]}
    unset term_smuggling
fi</code></pre>

<p>I begin by setting <code>GROUPNAME</code> and <code>USER</code> in a standard way:</p>

<pre><code language="bash">if [[ $OSTYPE == solaris* ]] ; then
    idout=(`/bin/id -a`)
    USER="${idout[0]%%\)*}"
    USER="${USER##*\(}"
    [[ $USER == ${idout[0]} ]] && USER="UnknownUser"
    GROUPNAME="UnknownGroup"
    unset idout
else
    [[ -z $GROUPNAME ]] && GROUPNAME="`id -gn`"
    [[ -z $USER ]] && USER="`id -un`"
fi</code></pre>

<p>Then some standard things (<code>MAILPATH</code> is used by bash to check for mail, that kind of thing), including creating <code>OS_VER</code> and <code>HOST</code> to allow me to identify the system I'm running on:</p>

<pre><code language="bash"># I tote my own terminfo files around with me
[ -d ~/.terminfo ] && export TERMINFO=~/.terminfo/
[ "$TERM_PROGRAM" == "Apple_Terminal" ] && \
    export TERM=nsterm-16color

MAILPATH=""
MAILCHECK=30
add_to_path_file MAILPATH /var/spool/mail/$USER
add_to_path MAILPATH $HOME/Maildir/
[[ -z $MAILPATH ]] && unset MAILCHECK
[[ -z $HOSTNAME ]] && \
    export HOSTNAME=`/bin/hostname` && echo 'Fake Bash!'
HISTSIZE=1000
HOST=${OSTYPE%%[[:digit:]]*}
OS_VER=${OSTYPE#$HOST}
[ -z "$OS_VER" ] && OS_VER=$( uname -r )
OS_VER=(${OS_VER//./ })
TTY=`tty`
PARINIT="rTbgq B=.,?_A_a P=_s Q=>|}+"

export USER GROUPNAME MAILPATH HISTSIZE OS_VER HOST TTY PARINIT</code></pre>

<p>I've also gotten myself into trouble in the past with <code>UMASK</code> being set improperly, so it's worth setting manually. Additionally, to head off trouble, I make it hard to leave myself logged in as root on other people's systems accidentally:</p>

<pre><code language="bash">if [[ $GROUPNAME == $USER && $UID -gt 99 ]]; then
    umask 002
else
    umask 022
fi

if [[ $USER == root ]] ; then
    [[ $SSH_CLIENT || $SSH_TTY || $SSH_CLIENT2  ]] && \
        export TMOUT=600 || export TMOUT=3600
fi

if [[ -z $INPUTRC && ! -r $HOME/.inputrc && -r /etc/inputrc ]];
then
    export INPUTRC=/etc/inputrc
fi</code></pre>

<p>It is at this point that we should pause and load anything that was in /etc/profile, just in case it was left out (and, if its in there, maybe it should override what we've done so far):</p>

<pre><code language="bash">export BASHRCREAD=1

if [[ -r /etc/profile && -z $SYSTEM_PROFILE ]]; then
    dprint "- loading /etc/profile ... "
    . /etc/profile
    export SYSTEM_PROFILE=1
fi</code></pre>

<p>Now I set my prompt (but only if this is an interactive shell). There are several details here (obviously). The first is that, if I'm logged into another system, I want to see how long I've been idle. So I include a timestamp whenever I'm logged into a remote system. I also added color to my prompt in two ways, which has been very useful. First, it changes the color of the $ at the end of the prompt to red if the last command didn't exit cleanly. Second, remote systems have yellow prompts, whenever I'm root I have a red prompt, and I created commands to flip between a few other colors (blue, purple, cyan, green, etc.) in case I find that useful to quickly distinguish between terminals. Anyway, here's the code:</p>

<pre><code language="bash">if [[ $- == *i* ]]; then
    if [[ $TERM == xterm* || $OSTYPE == darwin* ]]; then
        # This puts the term information into the title
        PSterminfo='\[\e]2;\u@\h: \w\a\]'
    fi
    PSparts[3]='(\d \T)\n'
    PSparts[2]='[\u@\h \W]'
    PSparts[1]='\$ '
    PScolors[2]='\[\e[34m\]' # Blue
    PScolors[3]='\[\e[35m\]' # Purple
    PScolors[4]='\[\e[36m\]' # Cyan
    PScolors[5]='\[\e[32m\]' # Green
    PScolors[6]='\[\e[33m\]' # Yellow
    PScolors[100]='\[\e[31m\]' # Badc
    PScolors[0]='\[\e[0m\]' # Reset
    if [[ $USER == root ]] ; then
        PScolors[1]='\[\e[31m\]' # Red
    elif [[ $SSH_CLIENT || $SSH_TTY || $SSH_CLIENT2 ]] ; then
        PScolors[1]="${PScolors[6]}" # yellow
        if [[ $HOSTNAME == marvin ]] ; then
            PScolors[1]="${PScolors[5]}" # green
        fi
    else
        unset PSparts[3]
        PScolors[1]=""
    fi
    function bashrc_genps {
        if [ "$1" -a "${PScolors[$1]}" ] ; then
            PSgood="$PSterminfo${PSparts[3]}${PScolors[$1]}${PSparts[2]}${PSparts[1]}${PScolors[0]}"
        else
            PSgood="$PSterminfo${PSparts[3]}${PSparts[2]}${PSparts[1]}"
        fi
        PSbad="$PSterminfo${PSparts[3]}${PScolors[$1]}${PSparts[2]}${PScolors[100]}${PSparts[1]}${PScolors[0]}"
    }
    bashrc_genps 1
    function safeprompt {
        export PS1='{\u@\h \W}\$ '
        unset PROMPT_COMMAND
    }
    alias stdprompt='bashrc_genps 1'
    alias blueprompt='bashrc_genps 2'
    alias purpleprompt='bashrc_genps 3'
    alias cyanprompt='bashrc_genps 4'
    alias greenprompt='bashrc_genps 5'
    alias whiteprompt='bashrc_genps'
    # this is executed before every prompt is displayed
    # it changes the prompt based on the preceeding command
    export PROMPT_COMMAND='[ $? = 0 ] && PS1=$PSgood || PS1=$PSbad'
fi</code></pre>

<p>Now I set up the various paths. Note that it doesn't matter if these paths don't exist; they'll be checked and ignored if they don't exist:</p>

<pre><code language="bash">verify_path PATH
add_to_path PATH "/usr/local/sbin"
add_to_path PATH "/usr/local/teTeX/bin"
add_to_path PATH "/usr/X11R6/bin"
add_to_path PATH "$HOME/bin"
add_to_path_first PATH "/sbin"

add_to_path_first PATH "/bin"
add_to_path_first PATH "/usr/sbin"
add_to_path_first PATH "/opt/local/bin"
add_to_path_first PATH "/usr/local/bin"

if [[ $OSTYPE == darwin* ]] ; then
    add_to_path PATH "$HOME/.conf/darwincmds"

    # The XFILESEARCHPATH (for app-defaults and such)
    # is a wonky kind of path
    [ -d /opt/local/lib/X11/app-defaults/ ] && \
        add_to_path_force XFILESEARCHPATH \
            /opt/local/lib/X11/%T/%N
    [ -d /sw/etc/app-defaults/ ] && \
        add_to_path_force XFILESEARCHPATH /sw/etc/%T/%N
    add_to_path_force XFILESEARCHPATH /private/etc/X11/%T/%N
fi

verify_path MANPATH
add_to_path MANPATH "/usr/man"
add_to_path MANPATH "/usr/share/man"
add_to_path MANPATH "/usr/X11R6/man"
add_to_path_first MANPATH "/opt/local/share/man"
add_to_path_first MANPATH "/opt/local/man"
add_to_path_first MANPATH "/usr/local/man"
add_to_path_first MANPATH "/usr/local/share/man"

verify_path INFOPATH
add_to_path INFOPATH "/usr/share/info"
add_to_path INFOPATH "/opt/local/share/info"</code></pre>

<p>And now there are STILL MORE environment variables to set. This final group may rely on some of the previous paths being set (most notably, <code>PATH</code>).</p>

<pre><code language="bash">export PAGER='less'
have vim && export EDITOR='vim' || export EDITOR='vi'
if [[ -z $DISPLAY && $OSTYPE == darwin* ]]; then
    processes=`ps ax`
    # there are double-equals here, even though they don't show
    # on the webpage
    if [[ $processes == *xinit* || $processes == *quartz-wm* ]]; then
        export DISPLAY=:0
    else
        unset DISPLAY
    fi
fi
if [[ $HOSTNAME == wizard ]] ; then
    dprint Wizards X forwarding is broken
    unset DISPLAY
fi
export TZ="US/Central"
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then
    export HISTCONTROL=ignoreboth
else
    export HISTCONTROL="ignorespace:erasedups"
fi
export HISTIGNORE="&:ls:[bf]g:exit"
export GLOBIGNORE=".:.."
export CVSROOT=kyle@cvs.memoryhole.net:/home/kyle/cvsroot
export CVS_RSH=ssh
export BASH_ENV=$HOME/.bashrc
add_to_path_file MAILCAPS $HOME/.mailcap
add_to_path_file MAILCAPS /etc/mailcap
add_to_path_file MAILCAPS /usr/etc/mailcap
add_to_path_file MAILCAPS /usr/local/etc/mailcap
export EMAIL='kyle-envariable@memoryhole.net'
export GPG_TTY=$TTY
export RSYNC_RSH="ssh -2 -c arcfour -o Compression=no -x"
if [ -d /opt/local/include -a -d /opt/local/lib ] ; then
    export CPPFLAGS="-I/opt/local/include $CPPFLAGS"
    export LDFLAGS="-L/opt/local/lib $LDFLAGS"
fi
if have glibtoolize ; then
    have libtoolize || export LIBTOOLIZE=glibtoolize
fi</code></pre>

<p>One little detail that I rather like is the fact that xterm's window title often tells me exactly what user I am on what machine I am, particularly when I'm ssh'd into another host. This little bit of code ensures that this happens:</p>

<pre><code language="bash">if [[ $TERM == xterm* || $OSTYPE == darwin* ]]; then
    export PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME/.*/}: ${PWD/${HOME}/~}\007"'
else
    unset PROMPT_COMMAND
fi</code></pre>

<h3 id="#charset">Character Set Detection</h3>

<p>I typically work in a UTF-8 environment. MacOS X (my preferred platform for day-to-day stuff) has made this pretty easy with really excellent UTF-8 support, and Linux has come a long way (to parity, as far as I can tell) in the last few years. Most of my computing is done via a uxterm (aka. xterm with UTF-8 capability turned on), but I also occasionally dabble in other terminals (sometimes without realizing it). Despite the progress made, however, not all systems support UTF-8, and neither do all terminals. Some systems, including certain servers I've used, simply don't have UTF-8 support <i>installed<i>, even though they're quite capable of it.</p>

<p>The idea is that the <code>LANG</code> environment variable is supposed to reflect the language you want to use and character set your terminal can display. So, this is where I try and figure out what <code>LANG</code> should be.</p>

<p>The nifty <code>xprop</code> trick here is from a vim hint I found. I haven't used it for very long, but so far it seems to be a really slick way of finding out what sort of environment your term is doing, even if it hasn't set the right environment variables (e.g. <code>LANG</code>).</p>

<p>One of the more annoying details of this stuff is that ssh doesn't pass LANG (or any other locale information) along when you connect to a remote server. Granted, there are good reasons for this (just because <i>my</i> computer is happy when <code>LANG=en_US.utf-8</code> doesn't mean any server I connect to would be), but at the same time, shouldn't the remote server be made aware of my local terminal's capabilities? Imagine if I connected to a server that defaults to Japanese, but I want it to know that I use English! Remember how I smuggled that information through in <code>TERM</code> and stuck it in the <code>SSH_LANG</code> variable? Here's where it becomes important.</p>

<p>I've also fiddled with different variations of this code to make it as compatible as possible. So far, this should work with Bash 2.05b and up... though that makes it slightly awkward-looking.</p>

<p>As a final note here, I discovered that <code>less</code> is capable of handling multibyte charsets (at least, recent versions of it are), but for whatever reason it doesn't always support <code>LANG</code> and other associated envariables. It DOES however support <code>LESSCHARSET</code>...</p>

<p>Anyway, here's the code:</p>

<pre><code language="bash">if [[ -z $LANG ]] ; then
    dprint no LANG set
    if [[ $WINDOWID ]] && have xprop ; then
        dprint querying xprop
        __bashrc__wmlocal=(`xprop -id $WINDOWID -f WM_LOCALE_NAME 8s ' $0' -notype WM_LOCALE_NAME`)
        export LANG=`eval echo ${__bashrc__wmlocal[1]}`
        unset __bashrc__wmlocal
    elif [[ $OSTYPE == darwin* ]] ; then
        dprint "I'm on Darwin"
        if [[ ( $SSH_LANG && \
            ( $SSH_LANG == *.UTF* || $SSH_LANG == *.utf* ) || \
            $TERM_PROGRAM == Apple_Terminal ) && \
            -d "/usr/share/locale/en_US.UTF-8" ]] ; then
            export LANG='en_US.UTF-8'
        elif [ -d "/usr/share/locale/en_US" ] ; then
            export LANG='en_US'
        else
            export LANG=C
        fi
    elif [[ $TERM == linux || $TERM_PROGRAM == GLterm ]] ; then
        if [ -d "/usr/share/locale/en_US" ] ; then
            export LANG='en_US'
        else
            export LANG=C # last resort
        fi
    else
        if [[ $SSH_LANG == C ]] ; then
            export LANG=C
        elif have locale ; then
            dprint "checking locale from big list (A)"
            locales=`locale -a`
            locales="${locales//[[:space:]]/|}" # not +() because that's slow
            if [[ en_US.utf8 == @($locales) ]] ; then
                export LANG='en_US.utf8'
            elif [[ en_US.utf-8 == @($locales) ]] ; then
                export LANG='en_US.utf-8'
            elif [[ en_US.UTF8 == @($locales) ]] ; then
                export LANG='en_US.UTF8'
            elif [[ en_US.UTF-8 == @($locales) ]] ; then
                export LANG='en_US.UTF-8'
            elif [[ en_US == @($locales) ]] ; then
                export LANG='en_US'
            else
                export LANG=C
            fi
            unset locales
        fi
    fi
else
    dprint "- LANG IS ALREADY SET! ($LANG)"
    if [[ $SSH_LANG && $SSH_LANG != $LANG ]]; then
        if [[ $SSH_LANG == C ]] ; then
            export LANG=C
        else
            dprint "checking locale from big list (B)"
            locales=`locale -a`
            locales="${locales//[[:space:]]/|}" # not +() because that's slow
            if [[ $SSH_LANG == @(${locales}) ]] ; then
                dprint "- SSH_LANG is a valid locale, resetting LANG"
                LANG=$SSH_LANG
            else
                dprint "- SSH_LANG is NOT a valid locale"
                wantutf8=no
                if [[ $SSH_LANG == *.<code>(u|U)</code>(t|T)@(f|F)?(-)8 ]] ; then
                    wantutf8=yes
                    if [[ ! $LANG == *.<code>(u|U)</code>(t|T)@(f|F)?(-)8 ]] ; then
                        dprint "- want utf-8, but LANG is not utf8, unsetting"
                        unset LANG
                    fi
                else
                    dprint "- don't want utf-8"
                fi
                if [[ ! $LANG || ! $LANG == @($locales) ]] ; then
                    if [ "$wantutf8" = yes ] ; then
                        dprint "- finding a utf8 LANG"
                        if [[ en_US.utf8 == @($locales) ]] ; then
                            export LANG='en_US.utf8'
                        elif [[ en_US.utf-8 == @($locales) ]] ; then
                            export LANG='en_US.utf-8'
                        elif [[ en_US.UTF8 == @($locales) ]] ; then
                            export LANG='en_US.UTF8'
                        elif [[ en_US.UTF-8 == @($locales) ]] ; then
                            export LANG='en_US.UTF-8'
                        elif [[ en_US == @($locales) ]] ; then
                            export LANG='en_US'
                        else
                            export LANG=C
                        fi
                    else
                        dprint "- finding a basic LANG"
                        if [[ en_US == @($locales) ]] ; then
                            export LANG='en_US'
                        else
                            export LANG=C
                        fi
                    fi
                fi
                unset wantutf8
            fi
            unset locales
        fi
    else
        dprint "- ... without SSH_LANG, why mess with it?"
    fi
fi
dprint - LANG is $LANG
unset LESSCHARSET
if [[ $LANG == *.<code>(u|U)</code>(t|T)@(f|F)?(-)8 ]] ; then
    export LESSCHARSET=utf-8
fi</code></pre>

<h3 id="#aliases">Aliases</h3>

<p>This is where a lot of the real action is, in terms of convenience settings. Like anyone who uses a computer every day, I type a lot; and if I can avoid it, so much the better. (I'm a lazy engineer.)</p>

<p>Sometimes I can't </i>quite</i> get what I want out of an alias. In <b>csh</b> aliases can specify what to do with their arguments. In bash, aliases are really more just shorthand &mdash; "pretend I really typed this" kind of stuff. Instead, if you want to be more creative with argument handling, you have to use functions (it's not a big deal, really). Here's a few functions I added just because they're occasionally handy to have the shell do for me:</p>

<pre><code language="bash">function exec_cvim {
/Applications/Vim.app/Contents/MacOS/Vim -g "$@" &
}

function darwin_locate { mdfind "kMDItemDisplayName == '$@'wc"; }
if [[ $- == *i* && $OSTYPE == darwin* && ${OS_VER[0]} -ge 8 ]] ;
then
alias locate=darwin_locate
fi

function printargs { for F in "$@" ; do echo "$F" ; done ; }
function psq { ps ax | grep -i $@ | grep -v grep ; }
function printarray {
for ((i=0;$i<`eval 'echo ${#'$1'[*]}'`;i++)) ; do
    echo $1"[$i]" = `eval 'echo ${'$1'['$i']}'`
done
}
alias back='cd $OLDPWD'</code></pre>

<p>There are often a lot of things that I just expect to <i>work</i>. For example, when I type "ls", I want it to print out the contents of the current directory. In color if possible, without if necessary. It often annoys me, on Solaris systems, when the <i>working</i> version of <code>ls</code> is buried in the path, while a really lame version is up in <code>/bin</code> for me to find first. Here's how I fix that problem:</p>

<pre><code language="bash"># GNU ls check
if [[ $OSTYPE == darwin* ]]; then
    dprint "- DARWIN ls"
    alias ls='/bin/ls -FG'
    alias ll='/bin/ls -lhFG'
elif have colorls ; then
    dprint "- BSD colorls"
    alias ls='colorls -FG'
    alias ll='colorls -lhFG'
else
    __kbwbashrc__lsarray=(`\type -ap ls`)
    __kbwbashrc__lsfound=no
    for ((i=0;$i<${#__kbwbashrc__lsarray[*]};i=$i+1)) ; do
        if ${__kbwbashrc__lsarray[$i]} --version &>/dev/null ;
        then
            dprint "- found GNU ls: ${__kbwbashrc__lsarray[$i]}"
            alias ls="${__kbwbashrc__lsarray[$i]} --color -F"
            alias ll="${__kbwbashrc__lsarray[$i]} --color -F -lh"
            __kbwbashrc__lsfound=yes
            break
        fi
    done
    if [ "$__kbwbashrc__lsfound" == no ] ; then
        if ls -F &>/dev/null ; then
            dprint "- POSIX ls"
            alias ls='ls -F'
            alias ll='ls -lhF'
        else
            alias ll='ls -lh'
        fi
    fi
    unset __kbwbashrc__lsarray __kbwbashrc__lsfound
fi</code></pre>

<p>Similar things are true of <code>make</code> and <code>sed</code> and such. I've gotten used to GNU's version, and if they exist on the machine I'd much rather automatically use them than have to figure out whether it's really called <code>gnused</code> or <code>gsed</code> or <code>justtowasteyourtimesed</code> all by myself:</p>

<pre><code language="bash">if [[ $OSTYPE == linux* ]] ; then
    # actually, just Debian, but this works for now
    alias gv="gv --watch --antialias"
else
    alias gv="gv -watch -antialias"
fi
if have gsed ; then
    alias sed=gsed
elif have gnused ; then
    alias sed=gnused
fi
if have gmake ; then
    alias make=gmake
elif have gnumake ; then
    alias make=gnumake
fi</code></pre>

<p>The rest of them are mostly boring, with one exception:</p>

<pre><code language="bash">alias macfile="perl -e 'tr/\x0d/\x0a/'"
have tidy && alias tidy='tidy -m -c -i'
have vim && alias vi='vim'
alias vlock='vlock -a'
alias fastscp='scp -c arcfour -o Compression=no' # yay speed!
alias startx='nohup ssh-agent startx & exit'
alias whatlocale='printenv | grep ^LC_'
alias fixx='xauth generate $DISPLAY'
alias whatuses='fuser -v -n tcp'
alias which=type
alias ssh='env TERM="$TERM:$LANG" ssh'
have realpath || alias realpath=realpath_func
if have readlink ; then
    unset -f readlink_func
else
    alias readlink=readlink_func
fi
if [[ $OSTYPE == darwin* ]]; then
    alias top='top -R -F -ocpu -Otime'
    alias cvim='exec_cvim'
    alias gvim='exec_cvim'
fi</code></pre>

<p>Did you note that ssh alias? Heh.</p>

<h3 id="#tab">Tab-completion Options</h3>

<p>Bash has had, for a little while at least, the ability to do custom tab-completion. This is <i>really</i> convenient (for example, when I've typed <code>cvs commit</code> and I hit tab, bash can know that I really just want to tab-complete files that have been changed). However, I won't bore you with a long list of all the handy tab-completions that are out there. Most of mine are just copied from <a href="http://www.caliban.org/bash/">here</a> anyway. But I often operate in places where that big ol' bash-completion file can be in multiple places. Here's the simple little loop I use. You'll notice that it only does the loop after ensuring that bash is of recent-enough vintage:</p>

<pre><code language="bash">completion_options=(
~/.conf/bash_completion
/etc/bash_completion
/opt/local/etc/bash_completion
)
if [[ $BASH_VERSION && -z $BASH_COMPLETION && $- == *i* ]] ;
then
    bash=${BASH_VERSION%.*}; bmajor=${bash%.*}; bminor=${bash#*.}
    if [ $bmajor -eq 2 -a $bminor '>' 04 ] || [ $bmajor -gt 2 ] ;
    then
        for bc in "${completion_options[@]}" ; do
            if [[ -r $bc ]] ; then
                dprint Loading the bash_completion file
                if [ "$BASH_COMPLETION" ] ; then
                    BASH_COMPLETION="$bc"
                fi
                #COMP_CVS_REMOTE=yes
                export COMP_CVS_ENTRIES=yes
                source "$bc"
                break
            fi
        done
    fi
    unset bash bminor bmajor
fi
unset completion_options</code></pre>

<h3 id="#local">Machine-local settings</h3>

<p>You'd be surprised how useful this can be sometimes. Sometimes I need machine-specific settings. For example, on some machines there's a PGI compiler I want to use, and maybe it needs some environment variable set. Rather than put it in the main bashrc, I just put that stuff into <code>~/.bashrc.local</code> and have it loaded:</p>

<pre><code language="bash">dprint checking for bashrc.local in $HOME
if [ -r "${HOME}/.bashrc.local" ]; then
    dprint Loading local bashrc
    source "${HOME}/.bashrc.local"
fi</code></pre>

<h3 id="#logout">Auto-logout</h3>

<p>Lastly, it is sometimes the case that the <code>TMOUT</code> variable has been set, either by myself, or by a sysadmin who doesn't like idle users (on a popular system, too many idle users can unnecessarily run you out of ssh sockets, for example). In any case, when my time is limited, I like being aware of how much time I have left. So I have my bashrc detect the <code>TMOUT</code> variable and print out a big banner so that I know what's up and how much time I have. Note that bash can do simple math all by itself with the <code>$(( ))</code> construction. Heheh. Anyway:</p>

<pre><code language="bash">if [[ $TMOUT && "$-" == *i* ]]; then
    echo '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
    echo You will be autologged out after:
    echo -e -n '\t'
    seconds=$TMOUT
    days=$((seconds/60/60/24))
    seconds=$((seconds-days*24*60*60))
    hours=$((seconds/60/60))
    seconds=$((seconds-hours*60*60))
    minutes=$((seconds/60))
    seconds=$((seconds-minutes*60))
    [[ $days != 0 ]] && echo -n "$days days "
    [[ $hours != 0 ]] && echo -n "$hours hours "
    [[ $minutes != 0 ]] && echo -n "$minutes minutes "
    [[ $seconds != 0 ]] && echo -n "$seconds seconds "
    echo
    echo ... of being idle.
    unset days hours minutes seconds
fi

dprint BASHRC_DONE</code></pre>

<p>While I'm at it, I suppose I should point out that I also have a <code>~/.bash_logout</code> file that's got some niceness to it. If it's the last shell, it clears sudo's cache, empties the console's scrollback buffer, and clears the screen. Note: <b><i>DO NOT PUT THIS IN YOUR BASHRC</i></b> You wouldn't like it in there.</p>

<pre><code language="bash">if [ "$SHLVL" -eq 1 ] ; then
    sudo -k
    type -P clear_console &>/dev/null && clear_console 2>/dev/null
    clear
fi</code></pre>

<p>And that's about it! Of course, I'm sure I'll add little details here and there and this blog entry will become outdated. But hopefully someone finds my bashrc useful. I know I've put a lot of time and effort into it. :)</p>]]>
      
   </content>
</entry>
<entry>
   <title>w3m and MacPorts</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/03/w3m_and_macports.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1323</id>
   <created>2008-03-13T16:12:00Z</created>
   <issued>2008-03-13T16:12:00Z</issued>
   <modified>2008-03-14T00:17:39Z</modified>
   
   <summary type="text/plain">For whatever reason, w3m refuses to build on my Intel OSX box with the latest boehmgc library. To get it to build, you must forcibly downgrade to boehmgc 6.8 or 6.7 or something earlier. Also, I noticed that w3m isn&amp;#8217;t...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="People Suck" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>For whatever reason, w3m refuses to build on my Intel <span class="caps">OSX </span>box with the latest boehmgc library. To get it to build, you must forcibly downgrade to boehmgc 6.8 or 6.7 or something earlier.</p>

<p>Also, I noticed that w3m isn&#8217;t marked as depending on gdk-pixbuf. Strictly speaking, it doesn&#8217;t, but it does if you have <code>--enable-image=x11</code>. :P Add this to your Portfile:</p>

<pre><code>depends_lib lib:libgccpp.1:boehmgc bin:gdk-pixbuf-config:gdk-pixbuf</code></pre>

<p>Also, it seems that either w3m or gdk-pixbuf-config appends an extra library to the config line for gdk-pixbuf-config (essentially, they specify <code>-lgdk_pixbuf</code> <span class="caps">AND </span><code>-lgdk_pixbuf_xlib</code>). That extra library causes build problems for w3m; you can fix it by editing <code>/opt/local/bin/gdk-pixbuf-config</code> and removing the <code>-lgdk_pixbuf</code> from what it prints out (however, if you use other software that uses gdk-pixbuf-config, you may need to put it back once w3m has finished building).</p>]]>
      
   </content>
</entry>
<entry>
   <title>Sorting Spaces</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/03/sorting_spaces.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1321</id>
   <created>2008-03-13T00:08:44Z</created>
   <issued>2008-03-13T00:08:44Z</issued>
   <modified>2008-03-13T01:48:03Z</modified>
   
   <summary type="text/plain">There seems to be some disagreement, at Apple Computer, about exactly what the definition of the word &amp;#8220;ignore&amp;#8221; is. From the &amp;#8220;sort&amp;#8221; man page: -d Sort in `phone directory&amp;#8217; order: ignore all characters except letters, digits and blanks when sorting....</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Commentary" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>There seems to be some disagreement, at Apple Computer, about exactly what the definition of the word &#8220;ignore&#8221; is. From the &#8220;sort&#8221; man page:</p>

<blockquote><p> -d     Sort  in  `phone  directory&#8217; order: ignore all characters except letters, digits and blanks when sorting.</p></blockquote>

<p>What does that suggest to you? Well, let&#8217;s compare it to the <span class="caps">GNU </span>&#8220;sort&#8221; man page:</p>

<blockquote><p>-d, &#8212;dictionary-order<br />
              consider only blanks and alphanumeric characters</p></blockquote>

<p>So you&#8217;d <strong><span class="caps">THINK</span></strong>, right, that sorting with these two options would be equivalent, right?</p>

<p>Nope!</p>

<p>Here&#8217;s a simple list:</p>

<p>- 192.168.2.4 foo<br />
- 192.168.2.42 foo</p>

<p>How should these things be sorted when the -d option is in effect? You&#8217;ve got a conundrum: is a space sorted <span class="caps">BEFORE </span>a number or <span class="caps">AFTER </span>a number?</p>

<p>Curse you, alphabet! You&#8217;re never around when I need you!</p>

<p>And, of course, <span class="caps">BSD </span>and <span class="caps">GNU </span>answer that question differently. On <span class="caps">GNU, </span>the answer is <span class="caps">AFTER, </span>on <span class="caps">BSD </span>the answer is <span class="caps">BEFORE</span>! Oh goody.</p>

<p>Here&#8217;s a better way if you need the sorting results to be the same on both <span class="caps">BSD </span>and <span class="caps">GNU</span>: replace all spaces with something else non-alpha-numeric that isn&#8217;t used in the file (such as an underscore, or an ellipsis, or an em-dash). Then sort with -ds (no last-minute saving throws!), then replace the underscore (or whatever) with a space again.</p>

<p>And if you need it to be consistent on <span class="caps">OSX </span>platforms too, make it a -dfs sort (so that capitals and lower-case are considered the same).</p>]]>
      
   </content>
</entry>
<entry>
   <title>Apple&apos;s Compiler Idiocy</title>
   <link rel="alternate" type="text/html" href="http://www.memoryhole.net/kyle/2008/01/apples_compiler_idiocy.html" />
   <id>tag:www.memoryhole.net,2008:/kyle/6.1308</id>
   <created>2008-01-11T22:30:29Z</created>
   <issued>2008-01-11T22:30:29Z</issued>
   <modified>2008-01-12T01:42:09Z</modified>
   
   <summary type="text/plain">This is something that&amp;#8217;s been bugging me for a while here, and I might as well write it down since I finally found a solution. I have an atomic-increment function. To make it actually atomic, it uses assembly. Here&amp;#8217;s the...</summary>
   <author>
      <name>kyle</name>
      
   </author>
         <category term="Research" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="text/html" mode="escaped" xml:lang="en" xml:base="http://www.memoryhole.net/kyle/">
      <![CDATA[<p>This is something that&#8217;s been bugging me for a while here, and I might as well write it down since I finally found a solution.</p>

<p>I have an atomic-increment function. To make it actually atomic, it uses assembly. Here&#8217;s the <span class="caps">PPC </span>version:</p>

<pre><code language="cc">static inline int atomic_inc(int * operand)
{
    int retval;
    register unsigned int incrd = incrd; // silence initialization complaints
    asm volatile (&quot;1:\n\t&quot;
                  &quot;lwarx  %0,0,%1\n\t&quot; /* reserve operand into retval */
                  &quot;addi   %2,%0,1\n\t&quot; /* increment */
                  &quot;stwcx. %2,0,%1\n\t&quot; /* un-reserve operand */
                  &quot;bne-   1b\n\t&quot; /* if it failed, try again */
                  &quot;isync&quot; /* make sure it wasn't all just a dream */
                  :&quot;=&amp;r&quot; (retval)
                  :&quot;r&quot; (operand), &quot;r&quot; (incrd)
                  :&quot;cc&quot;,&quot;memory&quot;);
    return retval;
}</code></pre>

<p>Now, what exactly is wrong with that, eh? This works <strong>great</strong> on Linux. The general <span class="caps">GCC </span>compiles this just fine, as does the <span class="caps">PGI </span>compiler, <span class="caps">IBM&#8217;</span>s compiler, and Intel&#8217;s compiler.</p>

<p>Apple&#8217;s compiler? Here&#8217;s the error I get:</p>

<pre><code language="cc">gcc -c test.c
/var/tmp/ccqu2RmV.s:5949:Parameter error: r0 not allowed for parameter 2 (code as 0 not r0)</code></pre>

<p>Okay, so, some kind of monkey business is going on. What does this look like in the .S file?</p>

<pre><code language="cc">1:
    lwarx r0,0,r2
    addi   r3,r0,1
    stwcx. r3,0,r2
    bne-   1b
    isync
    mr r3,r0</code></pre>

<p>It decided (retval) was going to be r0! Even though that&#8217;s apparently not allowed! (FYI it&#8217;s the <code>addi</code> that generates the error).</p>

<p>The correct workaround is to use the <strong>barely</strong> documented &#8220;b&#8221; option, like this:</p>

<pre><code language="cc">static inline int atomic_inc(int * operand)
{
    int retval;
    register unsigned int incrd = incrd; // silence initialization complaints
    asm volatile (&quot;1:\n\t&quot;
                  &quot;lwarx  %0,0,%1\n\t&quot; /* reserve operand into retval */
                  &quot;addi   %2,%0,1\n\t&quot; /* increment */
                  &quot;stwcx. %2,0,%1\n\t&quot; /* un-reserve operand */
                  &quot;bne-   1b\n\t&quot; /* if it failed, try again */
                  &quot;isync&quot; /* make sure it wasn't all just a dream */
                  :&quot;=&amp;b&quot; (retval) /* note the b instead of the r */
                  :&quot;r&quot; (operand), &quot;r&quot; (incrd)
                  :&quot;cc&quot;,&quot;memory&quot;);
    return retval;
}</code></pre>

<p>That ensures, on <span class="caps">PPC </span>machines, that the value is a &#8220;base&#8221; register (aka not r0).</p>

<p>How gcc on Linux gets it right all the time, I have no idea. But it does.</p>]]>
      
   </content>
</entry>

</feed>
