Geoff's Site Geoff Greer 2017-07-21T18:08:34-07:00 Thinkpad X62 2017-07-16T22:26:25-07:00 <p>Back in January, I <a href="/2017/01/23/oldest-viable-laptop/">used an old Thinkpad</a> while my <a href="/2015/04/19/2015-macbook-review/">12” Macbook</a> was being repaired. I found myself really enjoying some aspects of it. This nudged me down a path that ended with me modding a frankenpad built by some enthusiasts in Shenzhen.</p> <p>Using my old X61s left me frustrated with modern laptops. Sure, the X61s was old and slow, but that was to be expected. What I didn’t expect was just how much modern laptops have regressed. Modern chiclet-style keyboards are abysmal compared to the keyboard on the X61s. I forgot how much I preferred 4:3 screens for work. Title bars and tabs tend to consume vertical space, which 4:3 screens have plenty of. The X61s screen was dim, but I fixed that with an <a href="">LED backlight conversion kit</a>.</p> <p style="text-align: center; font-size: 80%;"> <a href="/photos/x62/DSC_2305.JPG"><img alt="Dear Lenovo: Please bring back the classic keyboard" src="/photos/x62/DSC_2305.JPG" /></a> <br /> Dear Lenovo: Please bring back this classic keyboard </p> <p>Some features were nice to have, but not particularly compelling: I liked the form factor, though it was a bit thick. The removable battery came in handy a few times. And I enjoyed little details like the latching lid and thin bezel.</p> <p>Of course, some aspects of the X61s were embarassing by modern standards. The screen resolution was only 1024×768, and not <a href="">IPS</a>, meaning colors changed with viewing angle. The VGA-out restricted possibilities for external displays. Performance and battery life were… less than ideal. Even a maxxed-out machine (1.8GHz Core 2 Duo, 8GB of RAM, SATAII SSD) was a little sluggish for work.</p> <p>Despite its shortcomings, I ended up using the X61s more than my 12” MacBook. The X61s convinced me that a much better laptop could exist. The same chassis with modern components would be a very compelling product.</p> <h3 id="the-x62">The X62</h3> <p>In the quest for something better, I stumbled upon the X62. This “model” isn’t made by Lenovo. It’s the product of <a href="">51nb</a>, a group of enthusiasts in Shenzhen. The X62 is an X61 chassis but with:</p> <ul> <li>A 12” 1400×1050 IPS LCD (likely salvaged from an X60 tablet).</li> <li>An Intel <a href="">Core i7-5600U</a> (Broadwell. Dual core. Turbo boost up to 3.2GHz.)</li> <li>Up to 32GB of RAM.</li> <li>Mini DisplayPort &amp; mini-HDMI out.</li> <li>802.11ac, Bluetooth 4, USB 3, SD card reader, Gigabit Ethernet.</li> <li>SATA3 &amp; mSATA.</li> </ul> <p>I ordered one back in March and received it in June. As with the X61s, I replaced the backlight with an LED kit. This improved battery life and brought the screen brightness from 160 nits to over 600. The backlight mod took hours, as it involved disassembling most of the laptop.</p> <p><img src="/photos/x62/IMG_1163.jpg" alt="ThinkPad X62 disassembled. I'm so glad I only have to do this once." /></p> <p>You can see more pictures of the process <a href="/photos/x62">here</a>.</p> <h3 id="linux">Linux</h3> <p>Ubuntu 17.04 worked out of the box, but there was one annoyance: battery life. For some reason, the laptop was using 10 watts at idle. Thanks to <a href="">some forum posts</a>, I managed to tweak various kernel module options and got idle down to 4 watts. This drastically improved battery life. I then put together a script to enable power savings on startup. These tweaks should benefit most Linux users on modern Intel hardware:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span><span class="ch">#!/bin/sh</span> <span class="c1"># Disable the NMI watchdog</span> <span class="nb">echo</span> <span class="s1">&#39;0&#39;</span> &gt; <span class="s1">&#39;/proc/sys/kernel/nmi_watchdog&#39;</span><span class="p">;</span> <span class="c1"># Runtime power management for I2C devices</span> <span class="k">for</span> i in /sys/bus/i2c/devices/*/device/power/control <span class="p">;</span> <span class="k">do</span> <span class="nb">echo</span> auto &gt; <span class="si">${</span><span class="nv">i</span><span class="si">}</span> <span class="k">done</span> <span class="c1"># Runtime power-management for PCI devices</span> <span class="k">for</span> i in /sys/bus/pci/devices/*/power/control <span class="p">;</span> <span class="k">do</span> <span class="nb">echo</span> auto &gt; <span class="si">${</span><span class="nv">i</span><span class="si">}</span> <span class="k">done</span> <span class="c1"># Runtime power-management for USB devices</span> <span class="k">for</span> i in /sys/bus/usb/devices/*/power/control <span class="p">;</span> <span class="k">do</span> <span class="nb">echo</span> auto &gt; <span class="si">${</span><span class="nv">i</span><span class="si">}</span> <span class="k">done</span> <span class="c1"># Low power SATA</span> <span class="k">for</span> i in /sys/class/scsi_host/*/link_power_management_policy <span class="p">;</span> <span class="k">do</span> <span class="nb">echo</span> min_power &gt; <span class="si">${</span><span class="nv">i</span><span class="si">}</span> <span class="k">done</span> <span class="c1"># Disable Wake-on-LAN on ethernet port</span> ethtool -s wlan0 wol d<span class="p">;</span> ethtool -s eth0 wol d <span class="c1">#Enable Audio codec power management</span> <span class="nb">echo</span> <span class="s1">&#39;1&#39;</span> &gt; <span class="s1">&#39;/sys/module/snd_hda_intel/parameters/power_save&#39;</span><span class="p">;</span> <span class="c1"># Low power wireless</span> iw dev wlan0 <span class="nb">set</span> power_save on</code></pre></figure> <p>I also had to use Realtek’s driver for the ethernet controller, as the default one wasted power. A <code>sudo apt install r8168-dkms</code> and a module blacklist later, I was in business. With these tweaks, my X62 idles in power state PC6. It’s possible to go lower, but I’m content with the current battery life.</p> <p>I wish Linux had better defaults, but this sort of thing is to be expected when it comes to open source software.</p> <h2 id="conclusion">Conclusion</h2> <p><a href="/photos/x62/DSC_2304.JPG"><img src="/photos/x62/DSC_2304.JPG" alt="ThinkPad X62" /></a></p> <p>The X62 is a niche product, but it really scratches an itch for me. There’s no other way to get a 4:3 matte screen, a great keyboard, and modern performance. My MacBook has become my secondary laptop. I’ve only taken it out of the house once since I got the X62, and that was for my Oregon bikepacking trip.</p> GNOME Terminal Antialiasing Saga 2017-06-14T19:49:46-07:00 <p><a href="/2016/08/26/gnome-terminal-cursor-blinking-saga/">Previously</a>.</p> <p>On lower-DPI displays, <a href="/2013/12/24/programming-fonts/">I like to use bitmap fonts</a> in my terminals and text editors. I was surprised to see that Gnome Terminal still <a href="">antialiased</a> and <a href="">hinted</a> my bitmap font:</p> <div style="text-align: center;"> <img alt="Gnome terminal making stuff blurry" src="/images/Screenshot from 2017-06-11 20-04-00.png" style="width: 471px; height: 239px;" /> </div> <p>Here’s a close-up:</p> <div style="text-align: center;"> <img alt="Enlargement of antialiasing" src="/images/Screenshot from 2017-06-11 20-04-00-crop.png" style="width: 450px; height: 135px; image-rendering: pixelated;" /> </div> <p>The red text is obvious, but you can also see tinges of blue and red on the white text. Not pretty.</p> <p>Other platforms make it easy to get the desired behavior. Both and PuTTY have checkboxes to disable antialiasing and subpixel hinting. But (as anyone familiar with Gnome would expect) Gnome Terminal has no GUI setting to change this.</p> <p>To solve the problem, I delved into <a href="">fontconfig</a>. My goal was to disable antialiasing for just one font. I created <code>~/.fonts.conf</code> and filled it with some guesses based on docs and related Stack Overflow answers:</p> <figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span></span><span class="cp">&lt;?xml version=&#39;1.0&#39;?&gt;</span> <span class="cp">&lt;!DOCTYPE fontconfig SYSTEM &#39;fonts.dtd&#39;&gt;</span> <span class="nt">&lt;fontconfig&gt;</span> <span class="nt">&lt;match</span> <span class="na">target=</span><span class="s">&quot;pattern&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;test</span> <span class="na">name=</span><span class="s">&quot;family&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;string&gt;</span>ProggyTinyTTSZ<span class="nt">&lt;/string&gt;</span> <span class="nt">&lt;/test&gt;</span> <span class="nt">&lt;edit</span> <span class="na">mode=</span><span class="s">&quot;assign&quot;</span> <span class="na">name=</span><span class="s">&quot;antialias&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;bool&gt;</span>false<span class="nt">&lt;/bool&gt;</span> <span class="nt">&lt;/edit&gt;</span> <span class="nt">&lt;edit</span> <span class="na">mode=</span><span class="s">&quot;assign&quot;</span> <span class="na">name=</span><span class="s">&quot;hinting&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;bool&gt;</span>false<span class="nt">&lt;/bool&gt;</span> <span class="nt">&lt;/edit&gt;</span> <span class="nt">&lt;/match&gt;</span> <span class="nt">&lt;/fontconfig&gt;</span></code></pre></figure> <p>This had no effect. I re-read some fontconfig docs and noticed that <code>~/.fonts.conf</code> had been deprecated for a new config path: <code>~/.config/fontconfig/fonts.conf</code>. I moved the config file. Still no change.</p> <p>After reading <em>more</em> fontconfig docs, I found out about <code>FC_DEBUG</code>:</p> <blockquote> <p>To help diagnose font and applications problems, fontconfig is built with a large amount of internal debugging left enabled. It is controlled by means of the FC_DEBUG environment variable.</p> </blockquote> <p>Surely, debug logging would give me some clues. Naturally, this variable wasn’t just a boolean:</p> <blockquote> <p>The value of this variable is interpreted as a number, and each bit within that value controls different debugging messages.</p> </blockquote> <p>I tried setting <code>FC_DEBUG</code> to 35 (0b0100011), corresponding to verbose info for caching &amp; matching. It turns out that verbose logging lives up to its name:</p> <pre><code>ggreer@zinc:~% FC_DEBUG=35 fc-match -v ProggyTinyTTSZ family antialias hinting file | wc -l 29771 </code></pre> <p>That’s right: almost 30,000 lines of debug logs— almost as useless as no logs. I played with other values, eventually settling on <code>FC_DEBUG=4</code>. That generated “only” 5,000 lines and seemed to contain some useful clues.</p> <p>The first thing I noticed when scouring the debug logs was that many settings were being processed after my user config. I traced this down to the order of config files in <code>/etc/fonts/conf.d/</code>. The rule to load user configs was in the middle, not at the last thing done. To ensure no global config was overriding my directives, I ran <code>sudo mv 50-user.conf 99-user.conf</code>. Debug logs then confirmed that my rules were run last.</p> <p>Still, I saw blurry text.</p> <p>At this point, I’d been at it for almost two hours. I was getting pretty frustrated. With nothing better coming to mind, I stared at my XML. I tediously compared it against examples in the fontconfig docs. I saw one suspicious line: I was using <code>&lt;match target="pattern"&gt;</code>, while many examples used <code>&lt;match target="font"&gt;</code>. I changed the target attribute from “pattern” to “font”:</p> <figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span></span><span class="cp">&lt;?xml version=&#39;1.0&#39;?&gt;</span> <span class="cp">&lt;!DOCTYPE fontconfig SYSTEM &#39;fonts.dtd&#39;&gt;</span> <span class="nt">&lt;fontconfig&gt;</span> <span class="nt">&lt;match</span> <span class="na">target=</span><span class="s">&quot;font&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;test</span> <span class="na">name=</span><span class="s">&quot;family&quot;</span> <span class="na">qual=</span><span class="s">&quot;any&quot;</span> <span class="na">compare=</span><span class="s">&quot;eq&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;string&gt;</span>ProggyTinyTTSZ<span class="nt">&lt;/string&gt;</span> <span class="nt">&lt;/test&gt;</span> <span class="nt">&lt;edit</span> <span class="na">mode=</span><span class="s">&quot;assign&quot;</span> <span class="na">name=</span><span class="s">&quot;antialias&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;bool&gt;</span>false<span class="nt">&lt;/bool&gt;</span> <span class="nt">&lt;/edit&gt;</span> <span class="nt">&lt;edit</span> <span class="na">mode=</span><span class="s">&quot;assign&quot;</span> <span class="na">name=</span><span class="s">&quot;hinting&quot;</span><span class="nt">&gt;</span> <span class="nt">&lt;bool&gt;</span>false<span class="nt">&lt;/bool&gt;</span> <span class="nt">&lt;/edit&gt;</span> <span class="nt">&lt;/match&gt;</span> <span class="nt">&lt;/fontconfig&gt;</span></code></pre></figure> <p>…and was finally rewarded with pixel-perfect text:</p> <div style="text-align: center;"> <img alt="Gnome terminal with correctly rendered fonts" src="/images/Screenshot from 2017-06-11 20-02-44.png" style="width: 471px; height: 239px;" /> </div> <p>A close-up:</p> <div style="text-align: center;"> <img alt="Enlargement of no antialiasing" src="/images/Screenshot from 2017-06-11 20-02-44-crop.png" style="width: 450px; height: 135px; image-rendering: pixelated;" /> </div> <p>Finally!</p> <p>This is yet more evidence of how ridiculously hostile Gnome is to users. I’m no novice, but it took me two hours to accomplish what takes two seconds on every other platform. What a waste.</p> Software Rot 2017-02-28T17:59:53-08:00 <p>In his book <a href="/2016/07/23/age-of-em/"><em>Age of Em: Work, Love and Life when Robots Rule the Earth</em></a>, Robin Hanson briefly discusses software rot:</p> <blockquote> <p>As software that was designed to match one set of tasks, tools, and situations is slowly changed to deal with a steady stream of new tasks, tools, and situations, such software becomes more complex, fragile, and more difficult to usefully change (Lehman and Belady 1985)<sup id="fnref:Lehman"><a href="#fn:Lehman" class="footnote">1</a></sup>. Eventually it is better to start over and write whole new subsystems, and sometimes whole new systems, from scratch.</p> </blockquote> <p>I’m pretty sure this is true. Adapting mature software to new circumstances tends to take more time and effort than writing new software from scratch. Software people don’t like to admit this, but the evidence is clear. Open source software has several high-profile examples.</p> <h2 id="multi-process-firefox">Multi-process Firefox</h2> <p>When it was first written, <a href="">Mozilla Firefox</a> ran everything in a single process. After the release of <a href="">Google Chrome</a>, it was clear that a multi-process model allowed for better security and performance. Mozilla developers soon started planning to make Firefox multi-process. That was in 2007.</p> <p>Almost a decade later, Mozilla finally <a href="">began rollout of multi-process Firefox</a>. This delay is not for want of trying. The teams at Mozilla are talented and driven. Still, Chrome was written from scratch in far less time than it has taken Firefox to change. There are two main reasons for this:</p> <ul> <li>Making a single process architecture multi-process means changing a <em>lot</em> of small things. Certain function calls have to be replaced with inter-process communication. Shared state must be wrapped in mutexes. Caches and local databases must handle concurrent access.</li> <li>Firefox needed to remain compatible with existing add-ons (or force devs to update their add-ons). Chrome got to create an extention API from scratch, avoiding such constraints.</li> </ul> <p>It gets worse. These constraints are at odds with each other: Overhaul the internal architecture, but alter public-facing APIs as little as possible. It’s no wonder Mozilla needed 10 years to accomplish this feat.</p> <h2 id="event-driven-apache">Event-driven Apache</h2> <p>When <a href="">Apache httpd</a> was first written, it used a process-per-connection model. One process would listen on port 80, then <code>accept()</code> and <code>fork()</code>. The child process would then <code>read()</code> and <code>write()</code> on the socket. When the request was finished, the child would <code>close()</code> the socket and <code>exit()</code>.</p> <p>This architecture had the advantage of being simple, easy to implement on many platforms, and… not much else. It was absolutely terrible for performance, especially when handling long-lived connections. To be fair: this <em>was</em> 1995. And Apache soon moved to a threaded model, which did help performance. Still, it couldn’t handle <a href="">10,000 simultaneous connections</a>. A connection-per-thread architecture takes 1,000 threads to service 1,000 concurrent connections. Each thread has its own stack and state, and must be scheduled by the operating system. It makes for a bad time.</p> <p>In contrast, <a href="">Nginx</a> used a <a href="">reactor pattern</a> from the start. This allowed it to handle more concurrent connections and rendered it immune to <a href="">slowloris attacks</a>.</p> <p>Nginx was first released in 2007, and its performance advantage was apparent. Years before the release of Nginx, the Apache devs had begun re-architecting httpd to perform better. The <a href="">event MPM</a> shipped with Apache 2.2 in 2005. Still, there were teething issues. Most importantly, the event MPM broke compatibility with popular modules like mod_php. It wasn’t until 2012 that Apache 2.4 shipped with it as the default.<sup id="fnref:httpd"><a href="#fn:httpd" class="footnote">2</a></sup> While far better than the previous <a href="">prefork</a> and <a href="">worker MPM</a>s, the worker MPM didn’t acheive parity with Nginx. Instead, it used separate thread pools for listening/accepting connections and processing requests. The architecture is roughly equivalent to running a load balancer or reverse proxy in front of a worker MPM httpd.<sup id="fnref:httpd2"><a href="#fn:httpd2" class="footnote">3</a></sup></p> <h2 id="cpython-gil">CPython GIL</h2> <p>Python is a nice programming language. It’s expressive, easy to learn (at least as programming languages go), and it’s supported on a wide variety of platforms. But for the past two decades, the most popular implementation of Python has had one major problem: it can’t easily take advantage of multiple CPU cores.</p> <p>The cause of Python’s lack of parallelism is its global interpreter lock, or GIL. From <a href="">the Python wiki</a>:</p> <blockquote> <p>In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)</p> </blockquote> <p>Originally, the GIL wasn’t a big deal. When Python was created, multi-core systems were rare. And a GIL is simple to write and easy to reason about. But today, even wristwatches have multi-core CPUs. The GIL is an obvious and glaring defect in what is otherwise a pleasant language. Despite CPython’s popularity, despite the project’s capable developers, despite sponsors such as Google, Microsoft, and Intel, <a href="">fixing the GIL isn’t even on the roadmap</a>.</p> <h2 id="conclusion">Conclusion</h2> <p>Even when given talented engineers, plenty of money, and clear vision, mature software can be extremely difficult to change. I tried to find cases that disproved software rot, but they don’t seem to exist. Robin Hanson <a href="">asked for counterexamples</a> and nobody came up with anything convincing. There are plenty of old software projects, but they haven’t had to adapt much. I’d love to find good counterexamples, as the current evidence paints a bleak picture for the long-term future of software.</p> <hr /> <h3 id="read-more">Read more</h3> <ul> <li><a href="">Overcoming Bias: Why Does Software Rot?</a></li> <li><a href="">Suprise: Software rots!</a></li> <li><a href="">Wikipedia: Software rot</a></li> </ul> <hr /> <div class="footnotes"> <ol> <li id="fn:Lehman"> <p>The cite is for a text called <em>Program Evolution: Processes of Software Change</em>. The work is older than me, and I can’t find an online version. I bought a physical copy and have been slowly making my way through it. The terminology is odd, but the conclusions haven’t been particularly surprising.&nbsp;<a href="#fnref:Lehman" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:httpd"> <p>The original version of this blog post contained some misapprehensions about the timeline. Thanks goes to <a href="">Paul Querna</a> for correcting them.&nbsp;<a href="#fnref:httpd" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:httpd2"> <p>Anyone who knows about httpd’s internals will take issue with this sentence. The comparison sacrifices accuracy for brevity. I apologize.&nbsp;<a href="#fnref:httpd2" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> Oldest Viable Laptop 2017-01-23T18:04:30-08:00 <p>What’s the oldest laptop you could reasonably do your job with? 3 years old? 5 years? 10? And if asked the same question 10 years ago, would your number be higher or lower? Thanks to a failing battery in <a href="/2015/04/19/2015-macbook-review/">my 12” MacBook</a>, I discovered my answer.</p> <p>For the 10 days it took to repair my MacBook, I had to use my backup laptop:</p> <p><img src="/photos/pics/thinkpad_x61s.jpg" alt="ThinkPad X61s" /></p> <p>This is a <a href="">ThinkPad X61s</a>. Despite being made in 2007, it’s been fine for work. Yes, everything about it is worse than my MacBook. It’s slower and heavier. It lacks a trackpad. The screen is a mere 1024x768, causing some websites to show their mobile layout.)<sup id="fnref:font"><a href="#fn:font" class="footnote">1</a></sup> Still, the experience has been significantly better than I predicted. The only major hardware drawback is the lack of video camera. The main sources of frustration have been software. Ubuntu 16.04 doesn’t have a built-in dictionary or thesaurus. The default calendar app is a joke. <del>And Thunderbird doesn’t have a unified inbox view.</del> (Edit: <a href="">It does</a>. Thanks To Brendan Long for pointing this out.)</p> <p>Had a similar circumstance happened 10 years ago, my oldest viable laptop would not be so old. That is to say: There’s no way that in 2007, I’d be able to get by with a laptop from 1997. The performance issues would be insurmountable.</p> <p>Growing up, I never thought I’d be able to use decade-old hardware without issue. Either laptop improvements are well into diminishing returns, or progress in hardware has stagnated, or both.</p> <p>Update: It’s been two weeks since I got my MacBook back, and I still tend to use my ThinkPad more. I’m not sure if I’ll stick with it, but there’s something about this machine that causes me to favor it.</p> <hr /> <div class="footnotes"> <ol> <li id="fn:font"> <p>My terminal and editor windows had plenty of space thanks to <a href="/2013/12/24/programming-fonts/">pixel-perfect programming fonts</a>.&nbsp;<a href="#fnref:font" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> Ag & Ripgrep: .ignore 2016-09-26T14:12:57-07:00 <p>A few days ago, <a href="">Andrew Gallant</a> (AKA BurntSushi) released <a href="">ripgrep</a>, a search tool similar to to <a href="/ag">ag</a>. To coincide with the initial release, Gallant wrote <a href="">a blog post comparing various search tools</a>. In it, he discusses both the advantages and shortcomings of a half-dozen tools, including ag. He finds bugs, pathological performance cases, and unexpected ways in which these tools can be sped-up. If the topic even remotely interests you, read Gallant’s blog post.</p> <p>When Gallant’s post <a href="">was discussed on Hacker News</a>, I <a href="">replied</a> with my thoughts. This spurred a pleasant and productive discussion. I noticed that both of our tools had ignore files (<code>.agignore</code> and <code>.rgignore</code>), so <a href="">I suggested we converge on a common name and format</a>. We quickly agreed on <code>.ignore</code> (surprisingly, it wasn’t taken). Within a couple of hours, both of us had added the feature into our respective projects.<sup id="fnref:ag"><a href="#fn:ag" class="footnote">1</a></sup><sup id="fnref:rg"><a href="#fn:rg" class="footnote">2</a></sup> The author of <a href="">sift</a> also <a href="">seems to be on board</a>.</p> <p>Support for <code>.ignore</code> files is in ag v0.33.0 and later. If you have <code>.agignore</code> files, don’t worry. You’ll have plenty of time to rename them. It will be at least six months before I deprecate them, and probably a year before they’re no longer read.</p> <p>The whole experience was positive for everyone involved. Andrew and I had an enjoyable exchange. Users of our tools were saved some trouble. And there’s a decent chance that more software will support the same <code>.ignore</code> standard. I’m happy to have played a part in creating this small gem of collaboration.</p> <hr /> <div class="footnotes"> <ol> <li id="fn:ag"> <p><a href="">Ag pull request #974: Prefer .ignore to .agignore</a>&nbsp;<a href="#fnref:ag" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:rg"> <p><a href="">Ripgrep pull request #41: Switch from .rgignore to .ignore</a>&nbsp;<a href="#fnref:rg" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> GNOME Terminal Cursor Blinking Saga 2016-08-26T20:51:37-07:00 <p>When setting up a new computer, one of the first things I do is disable cursor blinking in the terminal. For the past decade, the Gnome team has worked diligently to make this as hard as possible.</p> <div style="text-align: center;"> <img alt="On. Off. On. Off. On. Off..." src="/images/gnome_terminal_cursor_blinking.gif" /> </div> <p>The story begins in 2006. Back then, Gnome Terminal had a checkbox in its settings. Here’s a screenshot from Ubuntu 6.06:</p> <p><img src="/images/gnome_terminal_ubuntu_6.png" alt="GNOME Terminal Settings in Ubuntu 6.06" /></p> <p>Unchecking “Cursor blinks” would stop the cursor from blinking. It was simple and accessible; exactly the kind of UI that has no place in a Gnome project. Some developers soon decided to “simplify” things by going, “…on a quest to remove all annoying tiny little bits in GNOME’s UI.” Instead of having a checkbox, they wanted the terminal to obey the global system setting (for cursors in text editors, browser URL bars, etc). Hence <a href="">Bug 342921 - Cursor blinking preference should follow system defaults</a>. The checkbox was removed in Gnome v2.22, meaning that the only way to disable blinking was to turn it off in the entire Gnome UI. This annoyed many people, as a giant blinking block is far more prominent than a thin blinking line. Users quickly responded. They created issues such as <a href="">Bug 533522 - Alllow override of system blink preference</a> and <a href="">Bug 534207 - Something to fix the cursor blinking problem soon</a>. Gnome was going through a UI freeze at the time, so the “fix” was to create a gconf setting. Now to disable terminal blinking, you had to run:</p> <pre><code>gconftool-2 --set "/apps/gnome-terminal/profiles/Default/cursor_blink" \ --type boolean "False" </code></pre> <p>Good luck figuring that out on your own. Fortunately, the magic invocation was quickly documented on sites such as Stack Overflow. It even made its way to <a href="">a page dedicated to stopping cursor blinks</a>. Things stayed that way for five years.</p> <p>Then Gnome 3 came out.</p> <p>The old magic invocation no longer appeased the Gnome gods. This issue was created: <a href="">Bug 702901 - Disabling blinking cursor not working</a>. The reason for the breakage was twofold. First: Gnome switched from gconf to dconf. Second: Gnome devs changed the config schema for terminal settings. In Gnome 2, each terminal profile was stored by name. This allowed for paths like <code>/profiles/Default/</code> to work across systems. Gnome 3 stored profiles by UUID. Since UUIDs tend to be rather <em>unique</em>, there isn’t a standard default key. One first has to get the UUID of the default profile, then set the appropriate key under it. Like so:</p> <pre><code>% gsettings get org.gnome.Terminal.ProfilesList default 'b1dcc9dd-5262-4d8d-a863-c897e6d979b9' % gsettings set org.gnome.Terminal.Legacy.Profile:/org/gnome/terminal/legacy/profiles:/:b1dcc9dd-5262-4d8d-a863-c897e6d979b9/ cursor-blink-mode off % </code></pre> <p>To make it a one-liner, you have to use a subshell:</p> <pre><code>gsettings set org.gnome.Terminal.Legacy.Profile:/org/gnome/terminal/legacy/profiles:/:$(gsettings get org.gnome.Terminal.ProfilesList default | tr -d \')/ cursor-blink-mode off </code></pre> <p>Annoyingly, this information isn’t in Gnome’s answer to <a href=";redirect=Terminal%2FFAQ#How_can_I_stop_the_cursor_from_blinking.3F">How can I stop the cursor from blinking?</a>. Instead, they link to another answer, which contains this gem:</p> <blockquote> <p>Unfortunately, the gsettings tool can’t currently autocomplete the key names with relocatable schemas (that’s <a href="">this bug</a>), but you can just <a href="">read the schema itself</a>.</p> </blockquote> <p>I cannot recall encountering a more user-hostile experience. How many people are going to read a <em>700 line XML file</em> to tweak their terminal? It’s absurd. There are two possible explanations for such a terrible experience: Either the Gnome developers are indifferent to their users, or they are incompetent. Actually, it could be worse. It could be both.</p> <h3 id="conclusion">Conclusion</h3> <p>The issue to re-add the blink checkbox still exists: <a href="">Bug 559990 - Add UI for the cursor blink preference</a>. I doubt it will be fixed any time soon. In case you were curious: OS X has a non-blinking cursor by default. Changing it requires checking a box in the settings menu:</p> <p><img src="/images/Screen Shot 2016-08-29 at 18.35.49.png" alt="OS X settings" /></p> <p>…just like Gnome had 10 years ago.</p> <p>Desktop Linux is fraught with these sorts of issues. The only reason I use it is because I need to test software on the platform. If not for that, I doubt I’d ever touch a Linux GUI again.</p> <p>Update: <a href="/2017/06/14/gnome-terminal-antialiasing-saga/">the saga continues</a>.</p> Age of Em 2016-07-23T11:21:48-07:00 <p>Back in June, I read <a href="">Robin Hanson</a>’s book: <a href=""><em>Age of Em: Work, Love and Life when Robots Rule the Earth</em></a>. I found it fascinating. Two months later, the book’s ideas still pop into my mind daily. Nothing else I’ve read in the past year has done that. This post is a summary of the core ideas in <em>Age of Em</em>, followed by my observations and critiques.</p> <h2 id="the-author">The Author</h2> <blockquote> <p>I expect my analysis to be relevant for a large cloud of different but similar scenarios. In particular, conditional on my key assumptions, I expect at least 30% of future situations to be usefully informed by my analysis. Unconditionally, I expect at least 10%.<br /> – Robin Hanson, <em>Age of Em</em></p> </blockquote> <p>Let me make my bias known: I am a fan of Robin Hanson. He possesses a rare combination of intelligence, expertise, creativity, and civility. Moreover, he makes exceptional use of these gifts. Hanson looks at the big picture– the <em>really</em> big picture. For example: When examining <a href="">the growth of humanity over millions of years</a>, the invention of writing is a rounding error. At such a high-level view, all of human history can be described by three eras of growth<sup id="fnref:eras"><a href="#fn:eras" class="footnote">1</a></sup>:</p> <ol> <li>2,000,000 BC to 5,000 BC: Foraging, in which population doubled every 250,000 years.</li> <li>5,000 BC to 1600 AD: Farming, in which population doubled every 1,000 years.</li> <li>1600 AD to present: Industry, in which GDP doubled every 15 years.</li> </ol> <p>These numbers are approximate, of course. Hanson also notes that each transition to the next era took less than a doubling time of the previous era. E.g. industry replaced farming as the dominant growth mode in less than the doubling time of farming (1,000 years).</p> <p>If one extrapolates the numerological trend (admittedly, an absurdly weak argument), the next era after ours would have an economic doubling time on the order of months or weeks. This sounds crazy. How could a 200x speedup in economic growth <em>possibly</em> happen? What would such a world look like? <em>Age of Em</em> is Hanson’s attempt to answer these questions.</p> <h2 id="the-idea">The Idea</h2> <p>Today, economic growth is bottlenecked by people. Machines can be mass-produced, but people take decades to mature and learn skills. It’s possible to make more machines per worker, but one quickly runs into diminishing returns. Someone with 10 laptops or 10 bulldozers won’t be 10x as productive.<sup id="fnref:bottleneck"><a href="#fn:bottleneck" class="footnote">2</a></sup> To speed-up economic growth by 200x, the bottleneck must be removed. Something must substitute for human intelligence.</p> <p>When considering substitutes for human intelligence, most people’s thoughts turn to artificial intelligence. But Hanson is pessimistic about the arrival of de-novo AI. He thinks it is more likely that <a href="">brain emulations</a> will come first.<sup id="fnref:ai"><a href="#fn:ai" class="footnote">3</a></sup> (The “em” in <em>Age of Em</em> is short for “emulation”.) Creating such ems requires three key technologies, and none of them exist today:</p> <ol> <li>Fast, cheap computers.</li> <li>High-resolution scans of one or more human brains.</li> <li>Accurate models of all relevant cell types in the brain.</li> </ol> <p>Hanson doesn’t give any concrete timeline for when these technologies will exist. He only says “some time in the next century”. Fortunately, none of his analysis depends on when ems become feasible, just that they arrive before AI.</p> <p>Once ems are made, they’ll differ from us in several ways:</p> <ol> <li>Ems are immortal…-ish. So long as their hardware is maintained and powered, they can live indefinitely. This isn’t as great as it sounds. Today’s cars and houses are just as “immortal”.</li> <li>Ems can run at different speeds. Faster ems cost more to run, similar to how faster cloud servers cost more.</li> <li>Ems can “teleport” by transmitting their mind-state across a network. If latency is low enough, they need only to transmit their input and output signals.</li> <li><strong>Ems can be copied.</strong> Many consequences stem from this one difference.</li> </ol> <p>What makes <em>Age of Em</em> interesting is that it rigorously explores the consequences of ems, and that it does so by applying standard scientific models. There’s no hand-waving or contrarianism. The only unusual thing is the question being asked: “What would things be like in an em world?”</p> <h2 id="my-observations">My Observations</h2> <blockquote> <p>Seen up close and honestly, I expect the future usually to look like most places: mundane, uninspiring, and morally ambiguous, with grand hopes and justifications often masking lives of quiet desperation. Of course, lives of quiet desperation can still be worth living.<br /> – Robin Hanson, <em>Age of Em</em></p> </blockquote> <p>Reading <em>Age of Em</em> opened my eyes to how sloppy most futurism is. The field is filled with incomplete analyses and moralizing about the present day. In contrast, <em>Age of Em</em> treats the future like a real place. To use language from <a href="">construal level theory</a>: It kept me thinking in near-mode. I was pleased by the broad range of topics covered. These included: thermodynamics, software engineering, reversible computing, surveillance, city planning, language, and charity. No matter one’s interests, there’s something to engage with.</p> <p>Two specific parts of the book captivated me. They were software-related, of course. One section described the life of a software engineer:</p> <blockquote> <p>For software engineering tasks where parallel software and tools suffice, and where the software doesn’t need to interact with slower physical systems, em software engineers could be productive even when sped up to the top cheap speed. This often makes it feasible to avoid the costs of coordinating across many engineers, by having a single engineer spend an entire subjective career creating a large software system. For an example, an engineer that spent a subjective century at mega-em speeds would complete this period in less than 1 objective hour. Thus when such a delay is acceptable, parallel software may be written by a single engineer taking a subjective career length.</p> </blockquote> <p>This scenario may seem absurd, but it’s a decent prediction of what will happen in a world where minds can be copied and run at different speeds. While <em>you</em> may not want to live such a life, there are some people who will choose it. Those minds will dominate the em economy.</p> <p>Another section briefly discussed software rot. That is: Adapting mature software to new circumstances requires more time and effort than creating new software from scratch. Open source software has <a href="/2017/02/28/software-rot/">tons of examples of this</a>.</p> <p>Hanson claims to be applying standard models and theories to his em scenario, but I don’t know enough social science to tell if that’s true. When the text entered my areas of expertise (computer science, software engineering), I found nothing objectionable. Also, I haven’t heard any domain experts disputing his claim, so I’d bet money that Hanson correctly applied standard theories to this scenario.</p> <p>I’m not sure if the author intended it, but the text works well as an ebook. There are plenty of references and links to related chapters, so I had no problem jumping around. Many footnotes contained URLs, making it easy to track down more info on conclusions I was more skeptical about. I wish more ebooks took advantage of the medium in these ways.</p> <h2 id="my-criticisms">My Criticisms</h2> <blockquote> <p>In sum, even though many critics have reasonable points, I still think the analysis in this book was worth the effort.<br /> – Robin Hanson, <em>Age of Em</em></p> </blockquote> <p>While the ideas fascinated me, the writing itself was rather matter-of-fact. It wasn’t as dry as an academic paper, but neither was it particularly compelling. This could be taken in a positive light. It means the book succeeds on its ideas alone, not just due to fancy writing. And it’s not all dry. The penultimate chapter –on policy recommendations– uses more emotionally stirring language.</p> <p>When it comes to <em>Age of Em</em>’s core ideas, I can’t find any major flaws. To dispute the book requires either rejecting Hanson’s core assumptions or rejecting widely-accepted scientific models. The book contains so much detail that it would be easy to nitpick, but such pedantry is tiresome to read and to write.</p> <p>In my opinion, the weakest part of <em>Age of Em</em> is the assumption that em minds won’t be very tweakable. To be fair, I think it’s a valid simplifying assumption. The book is a straightforward, first-cut analysis. Complicating the base assumptions would have made for an intractable project. Still, one should not be very confident about how tweakable brain emulations can be. And even if most tweaks are hard, there may exist a few easy tweaks which could create a world we find utterly abhorrent. I’d really like to know what neuroscience has to say about this.</p> <h2 id="conclusion">Conclusion</h2> <blockquote> <p>…many of your ancestors would be tempted to disown you, if they were told many things about you. While they’d be pleased and impressed by many of your features, other things about you might horrify them.<br /> – Robin Hanson, <em>Age of Em</em></p> </blockquote> <p>When examined closely, the world described in <em>Age of Em</em> doesn’t sound so bad to me. Ems will be smarter, healthier, and harder working than almost everyone today. They’ll live long and fulfilling lives. Compared to our civilization, theirs will be larger, more capable, and more robust.</p> <p>I think many detractors neglect that last adjective: robust. Existential risk is a crucial consideration. Hanson notes that it would be very hard to eradicate ems. Also, sped-up ems could monitor nanotech or AI experiments and react far more quickly than humans. Even if one thinks an em world is less desirable than ours, the reduction in existential risk may be worth it.</p> <p>Lastly: As positive as my review is, I must admit that <em>Age of Em</em> will only interest a small subset of people. Fortunately, most people reading this will qualify.</p> <hr /> <div class="footnotes"> <ol> <li id="fn:eras"> <p><a href="">Long-Term Growth As A Sequence of Exponential Modes</a>&nbsp;<a href="#fnref:eras" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:bottleneck"> <p>You might ask, “If people are the bottleneck for economic growth, and population doesn’t double every 15 years, then how does our economy do that?” The answer is that much of today’s economic growth comes from making <em>better</em> machines.&nbsp;<a href="#fnref:bottleneck" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:ai"> <p>Hanson backs this up with supporting evidence &amp; reasoning in the book. A shorter version is in a blog post titled <em><a href="">AI Progress Estimate</a></em>.&nbsp;<a href="#fnref:ai" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> Bikepacking: The Idaho Hot Springs Loop 2016-07-22T16:48:55-07:00 <p>I recently rode the Idaho Hot Springs loop with my dad &amp; my uncle. In nine days, we traveled 510 miles and climbed 37,000 feet. It was rather enjoyable.</p> <p>Read <a href="">my dad’s write-up</a> for all the details. You can also view <a href="/photos/idaho_hot_springs_loop_2016/">my photo gallery of the trip</a>.</p> <p>For those who care to see where we went, I tracked the entire route on Strava:</p> <ul> <li>Day 1: <a href="">McCall to Warm Lake</a></li> <li>Day 2: <a href="">Warm Lake to Stanley</a></li> <li>Day 3: <a href="">Stanley to Ketchum attempt 1. My dad’s derailleur broke.</a></li> <li>Day 3: <a href="">Stanley to Ketchum attempt 2. Started at 4:30PM.</a></li> <li>Day 4: <a href="">Ketchum to Featherville</a></li> <li>Day 5: <a href="">Featherville to Cottonwood campground</a></li> <li>Day 6: <a href="">Cottonwood campground to Idaho City</a></li> <li>Day 7: <a href="">Idaho City to Trail Creek campground</a></li> <li>Day 8: <a href="">Trail Creek campground to Cascade</a></li> <li>Day 9: <a href="">Cascade to McCall</a></li> </ul> <p>Lastly, this trip caused me to break <a href="/2015/01/07/burnout-is-in-the-mind/">my GitHub streak</a>. I brought <a href="/2015/04/19/2015-macbook-review/">my laptop</a>, but most nights I was too tired to do anything. At the end, my streak had been going for 1280 days. That’s 3 years, 6 months, and 3 days. Or put another way: over 10% of my life. I don’t know if I’ll ever exceed that, but I’ve already gotten back in the groove.</p> Interesting Tech that is Just Around the Corner 2016-05-14T17:36:23-07:00 <p>When talking about near-future technologies, much of the discussion surrounds stuff like self-driving cars and <a href="">CRISPR</a>. Rarely do people notice the more modest things that are both closer to fruition and more likely to be brought to market. Allow me to provide some examples.</p> <h2 id="vr-with-eye-tracking">VR with Eye Tracking</h2> <p>VR is becoming commercially feasible, but few have paid attention to how it can be improved with <a href="">eye-tracking</a>. It may seem like a minor addition, but it’s not just a joystick-like input. Eye-tracking allows for far more interesting VR experiences.</p> <p>Imagine this technology being used in a VR <a href="">Silent Hill</a> or <a href="">Resident Evil</a>. You put the headset on and start playing. You see something move in your peripheral vision, but by the time your eyes have <a href="">saccaded</a>, it’s gone. No matter how hard you try, you can’t get a good look at it. With only hints and sounds to go on, your imagination fills in the rest.</p> <p>Such a game would be –without a doubt– the scariest game <em>ever</em>. I can’t wait to play it.</p> <h2 id="paralympics">Paralympics</h2> <p>It’s likely that some Paralympic records will soon exceed their Olympic counterparts. At top speed, the best below-the-knee prosthetics are more efficient than natural legs.<sup><a href="#ref_1">[1]</a></sup> So why haven’t the records already fallen? Several reasons:</p> <ul> <li>Compared to able-bodied athletes, the talent pool of below-the-knee amputee athletes is miniscule. Oscar Pistorius was in the top 0.1% of that group. Usain Bolt is in the top 0.000001% of able-bodied athletes.</li> <li>Runners on prosthetics take longer to accelerate to top speed, harming their performance in shorter distance events. Unfortunately, the longest Paralympic event for below-the-knee amputees is 400m.</li> </ul> <p>So the technology is here, but it’s hard to predict exactly when a Paralympic athlete will hold an overall record. It depends on a sufficiently elite athlete emerging from the talent pool (effectively a random event) and the whims of the International Paralympic Committee. I’d put even money on it happening before or at Tokyo (2020).</p> <h2 id="implantable-blood-glucose-monitors">Implantable Blood Glucose Monitors</h2> <p>1 in 12 adults worldwide have diabetes.<sup><a href="#ref_2">[2]</a></sup> Sufferers can lose limbs or even go blind. With proper blood glucose monitoring, many of these maladies can be avoided. Today, this requires pricking the skin to get a blood sample. It’s really hard to get people to cut themselves a half-dozen times a day, especially when the consequences of poor blood glucose management are years or decades away. Even then, 5-6 measurements per day doesn’t allow for very accurate management. Implantable glucose monitors solve these problems.</p> <p>Several companies have such devices in trials. It’s likely that they’ll be publicly available in a few years.</p> <h2 id="conclusion">Conclusion</h2> <p>There are so many inventions that will improve our lives. Most of them won’t make the news. Most of them –when held in isolation– won’t fundamentally change the human condition. But they gradually accumulate, and eventually we look back and think, “Wow, stuff sure was primitive back in the day.”</p> <p>Don’t forget that.</p> <hr /> <ol> <li> <p><span id="ref_1"></span> <a href="">Wikipedia: The Mechanics of Oscar Pistorius’ running blades</a></p> </li> <li> <p><span id="ref_2"></span> <a href="">Wikipedia: Epidemiology of diabetes mellitus</a></p> </li> </ol> Warrantless Fingerprinting 2016-03-18T23:24:34-07:00 <p>Imagine a world in which the police needed a warrant to dust for latent fingerprints. That is, after a crime is discovered or reported, the police have to go to a judge and demonstrate probable cause that person X is involved. If the judge agrees, a fingerprint warrant is issued. Only then can the police dust the crime scene for fingerprints, and the only information they’re allowed to use is whether X’s fingerprints are present.</p> <p>It would be a world much like this one, except criminals would be caught less often.</p> <p>Now imagine living in that world and trying to convince others that the country would be better-off if police <em>didn’t</em> need a warrant to dust for fingerprints. Yes, you admit, there’s a chance that some innocent peoples’ privacy could be infringed, but more criminals would be brought to justice. And with more fingerprints, prosecutors wouldn’t have to rely on flimsier evidence such as eyewitness testimony. In addition to convicting more criminals, this could spare innocent people from prison. In short: we could reduce crime, save lives, and make justice more just.</p> <p>Is there any doubt that despite your benevolent intentions, you would be branded a fascist? After all, you are a proponent of <em>warrantless fingerprinting</em>. Many of your friends and peers would think less of you for that. If you had any fame, you would likely be hounded by the <a href="">ACLU</a> and the <a href="">EFF</a>. Despite being sincere in your views and using straightforward reasoning, people would continually misrepresent you as an authoritarian who hates privacy.</p> <p>Hopefully, this is starting to sound familiar.</p> <p>Our current legal system is the product of many historical accidents. In some circumstances, it may give too much leeway to police and prosecutors. In others, it may give too little. Reasonable, intelligent people can come to different conclusions about this. Those in favor of warrantless X are not authoritarian or fascistic. Those against warrantless X are not paranoid anarchists. So please tone down the rhetoric and <a href="/2015/03/25/please-stop-getting-outraged/">stop getting outraged</a>.</p> On Learning C, Part 4: What Should I Read? Why Should I believe you? 2016-02-10T22:30:00-08:00 <h3 id="table-of-contents">Table of Contents</h3> <ul> <li><a href="/2016/01/04/on-learning-c-part-1-k-r/">Part 1: K&amp;R</a></li> <li><a href="/2016/01/18/on-learning-c-part-2-zed-shaws-learn-c-the-hard-way/">Part 2: Zed Shaw’s Learn C the Hard Way</a></li> <li><a href="/2016/02/04/on-learning-c-part-3-c-programming-substance-guidelines/">Part 3: C Programming Substance Guidelines</a></li> <li><a href="/2016/02/10/on-learning-c-part-4-so-what-should-i-read/">Part 4: What Should I Read? Why Should I believe you?</a> (You are here.)</li> </ul> <hr /> <h2 id="why-should-i-believe-you">Why should I believe you?</h2> <p>When it comes to C, I am quite proficient. I’ve been employed professionally to write C. I created <a href="/ag/">a somewhat popular open source project in C</a>. I have extensive experience profiling C programs<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup><sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup>, optimizing them<sup id="fnref:3"><a href="#fn:3" class="footnote">3</a></sup><sup id="fnref:4"><a href="#fn:4" class="footnote">4</a></sup>, and making them multithreaded<sup id="fnref:5"><a href="#fn:5" class="footnote">5</a></sup>. I’ve contributed to other open source C projects. In short, if I’m not worth taking seriously on this topic, then very few people are.</p> <h3 id="how-i-learned-c">How I Learned C</h3> <p>C was my first real programming language.<sup id="fnref:6"><a href="#fn:6" class="footnote">6</a></sup> I was introduced to it at the age of 13. Thanks to some luck and an advanced placement test, I was allowed to take up to two classes per semester at <a href="">Gonzaga University</a>. Wanting to learn more about programming, I enrolled in CS121.</p> <p><br /></p> <p><img alt="My student ID" src="/images/student_id.jpg" style="width: 512px; height: 330px;" /></p> <p>I stuck out a little on campus.</p> <p><br /></p> <p>I distinctly remember an early assignment where I was completely stumped by a bug. I’d almost finished the program, but there was one issue that I couldn’t fix. An <code>if</code> statement was always evaluating to <code>TRUE</code>, even when it shouldn’t. The <code>else</code> was never taken. The program compiled without warnings. It was incredibly frustrating.</p> <p>I spent <em>two days</em> staring at that code. I didn’t know about debuggers, so I peppered my code with <code>printf()</code>s. I commented and uncommented chunks of code. No matter what I tried, I simply couldn’t understand why the program was misbehaving. I was almost in tears when I asked my dad for help. He saw the problem in seconds:</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span></span><span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">=</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span></code></pre></figure> <p>I had a single equals in a conditional. That meant I was assigning <code>a</code> to <code>b</code> instead of comparing them. As soon as I added another equals, my program worked flawlessly. All that effort and frustration was caused by a single missing character.<sup id="fnref:7"><a href="#fn:7" class="footnote">7</a></sup></p> <p>I’m still surprised that, afterwards, I remained interested in writing code. I’ve quoted him before, but <a href="">Douglas Crockford</a> <a href="">said it best</a>:</p> <blockquote> <p>I think there has to be something seriously wrong with you in order to do this work. A normal person, once they’ve looked into the abyss, will say, “I’m done. This is stupid. I’m going to do something else.” But not us, ‘cause there’s something really wrong with us.</p> </blockquote> <p>This was undoubtedly the most difficult educational experience of my life. I really did learn C the hard way.</p> <h2 id="so-what-should-i-read">So what should I read?</h2> <p>Honestly? I’m not sure. For those who want to to learn C, I have yet to find a book that I can unconditionally recommend. The only text I can personally suggest is <a href="">the Kernighan and Ritchie book</a> from <a href="/2016/01/04/on-learning-c-part-1-k-r/">part 1</a>. As I said in that post, caveats apply.</p> <p>For those who know some C and are looking to improve their skills, there are better resources. I’ve heard good things about <a href="">Robert Love</a>’s <a href=""><em>Linux Kernel Development</em></a> (<a href="">PDF</a>). And though it covers <em>much</em> more than C, <a href=""><em>Computer Systems: A Programmer’s Perspective</em></a> will teach you all the gory details of how source code gets turned into machine code.</p> <!-- There are also a few resources I haven't reviewed, bu [Modern C]( by [Jens Gustedt]( C Programming: A Modern Approach --> <p>The dearth of good C books is a bit of a bummer, but there may be a silver lining. I think many learners rely too much on books. It’s often more educational to poke around on your own. Read other people’s code. Examine open source projects. Ask others for help. And if you’re not sure how something works, <a href="/2012/01/30/programming-we-can-do-science/">do science</a>!</p> <hr /> <div class="footnotes"> <ol> <li id="fn:1"> <p><a href="/2012/01/23/making-programs-faster-profiling/">Making Ag Faster: Profiling with Valgrind</a>&nbsp;<a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p><a href="/2012/02/08/profiling-with-gprof/">Profiling with Gprof</a>&nbsp;<a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:3"> <p><a href="/2015/02/08/optimizing-ag-special-casing-file-extensions/">Optimizing Ag: Special-casing File Extensions</a>&nbsp;<a href="#fnref:3" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:4"> <p><a href="/2012/09/03/profiling-ag-writing-my-own-scandir/">Profiling Ag. Writing My Own Scandir</a>&nbsp;<a href="#fnref:4" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:5"> <p><a href="/2012/09/07/the-silver-searcher-adding-pthreads/">The Silver Searcher: Adding Pthreads</a>&nbsp;<a href="#fnref:5" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:6"> <p>Before that, I’d only written a few toy programs in <a href="">Logo</a>, <a href="">QBasic</a>, and <a href="">TI-BASIC</a>.&nbsp;<a href="#fnref:6" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:7"> <p>If you’re wondering why the compiler didn’t warn about this, it’s because this happened in 1998. Back then, gcc didn’t warn about assignments in conditionals. Nowadays, any sane compiler will complain. How fortunate one is to learn C today. 🙂&nbsp;<a href="#fnref:7" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> On Learning C, Part 3: C Programming Substance Guidelines 2016-02-04T22:29:02-08:00 <h3 id="table-of-contents">Table of Contents</h3> <ul> <li><a href="/2016/01/04/on-learning-c-part-1-k-r/">Part 1: K&amp;R</a></li> <li><a href="/2016/01/18/on-learning-c-part-2-zed-shaws-learn-c-the-hard-way/">Part 2: Zed Shaw’s Learn C the Hard Way</a></li> <li><a href="/2016/02/04/on-learning-c-part-3-c-programming-substance-guidelines/">Part 3: C Programming Substance Guidelines</a> (You are here.)</li> <li><a href="/2016/02/10/on-learning-c-part-4-so-what-should-i-read/">Part 4: What Should I Read? Why Should I believe you?</a></li> </ul> <hr /> <h2 id="c-programming-substance-guidelines">C Programming Substance Guidelines</h2> <p>A few months ago, another C guide <a href="">was posted on Hacker News</a>: <a href="">C Programming Substance Guidelines</a>. I disagreed with some of its recommendations, including the endorsement of Shaw’s <em>Learn C the Hard Way</em>, so I left a comment on the HN submission. In hindsight, I see that my comment was too dismissive and curt. Fortunately, the author didn’t take it personally. In fact, he was interested in a more detailed critique. That’s the purpose of this post.</p> <p>What follows is a sort of shotgun approach. I’ve quoted and responded to the guidelines that I think are particularly important to follow (or not follow). And in some cases, I present a more nuanced version of the author’s advice, so as to diffuse some ways in which it could backfire. Alright, on with the show…</p> <h3 id="codebase-size">Codebase Size</h3> <blockquote> <p>Things that are large and frequently changing will <strong>never</strong> be secure. If you care about security, the best thing you can do is keep the project small.</p> </blockquote> <p>This is great advice. It applies to all bugs, not just security issues. After a half-century of research into software development, the best predictor of a codebases’s bug count is still lines of code. Even when researchers try to find correlations with more advanced metrics, it reduces to lines of code:</p> <blockquote> <p>For nonheader files, all the metrics show a high degree of correlation with lines of code. We accounted for the confounding effect of size, showing that the high correlation coefficients remain for different size ranges. In our opinion, there is a clear lesson from this study: syntactic complexity metrics cannot capture the whole picture of software complexity. Complexity metrics that are exclusively based on the structure of the program or the properties of the text (for example, redundancy, as Halstead’s metrics do), do not provide information on the amount of effort that is needed to comprehend a piece of code—or, at least, no more information than lines of code do.<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> </blockquote> <p>Be warned though, <a href="/2013/03/06/the-cost-of-features/">it is very hard to keep a codebase small</a>.</p> <h3 id="setting-pointers-to-null-after-free">Setting Pointers to <code>NULL</code> After <code>free()</code></h3> <blockquote> <p>Immediately clear pointers after freeing…</p> </blockquote> <p>I think this advice is dangerously double-edged. It really depends on what you’re doing. If you intend to reuse the pointer, definitely set it to <code>NULL</code> after <code>free()</code>ing. In that case, the pointer serves as both a pointer and as a flag for related parts of the program’s control flow. I recommend this pattern as it makes it very hard to get into an invalid state. Either the pointer points to a valid object, or it’s <code>NULL</code>.</p> <p>On the other hand: If you intend for the pointer to be allocated and deallocated only once, clearing it just hides bugs. If your program’s control flow has unexpected behavior that results in a double-free, it’s important to find out why that’s happening. Papering over it with <code>ptr = NULL;</code> will fix the symptom, but it won’t fix the underlying cause.</p> <h3 id="calloc-vs-malloc"><code>calloc()</code> vs. <code>malloc()</code></h3> <blockquote> <p>Use <code>calloc</code> instead of <code>malloc</code></p> </blockquote> <p>I don’t think this matters much, but I mildly disagree. Like the previous example, this will likely hide the underlying cause of bugs in your program. If an issue is “fixed” by <code>calloc()</code>, that means something wasn’t properly initialized. Zeroing-out the entire memory region might be the correct way to initialize it, or it might not. It really depends on what you’re doing. Instead of blindly zeroing-out everything, it’s better to explicitly initialize memory to the values you want. Doing so will help you avoid a class of bugs that –while rare– are very subtle and pernicious.</p> <h3 id="custom-allocators">Custom Allocators</h3> <blockquote> <p>Don’t write custom allocators</p> </blockquote> <p>I hardly ever come across these, but I certainly agree. Unless you <em>really</em> know what you’re doing, a custom allocator will likely be buggy and slow. It will also make it harder for other programmers to understand your code. I’ve never needed to write my own allocator. If memory allocation/deallocation becomes a performance bottleneck, use an existing library such as <a href="">APR’s memory pools</a>.</p> <h3 id="braces">Braces</h3> <p>Several guidelines reference brace style:</p> <blockquote> <p>Single line if possible: <code>if(rc &lt; 0) return rc;</code></p> </blockquote> <blockquote> <p>Single line if possible: <code>if(X == rc) return Y;</code></p> </blockquote> <blockquote> <p>Put one-statement conditionals on the same line as the condition</p> </blockquote> <blockquote> <p><code>if(rc &lt; 0) goto fail;</code></p> </blockquote> <p>I can’t get behind that. Never omit braces. The reasoning behind this is straightforward: When changing a one-line conditional into a multi-line conditional, people occasionally forget to add braces. Often, a <code>goto</code> or <code>return</code> ends up always being taken. By always using braces, you make your programs immune to this entire class of bugs. It’s a no-brainer. You’re sacrificing subjective improvement in appearance for an objective improvement in correctness. I can’t say it enough: Never omit braces.</p> <h3 id="error-handling">Error Handling</h3> <blockquote> <ul> <li>Error handling</li> <li>Do it, always, even in sample code<br /> … <ul> <li>Handle errors like everyone is watching</li> </ul> </li> </ul> </blockquote> <p>This seems needlessly paranoid. Both the “when” and “how” of error handling depend heavily on what you’re doing. If your code is used (or can be used) in something important, then go wild dealing with different return values. However, there are some errors that you should probably never try to handle. For example, according to the spec, <code>malloc()</code> will return <code>NULL</code> if it couldn’t allocate the requested memory. In practice, this is almost never the case. Modern operating systems lie about having enough memory. According to <a href="">the Linux <code>malloc</code> manpage</a>:</p> <blockquote> <p>By default, Linux follows an optimistic memory allocation strategy. This means that when <code>malloc()</code> returns non-<code>NULL</code> there is no guarantee that the memory really is available.</p> </blockquote> <p>After lying to your process, the OS will then <a href="">kill it for accessing the “allocated” memory</a>. It’s a similar story for OS X, FreeBSD, and others. Considering these behaviors, it makes very little sense to handle <code>malloc()</code> errors. Only the most mission critical code needs to be so robust. For most programs, it’s fine to just crash. If you like, you can make the error message nicer by <a href="">wrapping <code>malloc()</code> in a check that <code>exit()</code>s with a nonzero value</a>.</p> <p>That said, I think this guideline has a kernel of truth. C programs written by beginners tend to have issues with error handling. Often, they completely ignore errors and keep on truckin’, causing them to crash in odd ways. A simple crash-and-burn check would do wonders. Something like:</p> <figure class="highlight"><pre><code class="language-c" data-lang="c"><span></span><span class="n">rv</span> <span class="o">=</span> <span class="n">do_something</span><span class="p">(</span><span class="o">&amp;</span><span class="n">foo</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="n">rv</span><span class="p">)</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">&quot;Error: %s</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span> <span class="n">strerror</span><span class="p">(</span><span class="n">errno</span><span class="p">));</span> <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span></code></pre></figure> <p>(This assumes <code>do_something()</code> sets <code>errno</code>.)</p> <h2 id="conclusion">Conclusion</h2> <p>Overall, I think <em>C Programming Substance Guidelines</em> is helpful, but I can’t point newbies to it without major caveats. A decent portion of its advice is double-edged or counterproductive. My initial thoughts on the guide are a good note to end on:</p> <blockquote> <p>Please don’t blindly follow this guide. Explore other codebases (including the ones the author linked to). Talk to other programmers. Write your own projects, and get feedback from those who are more knowledgable.</p> </blockquote> <blockquote> <p>You might notice that this advice generalizes to every language. That’s because C isn’t special. If anything, the language itself is simpler than most. C has just had more time to accumulate cruft, both technical and cultural. So don’t feel intimidated. Once you learn those bits of historical trivia, you’ll be fine.</p> </blockquote> <hr /> <div class="footnotes"> <ol> <li id="fn:1"> <p><a href=""><em>Making Software: What Really Works, and Why We Believe It</em></a>, Chapter 8 (by Israel Herraiz &amp; Ahmed E. Hassan)&nbsp;<a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> On Learning C, Part 2: Zed Shaw's Learn C the Hard Way 2016-01-18T22:26:52-08:00 <h3 id="table-of-contents">Table of Contents</h3> <ul> <li><a href="/2016/01/04/on-learning-c-part-1-k-r/">Part 1: K&amp;R</a></li> <li><a href="/2016/01/18/on-learning-c-part-2-zed-shaws-learn-c-the-hard-way/">Part 2: Zed Shaw’s Learn C the Hard Way</a> (You are here.)</li> <li><a href="/2016/02/04/on-learning-c-part-3-c-programming-substance-guidelines/">Part 3: C Programming Substance Guidelines</a></li> <li><a href="/2016/02/10/on-learning-c-part-4-so-what-should-i-read/">Part 4: What Should I Read? Why Should I believe you?</a></li> </ul> <hr /> <p>Another common answer to, “What book should I read to learn C?”, is <a href="">Zed Shaw</a>’s <a href=""><em>Learn C the Hard Way</em></a>, AKA “LCTHW.”</p> <h2 id="learn-c-the-hard-way-a-flawed-text-with-an-agenda">Learn C the Hard Way: A Flawed Text with an Agenda</h2> <p>As harsh as that title may sound, I’m really not trying to court controversy. I have no grudge with Zed Shaw. In fact, I think he’s a talented programmer and an excellent teacher. He has helped thousands of people with his guide, <a href=""><em>Learn Python the Hard way</em></a>. He has done far more to advance programming education than I could ever hope to. It is for these reasons that I wish I could say, “<em>Learn C the Hard Way</em> is worth reading.” Unfortunately, it’s not.</p> <p>I’m not alone in this view. The most well-known (though in my opinion, flawed) criticism of LCTHW is <a href="">Learn C the Wrong way</a> by Tim Hentenaar. While snarky and hostile, it does contain valid criticisms. The chapter on setup is quite poor. The chapter on invoking the compiler is atrocious. It only talks of <code>make</code>, not directly invoking <code>gcc</code> or <code>clang</code>. In essence, it pretends there is only one way to invoke the compiler: a build system, and only one build system: <code>make</code>. I’m all for introductory texts keeping things simple, but that’s going too far. I could list dozens of other problems, but I’d largely be duplicating Hentenaar’s efforts. In short, there’s no salvaging this book.</p> <p>If I could distill my evaluation of LCTHW to one sentence, it would be: Shaw fundamentally disagrees with the majority of C programmers about how to write C. The biggest divergence comes from his condemnation of NULL-terminated strings. Originally, LCTHW had <a href="">an introductory chapter “critiquing” K&amp;R</a>. Much of the chapter boils down to: If you don’t NULL-terminate something that’s supposed to be NULL-terminated, all kinds of terrible things can happen. I’m not sure who this warning is intended for. Anyone who writes C will quickly have this knowledge burned into their brain. When it comes to details, Shaw’s critique is full of mistakes. Instead of listing them all, I’ll just link to the times it has been discussed on Hacker News: <a href="">1</a>, <a href="">2</a>, <a href="">3</a>, <a href="">4</a>. Take a look at <a href="">Shaw’s replies</a>. They make my case far better than I could.</p> <h2 id="shaws-response">Shaw’s Response</h2> <p>Shaw eventually received enough criticism that he <a href="">admitted defeat and removed the K&amp;R chapter</a>. In that blog post, he went on to disparage the entire C community and the language itself:</p> <blockquote> <p>But C? C’s dead. It’s the language for old programmers who want to debate section A.6.2 paragraph 4 of the undefined behavior of pointers. Good riddance. I’m going to go learn Go.</p> </blockquote> <p><a href="">Shaw’s response to Tim Hentenaar</a> had the same tact and grace. Honestly, I find Shaw’s response baffling. He spends very few words addressing technical arguments. Most of the post is Shaw saying good things about himself and bad things about Tim Hentenaar. Tim lacks qualifications. Tim doesn’t understand how to teach code. Tim is arrogant. Tim is hubristic. Tim can’t spell. Those may all be true, but they don’t address the arguments. Yes, Tim is a snarky prick, but that doesn’t make his arguments incorrect.</p> <p>There’s only one criticism that Shaw really addresses, and that’s Hentenaar’s defense of NULL-terminated strings. Shaw shows how Hentenaar’s <code>copy()</code> could fail… if the source string is corrupted. As I read this part of Shaw’s post, I was reminded of a quote by Charles Babbage:</p> <blockquote> <p>On two occasions I have been asked, “Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?” … I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question.<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> </blockquote> <p>If you’re willing to posit corrupt or invalid inputs to a function, all bets are off. No data structure will save you. Amusingly, Shaw’s original criticism of Hentenaar’s <code>copy()</code> was incorrect. On the one technical issue Shaw engaged with, he was mistaken. Someone else had to correct him. That’s quite the indictment of his C knowledge. If Shaw can’t correctly refute one C example, what are the chances he’s written a quality book on the language?</p> <h2 id="why-im-writing-this">Why I’m Writing this</h2> <p>I have no dog in this fight. I just don’t want newbies to be misinformed.<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup> As I said before, Zed Shaw’s guides have helped thousands. But when it comes to C, he is both mistaken and more than a little arrogant. I truly wish it were otherwise.</p> <hr /> <div class="footnotes"> <ol> <li id="fn:1"> <p><a href="">Passages from the Life of a Philosopher</a>&nbsp;<a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p>In fact, I knew nothing about LCTHW or this spat until <a href="">someone asked for “peer reviews”</a> of LCTHW in <a href="">/r/C_Programming</a>.&nbsp;<a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> On Learning C, Part 1: K&R 2016-01-04T21:38:43-08:00 <h3 id="table-of-contents">Table of Contents</h3> <ul> <li><a href="/2016/01/04/on-learning-c-part-1-k-r/">Part 1: K&amp;R</a> (You are here.)</li> <li><a href="/2016/01/18/on-learning-c-part-2-zed-shaws-learn-c-the-hard-way/">Part 2: Zed Shaw’s Learn C the Hard Way</a></li> <li><a href="/2016/02/04/on-learning-c-part-3-c-programming-substance-guidelines/">Part 3: C Programming Substance Guidelines</a></li> <li><a href="/2016/02/10/on-learning-c-part-4-so-what-should-i-read/">Part 4: What Should I Read? Why Should I believe you?</a></li> </ul> <hr /> <p>C is an important language for many reasons. It’s ubiquitous. No other language runs on as many platforms. It’s <em>fast</em>. It gets you closer to the bare metal than anything but assembly. It’s old. It has a long history, much of it intertwined with the beginnings of UNIX.</p> <p>It also has a reputation for being hard to master.</p> <p>When novices ask, “What book should I read to learn C?”, I’ve heard a common recommendation: <a href="">Kernighan</a> &amp; <a href="">Ritchie</a>’s <a href=""><em>The C Programming Language</em></a>, AKA “the K&amp;R book.” Unfortunately, I can’t recommend it to beginners. Allow me to explain why.</p> <h2 id="kr-decent-but-dated">K&amp;R: Decent, but Dated</h2> <p>The K&amp;R book does an excellent job of explaining the C language. It describes syntax, keywords, program structure, preprocessor behavior, and the standard library. There’s just one problem: it’s <em>old</em>. The last major update was in 1988. This leads to amusing anachronisms, such as the intro to a section about function arguments:</p> <blockquote> <p>One aspect of C functions may be unfamiliar to programmers who are used to some other languages, particularly Fortran. In C, all function arguments are passed “by value.”<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> </blockquote> <p>Humor aside, the book’s age does hurt its educational value. Omissions include:</p> <ul> <li><code>//</code>-style comments</li> <li><a href="">Inline functions</a></li> <li><code>snprintf()</code></li> <li>Types such as <code>bool</code> and <code>long long</code></li> <li>Variable-length arrays</li> <li><a href="">Variadic macros</a></li> </ul> <p>It’s not essential for beginners to know these features, but a book about C should at least mention them. K&amp;R also avoids any discussion of build systems (autotools), debuggers (gdb, lldb), or profilers (gprof, dtrace). That’s more understandable though. These tools either didn’t exist or were in their infancy when the book was authored. Considering how dated those sections would now be, it’s probably good that they’re missing.</p> <p>I think Kernighan &amp; Ritchie never had the goal of covering anything outside the language itself. And that’s the main reason why I can’t recommend K&amp;R to beginners. It lacks any sort of “getting started” section. It has no guides for setting up a development environment. There’s no chapter on how to install and use a compiler. Again, I realize these sections would be completely out-of-date had they existed in the 1988 book. Still, it’s important that programming language books help users set up development environments.</p> <p>In short: K&amp;R is part of a solid intro to C, but it’s not enough. It needs a few updates to reflect modern C. More importantly, it needs a companion book to cover setup and tooling. In its current form, it serves as more of an overview and reference.</p> <hr /> <div class="footnotes"> <ol> <li id="fn:1"> <p>Section 1.8: Arguments – Call by value. For those who may not know: Today, pass by value is the most popular design in modern languages.&nbsp;<a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> </ol> </div> FSEvents Tools: Watch a Directory for Changes 2015-12-25T08:35:18-08:00 <p>Often, when I modify files in a directory, I want some action to happen afterwards. For example: If I’m editing some source code locally, I might want it to be rsynced to a remote server. Or maybe I want to regenerate CSS after changing some <a href="">LESS</a> or <a href="">SASS</a>, or minify some JavaScript after changing the uncompressed source. Sometimes, there’s an application-specific solution to this problem. Sometimes, there isn’t. In the latter case, it would be nice to fall back on a general purpose tool.</p> <p>On Linux, this is a solved problem. Just install <a href=""><code>inotify-tools</code></a> and wrap it with a couple of scripts. Sadly, each operating system has its own unique API for monitoring filesystem changes. That means Windows and Mac users are out of luck.</p> <p><a href="/fsevents/"><code>fsevents-tools</code></a> is my attempt to solve this problem for OS X. As the name implies, it uses OS X’s <a href="">FSEvents API</a> to monitor filesystem changes. The first release (v1.0) consists of three commands:</p> <h3 id="notifywait">notifywait</h3> <p><code>notifywait</code> is a simple tool that the other utilities depend on. It does one thing: Given path(s) to watch, it waits until something in them changes and exits. For example, if you have public file sharing enabled and you’re expecting an incoming file, run:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text"><span></span>% notifywait ~/Public/Drop\ Box &amp;&amp; say &quot;Dropbox changed&quot;</code></pre></figure> <p>The output will be something like…</p> <figure class="highlight"><pre><code class="language-text" data-lang="text"><span></span>Path is /Users/ggreer/Public/Drop Box Watching /Users/ggreer/Public/Drop Box Change 18158642688910021128 in /Users/ggreer/Public/Drop Box/untitled folder, flags 131328 - matched directory, notifying</code></pre></figure> <p>…followed by your computer talking. Note: <code>notifywait</code> will also exit if a file or directory is moved or deleted. In this specific example, that’s probably more than you want.</p> <h3 id="notifyloop">notifyloop</h3> <p><code>notifyloop</code> takes a <code>path</code> and a <code>command</code>. When something in <code>path</code> changes, it runs <code>command</code>. For example, if you have a bunch of LESS in <code>styles/</code> and you want to rebuild CSS when they change, you’d do something like this:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text"><span></span>ggreer@carbon:~/code/ notifyloop styles ./ Watching styles Path is /Users/ggreer/code/ Watching /Users/ggreer/code/ Change 18158642688910117872 in /Users/ggreer/code/, flags 70656 - matched directory, notifying Running ./ lessc styles/colors.less styles/colors.css lessc styles/countdown.less styles/countdown.css lessc styles/hexagons.less styles/hexagons.css lessc styles/main-dark.less styles/main-dark.css lessc styles/main-light.less styles/main-light.css Path is /Users/ggreer/code/ Watching /Users/ggreer/code/</code></pre></figure> <p>Notice that, although <code></code> changed CSS files in <code>styles/</code>, <code>notifyloop</code> did not go into an infinite regress. That’s because <code>notifyloop</code> waits until <code>command</code> has finished before resuming monitoring changes.</p> <p>While simple, <code>notifyloop</code> is very flexible. You’ll probably use it more than the other tools.</p> <h3 id="autorsync">autorsync</h3> <p>Finally, there’s <code>autorsync</code>. It takes a <code>path</code> and a remote destination. If anything in <code>path</code> changes, it <a href="">rsyncs</a> <code>path</code> to the remote. In the following example, I copy the source for <a href="/ag/">ag</a> to my home server. Since the repo was out of date on that server, my first save of <code>decompress.c</code> causes a lengthy rsync. As expected, the second save rsyncs much faster. Here’s the command and output:</p> <figure class="highlight"><pre><code class="language-text" data-lang="text"><span></span>ggreer@carbon:~/code% autorsync ag lithium.local:code/ Watching ag Path is /Users/ggreer/code/ag Watching /Users/ggreer/code/ag Change 18158642688910848099 in /Users/ggreer/code/ag/src/decompress.c, flags 70656 - matched directory, notifying Running rsync -avz ag lithium.local:code/ building file list ... done ag/ ... sent 2882666 bytes received 52598 bytes 1174105.60 bytes/sec total size is 6612119 speedup is 2.25 Path is /Users/ggreer/code/ag Watching /Users/ggreer/code/ag Change 18158642688910848167 in /Users/ggreer/code/ag/src/decompress.c, flags 70656 - matched directory, notifying Running rsync -avz ag lithium.local:code/ building file list ... done ag/src/decompress.c sent 36800 bytes received 114 bytes 73828.00 bytes/sec total size is 6612119 speedup is 179.12 Path is /Users/ggreer/code/ag Watching /Users/ggreer/code/ag ...</code></pre></figure> <p>…and so on.</p> <p>Those who use GUI editors will likely recognize the value of <code>autorsync</code>. No longer will you have to ssh in and make changes using Vim or Emacs. Nor will you have to manually copy files or set up <a href="">sshfs</a>. With one command, everything gets synced to the remote server.</p> <p><br /></p> <p>I hope you find these tools as useful as I do. <a href="/fsevents/">Signed releases are available here</a>. The source code is on <a href="">GitHub at ggreer/fsevents-tools</a>.</p> The Case for "Pro" Browsers 2015-12-06T10:46:45-08:00 <p>Over the past few years, I’ve gradually become more frustrated with web browsers. Don’t get me wrong. Today’s browsers are faster, more stable, and more secure than ever. They have better debugging tools. They support more interesting technologies like <a href="">HTML5</a> and <a href="">WebRTC</a>. In almost every measurable way, browsers are strictly better than they used to be.</p> <p>Except one: User interfaces.</p> <p>Originally, browsers were created by developers for developers. But lately, Chrome and Firefox have focused on improving the experience for normal users. This is, on net, a good thing. The vast majority of users benefit from features like <a href="">voice search</a>, <a href="">profile synchronization</a>, <a href="">apps</a>, and fancy new tab pages. They also benefit from dangerous settings being hidden in places like <code>chrome://flags</code>.</p> <p>But not me. When running a browser for the first time, I disable a dozen features and install extensions to expose advanced behavior. I change my search engine so the Google Doodle doesn’t show up on Chrome’s new tab page. I add an extension to <a href="">restore Chrome’s presentation mode</a>.</p> <p>Still, I can’t fix every annoyance. The new tab page lacks recently-closed tabs. The developer console <a href="">requires some priming before pasting will work</a>. Pasting <code>javascript:</code> URLs in the address bar <a href="">doesn’t work</a>. Some TLS certificate errors can’t be overridden. These aren’t just UI annoyances. Some of these changes really have hurt developer productivity. And yet, I can’t fault Chrome for making these changes. They’re great for the vast majority of users.</p> <p>One browser can’t satisfy both consumers and developers. So what’s the solution? Simple: Have more than one browser. Differentiate. Use the same libraries and rendering engines, just tweak the UI to be more developer-friendly.</p> How to Write Good Bug Reports 2015-08-15T14:41:02-07:00 <p>I’ve created and maintained several open source projects over the years, and my biggest annoyance with them is bug reports. Not bugs themselves. Those are inevitable, and the actual process of fixing them is interesting. But bug reports are another matter. Far too often, I get reports that are all but useless. They waste time and cause frustration for everyone involved. In the hope that I can reduce this, I offer some guidelines for writing good bug reports.</p> <p>The goal of a bug report is to make it possible for a developer to reproduce the bug. I cannot emphasize this enough. A developer who can reproduce a bug is halfway to fixing it. At that point, they can bring their entire toolset to bear: debuggers, profilers, test cases, and lowly (but useful) <code>printf()</code>s. Inversely, trying to fix an unreproducible bug is an exercise in futility. Often, one ends up flying blind, reduced to changing code based on hunches and guessing. Believe me, it is not fun.</p> <p>So how do you make it easier for devs to reproduce a bug? Developers know the software in question, but your computing environment is a complete mystery to them. The more they know about that, the easier it will be for them to reproduce the issue. With this in mind, reports should contain information such as:</p> <ul> <li>The exact operating system version. (e.g. OS X 10.10.5)</li> <li>The version of the buggy software. (e.g. Chrome 44.0.2403.130, Sublime Text 3 Build 3083, ag version 0.30.0, etc.) It’s not uncommon for users to report bugs that have been fixed in newer versions.</li> <li>Steps to cause the problem. Exact commands are very helpful.</li> <li><em>Exact</em> error messages. Codebases can be searched for exact error messages. Not so for paraphrased ones.</li> <li>Debug logs. Again, copied exactly.</li> <li>Any unique data involved. I can’t convey how infuriating it is for someone to say, “It breaks on this file.” and <em>not</em> provide the file.</li> </ul> <p>Err on the side of including too much. It’s quite rare that I read a bug report and think, “Wow. This is way more info than I need.” Also, overcommunicating helps to reduce back-and-forth. Every reply/response cycle decreases the likelihood that a dev will get to the bottom of the issue.</p> <p>Guidelines are good, but I find examples help more. I don’t want to single anyone out, but here are some examples of bad bug reports. If you see your name on one of those issues, don’t feel upset. Reporting bugs is a skill. It doesn’t come naturally. Just try to do better in the future.</p> <ul> <li><a href="">After installing, don;t have tools on path · Issue #3 · ggreer/dsniff</a></li> <li><a href="">#BUG in diacritic · Issue #1 · ggreer/jekyll-gallery-generator</a></li> </ul> <p>What am I supposed to do with these? They don’t even describe what the <em>problem</em> is, let alone how to reproduce it. On the flip side, here are some good bug reports:</p> <ul> <li><a href="">Doesnt work with build from outside the source directory · Issue #18 · ggreer/jekyll-gallery-generator</a></li> <li><a href="">Ag thinks this PDF is not a binary file · Issue #637 · ggreer/the_silver_searcher</a></li> <li><a href="">Cannot run ag in parallel · Issue #33 · ggreer/the_silver_searcher</a></li> </ul> <p>These are pretty close to ideal. They include everything needed to reproduce the issue. One even linked to an example file that caused the bug. The limiting factor for fixing these was me, not the reporter. That is how it should be.</p> <p>Writing good bug reports requires more effort, but the payoff makes it worthwhile. Remember this when clicking, “Create Issue” and not only will you make developers’ lives easier, but you’ll improve the likelihood of getting the bug fixed.</p> Laptop Annoyances (Or: Why I Use a MacBook) 2015-07-25T14:04:40-07:00 <p>When asked why I use macs, I reply with something along the lines of, “Because macs don’t annoy me as much as other laptops.” That response is immediately followed by a request for examples. If this conversation is face-to-face, I boggle for a moment. There are so <em>many</em> examples. I have trouble enumerating them. For future reference, I’ve taken the time to list and explain these common annoyances.</p> <p>In the infintesimal chance that a Lenovo exec reads this: Please consider it a list of things to verify before shipping a new model.</p> <h3 id="table-of-contents">Table of Contents</h3> <ul> <li><a href="#trackpad">Trackpad</a></li> <li><a href="#keyboard">Keyboard</a></li> <li><a href="#screen">Screen</a></li> <li><a href="#battery">Battery life</a></li> <li><a href="#size">Size</a></li> <li><a href="#suspend">Suspend/Hibernate</a></li> <li><a href="#fans">Fans</a></li> <li><a href="#drivers">Drivers</a></li> <li><a href="#other">Other minor gripes</a></li> </ul> <hr /> <p><span id="trackpad"></span></p> <h2 id="trackpad">Trackpad</h2> <p>I sometimes wonder if laptop makers are controlled by a cabal of mouse manufacturers. Why else would so many companies churn out glitchy, barely-usable trackpads? It’s the only explanation that makes sense: Make terrible trackpads so more people buy external mice. The alternative is that laptop makers are incompetent, and they can’t <em>all</em> be incompetent, right?</p> <p>Out of all the annoyances I run into, this is the most common. My 2014 ThinkPad’s trackpad is inferior to the one on my <em>2005</em> iBook. Almost a decade separates the two machines, yet the newer one has a smaller, glitchier, worse-feeling trackpad. It’s absurd.</p> <p>There’s no secret formula for a good trackpad. Make it big. Make it glass. Make decent drivers. Outside of Apple gear, few models satisfy even one of those three.</p> <p>I long for the day when the only laptop users with mice are either gamers or psychotics. Judging from the current state of things, I doubt that day will ever come.</p> <hr /> <p><span id="keyboard"></span></p> <h2 id="keyboard">Keyboard</h2> <p><a href="/photos/pics/DSC_0662.jpg"><img alt="ThinkPad x140e keyboard" src="/photos/pics/thumbs/DSC_0662.jpg" /></a></p> <p>If you ever find yourself agreeing with the statement, “PrintScreen should go between Ctrl and Alt.”, put down the bottle of Kirkland Signature™ Absinthe and go to bed.</p> <p>Given how important keyboards are at melding mind and machine, it makes sense that laptop makers would spend some R&amp;D money on them. As far as I can tell, they are spending that money on novel ways to <em>ruin</em> keyboards. Apple experiments with a lot of new technologies, but one thing they hardly ever mess with is the keyboard. Macs don’t have “innovative” new layouts. They don’t have fancy “smart” function keys. They’re just normal keyboards. The reason for this is straightforward: If it ain’t broke, don’t fix it.</p> <hr /> <p><span id="screen"></span></p> <h2 id="screen">Screen</h2> <p><a href="/photos/pics/retina_screen.jpg"><img alt="MacBook Air vs Retina MacBook" src="/photos/pics/thumbs/retina_screen.jpg" style="width:400px; height:350px; float:left; padding-right:16px;" /></a></p> <p>Skimping on a screen is like buying cheap glasses. Yes, you will save money. And you’ll be annoyed by your frugality for thousands of hours afterwards. On the flip side, a high-resolution <a href="">IPS</a> display is a continual joy to use.</p> <p>When the retina MacBook Pros came out, I avoided looking at their screens. I heard they were gorgeous, and I didn’t want to be tempted into getting something larger and heavier. Now the 12” MacBook is out and my suspicion is confirmed: Once you use a nice screen, you can’t go back. Everything else looks blurry, blocky, and washed-out.</p> <hr /> <p><span id="battery"></span></p> <h2 id="battery-life">Battery life</h2> <p><a href="/photos/screenshots/Screen Shot 2015-08-03 at 00.17.16.png"><img alt="My MacBook's battery usage" src="/photos/screenshots/thumbs/Screen Shot 2015-08-03 at 00.17.16.png" style="width:400px; height:262px; float:left; padding-right:16px;" /></a></p> <p>It may sound like a lot, but most people need at least 9 hours of battery life. For software developers, that number is closer to 12. Let me explain.</p> <p>No sane person has ever said, “I wish this laptop’s battery didn’t last so long.” Still, an astonishing fraction of laptops have terrible battery life. Below some threshold, battery logistics are a constant distraction. Carry a charger. Search for a seat near an outlet. Dim the screen. Close apps. Monitor battery percentage like it’s the blood pressure of a trauma patient.</p> <p>Longer battery life is liberating. Once it reaches a certain threshold, you just plug in at night and stop worrying. After experiencing this, it’s impossible to go back.</p> <hr /> <p><span id="size"></span></p> <h2 id="size">Size</h2> <p>Most laptops are far larger than they need to be. Apple’s largest laptop is the 15” MacBook Pro. Compared to a lot of stuff out there, it’s positively miniscule.</p> <p><a href="/photos/pics/IMG_1241.jpg"><img alt="My MacBook next to an UltraBook" src="/photos/pics/thumbs/IMG_1241.jpg" /></a></p> <p>Look at this UltraBook™®© next to my 12” MacBook. It’s ultra-huge. Sure, most people <em>could</em> carry it around, but few would <em>want</em> to. Bigger laptops are more likely to be left at home. That’s a problem, since (to borrow a photography aphorism) the best computer is the one you have with you.</p> <hr /> <p><span id="suspend"></span></p> <h2 id="suspendhibernate">Suspend/Hibernate</h2> <p>Like trackpads, this is another simple aspect of laptops that almost everyone gets wrong. Here is how suspend and hibernate should work:</p> <ul> <li> <p>When the lid closes, the laptop suspends to RAM. In this mode, it consumes &lt;1% battery per hour.</p> </li> <li> <p>When the lid <em>starts to open</em>, the laptop resumes. The screen is on and ready for input before the user can finish opening the lid.</p> </li> <li> <p>If the laptop is open but sleeping (often due to idle), hitting any key or clicking the trackpad will resume it. The screen should be on and ready to work within a second.</p> </li> </ul> <p>Bonus points if Bluetooth input devices can wake the laptop from sleep.</p> <p>In the past decade, the <em>only</em> laptops that have satisfied these criteria have been made by Apple. Others take multiple seconds to wake, or they consume 5% battery per hour, or they’re only woken by lid-opening, not hitting keys. Phones and tablets never seem to have issues with suspend/hibernate, so I find it bizarre that laptops do.</p> <hr /> <p><span id="fans"></span></p> <h2 id="fans">Fans</h2> <p>Except for <a href="/2015/04/19/2015-macbook-review/">high-end low-power designs</a>, laptop fans are still a necessity. The easy part of laptop cooling is blowing air across a heatsink. The difficulty comes in designing something that works on all surfaces (including blankets) without distracting or annoying the user.</p> <p>It seems counterintuitive, but the only good location for ventilation is the hinge. Every other place has signifiacant disadvantages. Vents on the base can be blocked by blankets or bedding, causing overheating. Side vents blow hot air on things next to the laptop; usually resting hands. Front vents direct noise at the user and require long ducts, eating up space for the battery. In contrast, hinge vents can’t be blocked. They direct fan noise away from the user, and they don’t require long ducts.</p> <p>Another common fan annoyance results from poorly-written control firmware. Many laptops run their fans at a few discrete speeds. These discrete changes are noticed much more readily than continuously varied fans. It may seem minor, but this distraction adds up over the life of the laptop.</p> <hr /> <p><span id="drivers"></span></p> <h2 id="drivers">Drivers</h2> <p>Even the best hardware can be ruined by bad drivers. I would love to run Ubuntu on a Surface Pro, but the experience would be hell if it lacked decent drivers for the trackpad, camera, microphone, Bluetooth, WiFi, power saving, screen brightness, headphone switching, and a half-dozen other things that are only noticed when they’re broken.</p> <p>This stuff isn’t rocket science. No new technology needs to be invented. It just comes down to high standards and attention to detail.</p> <hr /> <p><span id="other"></span></p> <h2 id="other-minor-gripes">Other minor gripes</h2> <ul> <li> <p>No SSD. Today, any laptop sold without one is a defective laptop. While spinning rust is cheaper and can store more, it is orders of magnitude slower than a modern SSD.</p> </li> <li> <p>Build quality. Cheap plastic is a deal breaker. Lightweight alloys of aluminum or magnesium are ideal.</p> </li> <li> <p>LEDs. The only thing worse than an always-on LED is a constantly blinking LED. Double penalty if it’s one of those insanely bright blue LEDs. You might as well have a laptop that comes with an automatic eye-poking machine.</p> </li> <li> <p>Camera. The camera must be centered above the screen. Any other position will make for weird shots. Occasionally, I’ll see a model with the camera near the hinge. That’s great if you want to look up someone’s nose while video chatting. When it comes to the actual camera hardware, resolution doesn’t matter much, but low-light performance is important.</p> </li> <li> <p>Microphone placement is crucial. It must be away from noise-generating hardware such as the keyboard and fans. Ideally, the laptop can have multiple mics and use software or firmware to filter noise. Apple has dual-mic technology in all of their laptops. My ThinkPad x140e’s microphone is in the palm rest, making it all but useless.</p> </li> </ul> <hr /> <p>It’s because I have such low tolerance for these annoyances that I keep coming back to Apple. Some Apple models miss the mark, but I have yet to find any non-Apple laptop that gets these right.</p> <p>If you’ve found something else that you’re happy with, great. I really am happy for you. But don’t disparage mac owners for being sheep. There are a lot of details that Apple reliably gets right.</p> OS X: Code Signing and Firewalls 2015-04-26T10:37:19-07:00 <p>A few days ago, I encountered a new annoyance. Every time I launched an <a href="">io.js</a> server on my dev laptop, OS X’s firewall prompted me:</p> <div style="display: flex; justify-content: center;"> <img style="height: 290px; width: 532px;" src="/photos/screenshots/Screen Shot 2015-04-26 at 11.34.49.png" /> </div> <p>This was odd. Typically, such prompts are only seen the first time a new binary is executed. It’s expected after building a new version of io.js, but upgrading to v1.8.1 seemed to have changed this behavior. I checked my firewall rules and confirmed that io.js was explicitly allowed.</p> <p><img style="height: 654px; width: 780px" src="/photos/screenshots/Screen Shot 2015-04-26 at 12.11.13.png" /></p> <p>Confused? So was I. Why would OS X keep prompting for an allowed program? I needed to get work done, so rather than understand the problem, I tried to quickly fix it. Restarting didn’t help. Uninstalling and rebuilding io.js didn’t change anything. I finally stumbled on a clue when I attempted to remove and re-add the firewall rule. I could remove io.js, but I couldn’t re-add it. Clicking “allow” when prompted didn’t add it. Even manually adding <code>/usr/local/bin/iojs</code> did nothing.</p> <p>Poking around eventually paid off. I manually verified the code signature…</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% codesign -vv /usr/local/bin/iojs /usr/local/bin/iojs: invalid signature <span class="o">(</span>code or signature have been modified<span class="o">)</span></code></pre></figure> <p>Not good! But I had work to do. I couldn’t spend time satisfying my curiosity. As a band-aid, I simply signed the current binary.</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% sudo codesign -f -s - /usr/local/bin/iojs /usr/local/bin/iojs: replacing existing signature</code></pre></figure> <p>That was enough to tide me over until the weekend, but I wasn’t pleased at the prospect of doing this whenever I upgraded io.js. Also, I still had no idea how a program compiled on my own laptop could have an invalid signature.</p> <p>Yesterday evening, I dove into the problem. First, I reinstalled io.js to reproduce the firewall prompting issue. Then I tried to figure out where things were going wrong. What follows is my thought process interspersed with terminal output.</p> <p><br /></p> <p>Idea: Maybe my dev environment is borked. Let’s try compiling other programs to see if they pass signature checks.</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% codesign -vv /usr/local/bin/ag /usr/local/bin/ag: code object is not signed at all</code></pre></figure> <p>Ag’s build process lacks any signing steps, so that seems fine. Searching through io.js’s build scripts, I notice some steps that could potentially try to sign the binary. What about something with a similar build process to io.js? Would building Node.js result in an invalid signature?</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~/Downloads/node-v0.12.2% codesign -vv out/Release/node out/Release/node: code object is not signed at all</code></pre></figure> <p>Hmm… nope. In fact, it’s not signed at all. What’s different here? Also, how exactly is io.js’s signature invalid? After looking at <code>codesign</code>’s manpage again, I use <code>--describe</code>:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% codesign -dv /usr/local/bin/iojs <span class="nv">Executable</span><span class="o">=</span>/usr/local/bin/iojs <span class="nv">Identifier</span><span class="o">=</span>iojs-5555494426c029279ed5393a9c5c43ac9796d090 <span class="nv">Format</span><span class="o">=</span>Mach-O thin <span class="o">(</span>x86_64<span class="o">)</span> CodeDirectory <span class="nv">v</span><span class="o">=</span><span class="m">20100</span> <span class="nv">size</span><span class="o">=</span><span class="m">79954</span> <span class="nv">flags</span><span class="o">=</span>0x2<span class="o">(</span>adhoc<span class="o">)</span> <span class="nv">hashes</span><span class="o">=</span><span class="m">3991</span>+2 <span class="nv">location</span><span class="o">=</span>system <span class="nv">Signature</span><span class="o">=</span>adhoc Info.plist<span class="o">=</span>not bound <span class="nv">TeamIdentifier</span><span class="o">=</span>not <span class="nb">set</span> Sealed <span class="nv">Resources</span><span class="o">=</span>none Internal requirements <span class="nv">count</span><span class="o">=</span><span class="m">0</span> <span class="nv">size</span><span class="o">=</span><span class="m">12</span></code></pre></figure> <p>Interesting info, but nothing really stands out to me. Maybe running <code>codesign</code> with <code>dtruss</code> can help me figure out how OS X verifies the signature:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% sudo dtruss -f -t open codesign -dv /usr/local/bin/iojs ... PID/THRD SYSCALL<span class="o">(</span>args<span class="o">)</span> <span class="o">=</span> <span class="k">return</span> <span class="m">8507</span>/0x1e707: open<span class="o">(</span><span class="s2">&quot;/usr/lib/dtrace/libdtrace_dyld.dylib\0&quot;</span>, 0x0, 0x0<span class="o">)</span> <span class="o">=</span> <span class="m">3</span> <span class="m">0</span> <span class="m">8507</span>/0x1e707: open<span class="o">(</span><span class="s2">&quot;/dev/dtracehelper\0&quot;</span>, 0x2, 0x102F51000<span class="o">)</span> <span class="o">=</span> <span class="m">3</span> <span class="m">0</span> <span class="m">8507</span>/0x1e707: open<span class="o">(</span><span class="s2">&quot;/usr/local/bin/iojs\0&quot;</span>, 0x0, 0x1B6<span class="o">)</span> <span class="o">=</span> <span class="m">3</span> <span class="m">0</span> <span class="m">8507</span>/0x1e707: open<span class="o">(</span><span class="s2">&quot;/usr/local/bin/iojs\0&quot;</span>, 0x0, 0x1B6<span class="o">)</span> <span class="o">=</span> <span class="m">4</span> <span class="m">0</span></code></pre></figure> <p>Nothing?! I could have sworn OS X had a database of code signatures somewhere. <code>codesign</code> should have opened it. In desperation, I search my entire hard drive for that unique-looking identifier: <code>5555494426c029279ed5393a9c5c43ac9796d090</code>.</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% sudo ag --depth -1 -u 5555494426c029279ed5393a9c5c43ac9796d090 <span class="se">\</span> /Applications /Library /System /Users /private /usr <span class="m">2</span>&gt;/dev/null Binary file /private/var/db/DetachedSignatures matches.</code></pre></figure> <p>Success! Opening the file in a text editor shews binary stuff interspersed with text. Hopefully, <code>file</code> can figure it out.</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% file /private/var/db/DetachedSignatures /private/var/db/DetachedSignatures: SQLite <span class="m">3</span>.x database</code></pre></figure> <p>Good ol’ <code>file</code>, you never let me down. Except when you do. I’m relieved it’s a a SQLite DB, but what sort of horrid schema lurks inside?</p> <figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span></span><span class="n">ggreer</span><span class="o">@</span><span class="n">carbon</span><span class="p">:</span><span class="o">~%</span> <span class="n">sudo</span> <span class="n">sqlite3</span> <span class="o">/</span><span class="n">private</span><span class="o">/</span><span class="n">var</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">DetachedSignatures</span> <span class="n">SQLite</span> <span class="k">version</span> <span class="mi">3</span><span class="p">.</span><span class="mi">8</span><span class="p">.</span><span class="mi">5</span> <span class="mi">2014</span><span class="o">-</span><span class="mi">08</span><span class="o">-</span><span class="mi">15</span> <span class="mi">22</span><span class="p">:</span><span class="mi">37</span><span class="p">:</span><span class="mi">57</span> <span class="n">Enter</span> <span class="ss">&quot;.help&quot;</span> <span class="k">for</span> <span class="k">usage</span> <span class="n">hints</span><span class="p">.</span> <span class="n">sqlite</span><span class="o">&gt;</span> <span class="p">.</span><span class="n">tables</span> <span class="n">code</span> <span class="k">global</span> <span class="n">sqlite</span><span class="o">&gt;</span> <span class="p">.</span><span class="k">schema</span> <span class="n">code</span> <span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">code</span> <span class="p">(</span> <span class="n">id</span> <span class="nb">integer</span> <span class="k">primary</span> <span class="k">key</span> <span class="k">on</span> <span class="n">conflict</span> <span class="k">replace</span> <span class="n">autoincrement</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span> <span class="k">global</span> <span class="nb">integer</span> <span class="k">null</span> <span class="k">references</span> <span class="k">global</span> <span class="p">(</span><span class="n">id</span><span class="p">),</span> <span class="n">identifier</span> <span class="nb">text</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span> <span class="n">architecture</span> <span class="nb">integer</span><span class="p">,</span> <span class="n">identification</span> <span class="nb">blob</span> <span class="k">not</span> <span class="k">null</span> <span class="k">unique</span> <span class="k">on</span> <span class="n">conflict</span> <span class="k">replace</span><span class="p">,</span> <span class="n">signature</span> <span class="nb">blob</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span> <span class="n">created</span> <span class="nb">text</span> <span class="k">default</span> <span class="k">current_timestamp</span> <span class="p">);</span> <span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">identifier_index</span> <span class="k">on</span> <span class="n">code</span> <span class="p">(</span><span class="n">identifier</span><span class="p">);</span> <span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">architecture_index</span> <span class="k">on</span> <span class="n">code</span> <span class="p">(</span><span class="n">architecture</span><span class="p">);</span> <span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">id_index</span> <span class="k">on</span> <span class="n">code</span> <span class="p">(</span><span class="n">identification</span><span class="p">);</span> <span class="n">sqlite</span><span class="o">&gt;</span> <span class="p">.</span><span class="k">schema</span> <span class="k">global</span> <span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">global</span> <span class="p">(</span> <span class="n">id</span> <span class="nb">integer</span> <span class="k">primary</span> <span class="k">key</span> <span class="k">on</span> <span class="n">conflict</span> <span class="k">replace</span> <span class="n">autoincrement</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span> <span class="n">sign_location</span> <span class="nb">text</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span> <span class="n">signature</span> <span class="nb">blob</span> <span class="k">null</span> <span class="p">);</span> <span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">location_index</span> <span class="k">on</span> <span class="k">global</span> <span class="p">(</span><span class="n">sign_location</span><span class="p">);</span></code></pre></figure> <p>Fortunately, the schema is pretty simple. That <code>identifier</code> in the <code>code</code> table looks promising. I bet it’s got something io.js-related.</p> <figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span></span><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">code</span> <span class="k">WHERE</span> <span class="n">identifier</span> <span class="k">LIKE</span> <span class="ss">&quot;%iojs%&quot;</span><span class="p">;</span> <span class="mi">2</span><span class="o">|</span><span class="mi">2</span><span class="o">|</span><span class="n">iojs</span><span class="o">-</span><span class="mi">555549445</span><span class="n">f8d6c1f2ea53e118856306c27a3e609</span><span class="o">|</span><span class="mi">16777223</span><span class="o">|</span><span class="n">UUID_</span><span class="o">?</span><span class="n">l</span><span class="p">.</span><span class="o">?&gt;?</span><span class="n">V0l</span><span class="s1">&#39;?? |??</span> <span class="s1"> ?|2015-04-16 18:45:36</span> <span class="s1">4|4|iojs-5555494426c029279ed5393a9c5c43ac9796d090|16777223|UUID&amp;?)&#39;</span><span class="o">??</span><span class="mi">9</span><span class="p">:</span><span class="o">?</span><span class="err">\</span><span class="k">C</span><span class="o">???</span><span class="err">А</span><span class="o">|??</span> <span class="o">?|</span><span class="mi">2015</span><span class="o">-</span><span class="mi">04</span><span class="o">-</span><span class="mi">21</span> <span class="mi">22</span><span class="p">:</span><span class="mi">40</span><span class="p">:</span><span class="mi">24</span> <span class="n">sqlite</span><span class="o">&gt;</span> </code></pre></figure> <p>Bingo. The table contains my band-aid signature <em>and</em> one from a week earlier. Though we shouldn’t be too hasty and stop gathering information. Is there anything important in the <code>global</code> table?</p> <figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span></span><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="k">global</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="k">global</span> <span class="k">FROM</span> <span class="n">code</span> <span class="k">WHERE</span> <span class="n">identifier</span> <span class="k">LIKE</span> <span class="ss">&quot;%iojs%&quot;</span><span class="p">);</span> <span class="mi">2</span><span class="o">|/</span><span class="n">usr</span><span class="o">/</span><span class="k">local</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">iojs</span><span class="o">|??</span> <span class="o">?</span> <span class="mi">4</span><span class="o">|/</span><span class="n">usr</span><span class="o">/</span><span class="k">local</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">iojs</span><span class="o">|??</span> <span class="o">?</span></code></pre></figure> <p>No, but it was worth checking.</p> <p>Now a hypothesis emerges. I’m still not sure how, but <em>somehow</em> an older version of io.js was signed on April 16<sup>th</sup>. When I upgraded, that signature was no longer valid. The second signature in the DB was created when I manually signed the binary. Not wanting to go further down this rabbit hole, I delete the signatures, restart my computer, and see if I’m back to normal behavior:</p> <figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span></span><span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">DELETE</span> <span class="k">FROM</span> <span class="k">global</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="k">IN</span> <span class="p">(</span><span class="k">SELECT</span> <span class="k">global</span> <span class="k">FROM</span> <span class="n">code</span> <span class="k">WHERE</span> <span class="n">identifier</span> <span class="k">LIKE</span> <span class="ss">&quot;%iojs%&quot;</span><span class="p">);</span> <span class="n">sqlite</span><span class="o">&gt;</span> <span class="k">DELETE</span> <span class="k">FROM</span> <span class="n">code</span> <span class="k">WHERE</span> <span class="n">identifier</span> <span class="k">LIKE</span> <span class="ss">&quot;%iojs%&quot;</span><span class="p">;</span></code></pre></figure> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% codesign -vv /usr/local/bin/iojs /usr/local/bin/iojs: code object is not signed at all</code></pre></figure> <p>Victory! As expected, OS X’s firewall only prompts me once. After the first run, <code>codesign</code> is happy:</p> <figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span>ggreer@carbon:~% codesign -vv /usr/local/bin/iojs /usr/local/bin/iojs: valid on disk /usr/local/bin/iojs: satisfies its Designated Requirement</code></pre></figure> <p>I want to know why my old iojs signature failed, but I couldn’t find many clues from April 16<sup>th</sup>. That was just after I’d gotten <a href="/2015/04/19/2015-macbook-review/">my new laptop</a>, but before I enabled Time Machine (April 21<sup>st</sup>).</p> 2015 MacBook Review 2015-04-19T20:48:26-07:00 <p>I recently <a href="/2015/01/03/ten-years-of-progress-in-laptops/">evaluated Apple’s past decade of laptops</a>, and came to the conclusion that laptops have stagnated. Last week, I bought the base model MacBook in space gray.</p> <p>I rescind my conclusion. The MacBook is my favorite computer ever.</p> <p>My previous favorite was the 11” Air. I’ve used various incarnations since its late 2010 release. The Air is a nice laptop: small, light, reasonably quick. But it has some issues, the biggest being the screen. The Air’s <a href="">TN</a> display in 1366x768 is tolerable, but that’s pretty much all it is. The MacBook completely fixes that issue, while removing weight and adding style.</p> <p>The MacBook’s display is gorgeous. Its native resolution is 2304x1440, but the default scaling isn’t 2:1. Instead of a jumbo 1152x720 UI, the effective resolution is 1280x800. This gives the MacBook as much screen real estate as the 13” Retina MacBook Pro. Apple’s display settings let you go to 1400x900. Things work fine at that resolution, though switching desktops isn’t as smooth.</p> <p>Overall, performance is similar to my 2013 Air, which has a <a href="">Haswell</a>-based 1.3GHz Core i5. The MacBook is a little slower at sustained tasks, such as compiling large programs. It took 6:30 to compile <a href="">io.js</a> v1.8.1, compared to 5:30 on my Air. I also notice some slight stuttering if I switch desktops while building projects in IntelliJ. This is similar to my Air’s behavior when plugged into a cinema display.</p> <p>Battery life is a non-concern. Depending on what I’m doing, I can get anywhere from 5 to 12 hours unplugged. With my typical dev environment running, battery life is just above 8 hours. This is significantly better than my 2013 Air. Although both laptops have similar best-case endurance, I could exhaust the Air in 90 minutes with the right workload.</p> <p>Lastly, I really like the keyboard. It feels as if the keys are made of mouse buttons: short travel, but sharp. Fortunately, they aren’t as loud as mouse buttons. If anything, typing is quite quiet. After a week of typing on the MacBook, other laptop keyboards feel like I’m pressing my fingers into ground beef.</p> <p><a href="/photos/pics/IMG_1133.jpg"><img src="/photos/pics/thumbs/IMG_1133.jpg" /></a></p> <p>Bottom line: It’s beautiful, light, and more than fast enough for my needs. If you have the cash, I say go for it.</p> <p><br /></p> <p><strong>Addendum</strong>: I’ve read comments to the effect of, “I’ll wait for the next version with two USB ports.” I doubt the MacBook will ever have more than one USB-C port. Remember, the keyboard abuts the side. The only areas where ports wouldn’t intersect with the keyboard are already taken: USB-C on the left, headphone jack on the right. Adding another USB-C port would require sacrificing the headphone jack. I doubt that will happen any time soon.</p>