<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://blog.onderweg.eu/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.onderweg.eu/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2026-03-02T18:41:48+01:00</updated><id>https://blog.onderweg.eu/feed.xml</id><title type="html">Onderweg Blog</title><subtitle>Onderweg Blog</subtitle><author><name>Onderweg blog</name><email>dev@gerwert.io</email></author><entry><title type="html">Retro file formats: dBase files with custom indexes</title><link href="https://blog.onderweg.eu/dbase/" rel="alternate" type="text/html" title="Retro file formats: dBase files with custom indexes" /><published>2023-02-27T09:00:00+01:00</published><updated>2023-02-27T09:00:00+01:00</updated><id>https://blog.onderweg.eu/dbase</id><content type="html" xml:base="https://blog.onderweg.eu/dbase/"><![CDATA[<p>In my newly found <a href="/reviving-the-guide/">interest in retro file formats</a>, I turned my attention to the good old dBase format.</p>

<p>Quoting from Wikipedia:</p>

<blockquote>
  <p>dBase (also stylized dBASE) was one of the first database management systems for microcomputers and the most successful in its day.</p>
</blockquote>

<p>I’m old enough to remember working with dBase files on a MS-DOS computer. A typical use case for dBase at the time on a personal computer was to store a personal address book in a database. There were no iCloud backups or cheap reliable storage media back then. You’d put your database, worth hours of manual labour,  on a diskette and prayed you would not encounter disk read errors one day.</p>

<p>Anyway. What was nice about dBase is that it was relatively easy to create forms to enter data. Also, it included a programming language, ideal for quick data manipulation.</p>

<h2 id="the-dbf-format">The dbf format</h2>

<p>The actual data was stored in <code class="language-plaintext highlighter-rouge">.dbf</code> files. A <code class="language-plaintext highlighter-rouge">.dbf</code> file contains field definitions (names an types of fields in de database), followed by the actual data. The data is stored in fixed length <em>records</em>. Records are what we would refer to today as “rows” in SQL terminology, and <em>fields</em> are equivalent to columns.</p>

<p>While being being essentially an obsolete file format for most use-cases, dBase files are still widely used as part of the <a href="https://www.esri.com/content/dam/esrisites/sitecore-archive/Files/Pdfs/library/whitepapers/pdfs/shapefile.pdf">ESRI Shapefile</a> format. The Shapefile format is a geospatial data format. While called “shape<em>file</em>”, data is actually stored in a <em>collection of files</em>. One of those files is a dBase file, which is being used to store columnar attributes for each shape stored in <code class="language-plaintext highlighter-rouge">.shp</code> file.</p>

<h2 id="libraries-for-dbase-files-today">Libraries for dBase files today</h2>

<p>Although the dBase file structure is not very complicated, it is convenient that there are still libraries available today in many languages to read and write dBase files. For example, the code for reading/writing dBase files included in PHP 3.0 and in many other projects, originally created by Brad Eacker in 1993, is still a good starting point for handling dBase files in your own C code. Another example is the C implementation in <a href="https://github.com/OSGeo/shapelib">Shapelib</a>.</p>

<p>One of the main reasons that dBase libraries are not hard to find in these post-dBase times, is that dBase files are abundant in the world of GIS software, thanks to ESRI shapefiles. Many open source libraries that handle geospatial data contain code for handling dBase files, as part of their Shapefile implementation.</p>

<p>(By the way, the fact that dBase is still a widely used format, while not well suited for modern applications, is a situation that not everyone is <a href="http://switchfromshapefile.org/">happy with</a>.)</p>

<p>In general, dBase libraries can handle only a subset of <code class="language-plaintext highlighter-rouge">.dbf</code> files that you’ll encounter in the wild. That is because the most complicated part of the dBase format is undoubtedly the many versions and variants of the format. Other database software products like FoxPro and Clipper used to dBase file format as well, but with their own additions. For example by adding support for additional field types. Also, the dBase product evolved itself, ending with the dBASE 7 format. Currently though, III+–V is the most common dBASE file format <a href="https://en.wikipedia.org/wiki/.dbf#File_format_of_Level_5_DOS_dBASE">found in the wild</a>. So this is the the format most libraries will handle without issues.</p>

<p>The large amount of dBase variants becomes more of a problem when working with indexes.</p>

<h2 id="the-jungle-of-dbase-indexes">The jungle of dBase indexes</h2>

<p><code class="language-plaintext highlighter-rouge">.dbf</code> files are the backbone of a dBase database. But dBase actually defines many types of files. Just like Shapefiles, a dBase database can be a collection of files.</p>

<p>Widespread amongst these companion files are <code class="language-plaintext highlighter-rouge">.dbt</code> files, which are used for memo fields. These are fields that can contain more characters than the character max 254 characters of character fields.</p>

<p>Another important dBase file type are indexes. They are generally stored as a B+ tree in a separate file. Originally, dBase used <code class="language-plaintext highlighter-rouge">.ndx</code> for single indexes, and <code class="language-plaintext highlighter-rouge">.mdx</code> for multiple indexes. But of course, also for indexes, many new variants came, and stil exist in the wild.</p>

<p>For example, Visual FoxPro supports structural compound index (<code class="language-plaintext highlighter-rouge">.cdx</code>), nonstructural compound index (<code class="language-plaintext highlighter-rouge">.cdx</code>) files, and standalone index (<code class="language-plaintext highlighter-rouge">.idx</code>) files. Clipper uses <code class="language-plaintext highlighter-rouge">.ntx</code> files. It is similar to an <code class="language-plaintext highlighter-rouge">.ndx</code> file, but allows for longer search key expressions and stores data in ASCII format. In Shapfiles, <code class="language-plaintext highlighter-rouge">.ain</code> and <code class="language-plaintext highlighter-rouge">.aih</code> attribute indexes are used. In ArcGIS 8 and later however, <code class="language-plaintext highlighter-rouge">.atx</code> attribute indexes are used. GDAL/OGR 1.7 is using <code class="language-plaintext highlighter-rouge">.ind</code> and <code class="language-plaintext highlighter-rouge">.idm</code> attribute indexes for Shapefiles, which are not compatible with other GIS software.</p>

<p>As you can see, it’s a jungle out there. Figuring out which legacy index format to use when creating a new dBase file is daunting, just like finding libraries or format descriptions to read and write all these kinds of formats. And once you know what index format to use, and how it should be structured, it turns out that writing code for creating those indexes is really complicated (reading is a easier, but still not straightforward). For that reason probably, many software packages that can read <code class="language-plaintext highlighter-rouge">.dbf</code> files ignore indexes altogether.</p>

<p>While experimenting with handling dBase files in C, I was a bit stuck on how to handle indexing, for the above reasons. But then a pragmatic approach came to mind: forget legacy dBase index formats and just go for the most convenient, but still efficient, way of storing an index available today.</p>

<h2 id="using-file-based-keyvalue-stores-for-dbase-indexes">Using file-based key/value stores for dBase indexes</h2>

<p>An index is basically nothing more than a way to quickly map a key to a value. In this case, the key being the field to index in the database, and the value the record number. Because records in a <code class="language-plaintext highlighter-rouge">.dbf</code> file have a fixed length, once you now the record number, you can directly access it.</p>

<p>A while ago, I discoverd the existance of file-based key/value databases, like <a href="http://fallabs.com/tokyocabinet/">Tokyo cabinet</a>, its successor <a href="https://dbmx.net/tkrzw/">tkrzw</a> and constant databases. I’m a big fan of the concept, but finding every-day uses cases for personal projects is challenging. But I might have found at least one now: use it as an index for a dBase file. Because these key/value stores are implemented as a file hash or file B+ tree database, they can act perfectly as an index. The same goes for constant databaes.</p>

<p>All of the above key/value engines have <code class="language-plaintext highlighter-rouge">C</code> libraries available, often with bindings for other languages as well. So they can be used in any modern project relatively easy.</p>

<h3 id="constant-databases-for-indexes">Constant databases for indexes</h3>

<p><a href="http://cr.yp.to/cdb.html">CDBs</a>, short for “constant database”, are awesome. They are basically a very reliable and fast on-disk associative array, mapping keys to values. Why “constant”? Because you create them once, after that they are read only. To add/change entries, you must recreate the database. Not per se a problem when used for indexes: an index can be rebuild when needed. Rebuilding is not as fast as reading, but still fast enough for most use cases.</p>

<p>The use case for that would be what in the dBase world is called a “non-production” index. Production indexes in dBase are automatically opened and kept up to date whenever a table is opened. Non-production .mdx files and .ndx files must be explicitly opened to be updated.</p>

<p>Below an example in PHP, using PHPs build in <a href="https://www.php.net/manual/en/book.dba.php">DBA extension</a> and the <a href="https://pecl.php.net/package/dbase">PECL dBase extension</a>. It shows a simply way to build a custom index for a dBase file in a CDB database:</p>

<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Create `.dbf` file</span>
<span class="nv">$def</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
    <span class="k">array</span><span class="p">(</span><span class="s2">"name"</span><span class="p">,</span>     <span class="s2">"C"</span><span class="p">,</span>  <span class="mi">50</span><span class="p">),</span>
    <span class="k">array</span><span class="p">(</span><span class="s2">"age"</span><span class="p">,</span>      <span class="s2">"N"</span><span class="p">,</span>   <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
    <span class="k">array</span><span class="p">(</span><span class="s2">"email"</span><span class="p">,</span>    <span class="s2">"C"</span><span class="p">,</span> <span class="mi">128</span><span class="p">)</span>
<span class="p">);</span>
<span class="nv">$dbf</span> <span class="o">=</span> <span class="nb">dbase_create</span><span class="p">(</span><span class="s1">'/tmp/test.dbf'</span><span class="p">,</span> <span class="nv">$def</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$dbf</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">echo</span> <span class="s2">"Error, can't create the database</span><span class="se">\n</span><span class="s2">"</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// Add example records to .dbf</span>
<span class="nb">dbase_add_record</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Maxim Topolov'</span><span class="p">,</span> <span class="s1">'23'</span><span class="p">,</span> <span class="s1">'max@example.com'</span><span class="p">]);</span>
<span class="nb">dbase_add_record</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Leo Bakker'</span><span class="p">,</span> <span class="s1">'45'</span><span class="p">,</span> <span class="s1">'leo@example.com'</span><span class="p">]);</span>
<span class="nb">dbase_add_record</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Bear Voxny'</span><span class="p">,</span> <span class="s1">'23'</span><span class="p">,</span> <span class="s1">'bear@example.com'</span><span class="p">]);</span>
<span class="nb">dbase_add_record</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Qudo Malek'</span><span class="p">,</span> <span class="s1">'21'</span><span class="p">,</span> <span class="s1">'qudo@example.com'</span><span class="p">]);</span>
<span class="nb">dbase_add_record</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Pavlo Nyrola'</span><span class="p">,</span> <span class="s1">'34'</span><span class="p">,</span> <span class="s1">'pavlo@example.com'</span><span class="p">]);</span>

<span class="c1">// Create index for the 'email' field in a constant database</span>
<span class="nv">$cdb</span> <span class="o">=</span> <span class="nb">dba_open</span><span class="p">(</span><span class="s2">"/tmp/test.cdb"</span><span class="p">,</span> <span class="s2">"n"</span><span class="p">,</span> <span class="s2">"cdb_make"</span><span class="p">);</span>
<span class="nv">$num</span> <span class="o">=</span> <span class="nb">dbase_numrecords</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="nv">$i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="nv">$i</span> <span class="o">&lt;=</span> <span class="nv">$num</span><span class="p">;</span> <span class="nv">$i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="nv">$rec</span> <span class="o">=</span> <span class="nb">dbase_get_record</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">,</span> <span class="nv">$i</span><span class="p">);</span>
    <span class="nv">$key</span> <span class="o">=</span> <span class="nb">trim</span><span class="p">(</span><span class="nv">$rec</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
    <span class="nb">dba_insert</span><span class="p">(</span><span class="nv">$key</span><span class="p">,</span> <span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="nv">$i</span><span class="p">,</span> <span class="nv">$cdb</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Cleanup</span>
<span class="nb">dba_close</span><span class="p">(</span><span class="nv">$cdb</span><span class="p">);</span>
<span class="nb">dbase_close</span><span class="p">(</span><span class="nv">$dbf</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="dbms-or-berkeley-db-style-databases-for-indexes">DBMs (or “Berkeley DB style databases”) for indexes</h3>

<p>A constant database is by far the fastest solution for lookups, but updating the index requires a rebuild. So if you need to update the index often, you can also resort
to a DBM, or “Berkeley DB style databases” as they are called in PHP docs. Berkeley DB was one of the first in it’s genre of embedded databases for key/value data. Berkeley DB itself is not around anymore (not maintained). But its legacy is lasting. There are many modern variants, such as GNU dbm (C), TKRZW (C/C++), and for Go: BadgerDB and BoltDB amongst many others.</p>

<p>TKRZW is my favorite of these, one of the reasons is that it has a nice C interface. For example, creating a TKRZW database and adding records in C requires a minimum amount of code:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">TkrzwDBM</span> <span class="o">*</span><span class="n">dbm</span> <span class="o">=</span> <span class="n">tkrzw_dbm_open</span><span class="p">(</span><span class="s">"index.tkh"</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="s">"truncate=false,num_buckets=100"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dbm</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Failure while opening database</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Last status message: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">tkrzw_get_last_status_message</span><span class="p">());</span>
    <span class="n">exit</span><span class="p">(</span><span class="n">EXIT_FAILURE</span><span class="p">);</span>
<span class="p">}</span>

<span class="c1">// Add records.</span>
<span class="n">tkrzw_dbm_set</span><span class="p">(</span><span class="n">dbm</span><span class="p">,</span> <span class="s">"foo"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s">"hop"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
<span class="n">tkrzw_dbm_set</span><span class="p">(</span><span class="n">dbm</span><span class="p">,</span> <span class="s">"bar"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s">"step"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
<span class="n">tkrzw_dbm_set</span><span class="p">(</span><span class="n">dbm</span><span class="p">,</span> <span class="s">"baz"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="s">"jump"</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
</code></pre></div></div>

<p>By adding a loop through dBase records, like in the PHP example above, an index of one or more columns can be created.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Stepping away from index formats supported by other software that handles dBase files comes with the price of lack interoperability. Other software won’t be able to read those custom indexes. But for personal use, that’s not a problem at all, since <a href="https://blubsblog.bearblog.dev/i-am-the-only-user/">I’m only user</a>.</p>

<p>Altough you lose interoperability, it can easily beat struggling with old dBase index formats. Especially since there is no guarantee whatsover that even if you do pick an existing format, the index you create is interchangeable between different software products. There is no single standard with broad support for dBase indexes afterall.</p>

<p>A unanswered question you might have still about all this is: why would someone in 2023 work with the ancient dBase format in the first place? And the answer is the same as for many hobby projects: because why not! Why would you use something mundane as SQLite, when you can keep alive a 40 year old database format with no unicode support?</p>]]></content><author><name>Onderweg blog</name><email>dev@gerwert.io</email></author><category term="file-formats" /><category term="c" /><category term="dbase" /><summary type="html"><![CDATA[How to use file-based key/value stores, like CDB and TKRZW, to create custom indexes for dBase (dbf) files]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.onderweg.eu/dbase/dbase.png" /><media:content medium="image" url="https://blog.onderweg.eu/dbase/dbase.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Reviving The Guide: dusting off an old binary file format</title><link href="https://blog.onderweg.eu/reviving-the-guide/" rel="alternate" type="text/html" title="Reviving The Guide: dusting off an old binary file format" /><published>2022-10-28T10:00:00+02:00</published><updated>2022-10-28T10:00:00+02:00</updated><id>https://blog.onderweg.eu/reviving-the-guide</id><content type="html" xml:base="https://blog.onderweg.eu/reviving-the-guide/"><![CDATA[<p>Quite a while ago, in the 2010s while I was still a Windows user, <a href="https://theguide.sourceforge.net/">The Guide</a> was my absolute favorite note taking application.</p>

<p>The concept of The Guide is simple as it is brilliant. Notes are stored in a single file (<em>guide</em>). The notebook has a tree structure, every node can have (rich) text associated with it. Nodes can be assigned individual icons and colours. The tree can be of arbitrary depth. That’s it.</p>

<p>The official site describes the concept as follows:</p>

<blockquote>
  <p>the Guide is a two-pane extrinsic outliner. This concept is similar to mindmapping in some ways.</p>
</blockquote>

<p><img src="the-guide.png" width="750" alt="The Guide screenshot" /></p>

<p>When I moved from Windows to macOS however, my glorius time with The Guide was over. It is Windows only, so I was forced to switch to different note taking approaches.</p>

<p>I went for notes as separate Markdown files, ordered in a file system folders. Although different from my beloved Guide tree, this approach also works well for me. What I most like about it is that it’s portable, available everywhere when used in combination with for example Dropbox or Google drive, and completely without vendor-lock in. I can use any markdown editor to edit my notes, on desktop or on mobile.</p>

<p>But still. At times I miss the build-in tree based one-file approach from The Guide in all its simplicity. And I also sometimes would like to browse through my old notes. These are stored in <code class="language-plaintext highlighter-rouge">.gde</code> files, a binary format specific to The Guide. The only way to open them on Mac is by running The Guide in a virtual Windows machine, or in Wine. That’s not ideal. But also not very fun. Reading those files programmatically, so that I can do whatever I want with the data, is what’s I’m really after.</p>

<h2 id="using-libguide-in-c">Using <code class="language-plaintext highlighter-rouge">libguide</code> in C</h2>

<p>In order to programatically read my old <code class="language-plaintext highlighter-rouge">.gde</code>  files on Mac without the Windows application, I first checked whether The Guide source code was available (for some reason I never checked that before, or I forgot). To my great joy, the source is (still) available at sourceforge. But even better, the part that handles reading/writing <code class="language-plaintext highlighter-rouge">.gde</code>, is nicely packaged in a stand-alone C library: <code class="language-plaintext highlighter-rouge">libguide</code> (compiled as DLL). <code class="language-plaintext highlighter-rouge">libguide</code> can be used completely independent from the main GUI application (Guide) that was written in C++.</p>

<p>Next step would be to call <code class="language-plaintext highlighter-rouge">libguide</code> functions from my own code, and experiment with reading my old <code class="language-plaintext highlighter-rouge">.gde</code> files. It became apparent soon however, that <code class="language-plaintext highlighter-rouge">libguide</code> would not run <em>as-is</em> on my Mac, because of several Windows specific API calls in the code:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">CreateFileMapping</code>,<code class="language-plaintext highlighter-rouge">MapViewOfFileUse</code> are used to create memory mapped files when reading <code class="language-plaintext highlighter-rouge">gde</code> files.</li>
  <li><code class="language-plaintext highlighter-rouge">MultiByteToWideChar</code>, <code class="language-plaintext highlighter-rouge">WideCharToMultiByte</code> are used for unicode conversion.</li>
</ul>

<p>Luckily, these functions can be relatively easy replaced by Posix variants, like <code class="language-plaintext highlighter-rouge">mmap</code> and <code class="language-plaintext highlighter-rouge">mbsrtowcs</code>. So I did.</p>

<p>After replacing Windows specific functions with Posix ones and replacing the Visual Studio project by a Makefile, the library would compile. But, actually reading a guide file caused a segfault.</p>

<p>Back to the drawing board.</p>

<h3 id="pointer-size-problems">Pointer size problems</h3>

<p>It took me a while to figure out that the segfault cause was related to the difference in pointer size between 32-bit and 64-bit architectures.</p>

<p>See, on a 32-bit architecture (my old Windows machine) the pointer size is 4 bytes, on my current 64-bit machine (macOS) pointer size is 8 bytes. Not necessary an issue. But it becomes a problem when code explicitly relies on a specific pointer size, as turned out to be the case in <code class="language-plaintext highlighter-rouge">libguide</code>.</p>

<p>While reading a file fro memory, <code class="language-plaintext highlighter-rouge">libguide</code> uses “fake pointers”, to store unique IDs for nodes. A fake pointer here, is a pointer value read does not point to a real memory address. The value of the pointer interpreted as an <code class="language-plaintext highlighter-rouge">uint32</code> value.</p>

<p>Fake pointers are being used in <code class="language-plaintext highlighter-rouge">libguide</code> to store ID values of nodes. The small code fragment below, shows how IDs are being read from  a memory mapped region that contains a <code class="language-plaintext highlighter-rouge">gde</code> file:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// This fragment read the id and parent id of a node</span>
<span class="c1">// char *p points to memory mapped area with gde data</span>
<span class="o">*</span><span class="n">fake_node_ptr</span> <span class="o">=</span>  <span class="p">((</span><span class="k">struct</span> <span class="n">tree_node_t</span> <span class="o">**</span><span class="p">)</span><span class="n">p</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span> 
<span class="o">*</span><span class="n">fake_parent_ptr</span> <span class="o">=</span> <span class="p">((</span><span class="k">struct</span> <span class="n">tree_node_t</span> <span class="o">**</span><span class="p">)</span><span class="n">p</span><span class="p">)[</span><span class="mi">1</span><span class="p">];</span>
<span class="n">p</span> <span class="o">+=</span> <span class="mi">2</span> <span class="o">*</span> <span class="nf">sizeof</span><span class="p">(</span><span class="k">struct</span> <span class="n">tree_node_t</span> <span class="o">*</span><span class="p">);</span>
</code></pre></div></div>

<p>Because the number of bytes read from from the memory mapped area <code class="language-plaintext highlighter-rouge">p</code>depends on the pointer size of the reading machine, things go wrong when reading a file created on a 32-bit architecture by a 64-bit machine.</p>

<p>Writing has the same problem:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">int</span> <span class="nf">_guide_storer_fn</span><span class="p">(</span><span class="k">struct</span> <span class="n">tree_node_t</span> <span class="o">*</span><span class="n">node</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">memory_mapped_data</span><span class="p">)</span>
<span class="p">{</span>
	<span class="k">struct</span> <span class="n">tree_node_t</span> <span class="o">*</span><span class="n">parent</span> <span class="o">=</span> <span class="n">tree_get_parent</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
	<span class="k">struct</span> <span class="n">guide_nodedata_t</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> 
		<span class="p">(</span><span class="k">struct</span> <span class="n">guide_nodedata_t</span> <span class="o">*</span><span class="p">)</span><span class="n">tree_get_data</span><span class="p">(</span><span class="n">node</span><span class="p">);</span>
  <span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="p">(</span><span class="kt">FILE</span> <span class="o">*</span><span class="p">)</span><span class="n">memory_mapped_data</span><span class="p">;</span>
  
	<span class="c1">// write node_id</span>
	<span class="n">fwrite</span><span class="p">(</span><span class="o">&amp;</span><span class="n">node</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">node</span><span class="p">),</span> <span class="n">fp</span><span class="p">);</span> <span class="c1">// &lt;- sizeof(node) depends on architecture</span>
	<span class="c1">// parent_node_id</span>
	<span class="n">fwrite</span><span class="p">(</span><span class="o">&amp;</span><span class="n">parent</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">parent</span><span class="p">),</span> <span class="n">fp</span><span class="p">);</span>
  
  <span class="c1">// ... read rest of the data</span>
</code></pre></div></div>

<p>I fixed this by always reading and writing <code class="language-plaintext highlighter-rouge">uint32</code> values for node IDs, not relying on machine pointer sizes anymore:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">fwrite</span><span class="p">(</span><span class="o">&amp;</span><span class="n">node</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uint32</span><span class="p">),</span> <span class="n">fp</span><span class="p">);</span>	
<span class="n">fwrite</span><span class="p">(</span><span class="o">&amp;</span><span class="n">parent</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">uint32</span><span class="p">),</span> <span class="n">fp</span><span class="p">);</span>
</code></pre></div></div>

<p>This worked. Although for me it’s still an open question if there are situations where a node id won’t fit in a <code class="language-plaintext highlighter-rouge">uint32</code>.</p>

<p>Anyway, after these changes and some testing, I had a working cross-platform C library, that can read my old guide files on my Mac (no guarantees about yours, maybe it wil eat them). <a href="https://github.com/onderweg/the-guide-non-windows">Github repro</a>.</p>

<h2 id="multi-language-parsing-with-kaitai-struct">Multi-language parsing with Kaitai struct</h2>

<p>This was all really nice. But what if I would want to read/write <code class="language-plaintext highlighter-rouge">.gde</code> files in Go or Swift or whatever other language? Of course one can bridge the C code to other languages. Functions in a C library can be called by almost any language if there is some kind of “bridge” in-between. Often this is a foreign function interface (FFI) . For example, Go has the <code class="language-plaintext highlighter-rouge">"C"</code> package, for node there is <a href="https://github.com/node-ffi/node-ffi">node-ffi</a>, etc.</p>

<p>But developing bridge code for multiple languages is repetitive work. And also, <a href="https://dev.to/gerwert/calling-swift-from-go-5dbk">calling unmanaged C from managed languages</a> like Go and Swift, has all kind of drawbacks. Ideally, instead of bridging C, these languages would have a native <code class="language-plaintext highlighter-rouge">gde</code> parsers. Of course, creating those sounds like a lot of work. So, what if generating native parsers for multiple languages would be an automated process?</p>

<p>A while ago I discovered <a href="https://kaitai.io/">Kaitai struct</a>, which immediately appealed to me, but I could not think of a use case for me at the time.</p>

<p>Kaitai Struct is a declarative language (using YAML syntax), which can be used to describe binary formats. Once you have a description of a format, you can use Kaitai Struct to generate parsers for it in multiple languages.</p>

<blockquote>
  <p>The main idea is that a particular format is described in Kaitai Struct language (.ksy file) and then can be compiled with ksc into source files in one of the supported programming languages. These modules will include a generated code for a parser that can read the described data structure from a file or stream and give access to it in a nice, easy-to-comprehend API.</p>
</blockquote>

<p>Example of what a Kaitai Struct format description looks like:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">meta</span><span class="pi">:</span>
  <span class="na">id</span><span class="pi">:</span> <span class="s">tcp_segment</span>
  <span class="na">endian</span><span class="pi">:</span> <span class="s">be</span>
<span class="na">seq</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">src_port</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">u2</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">dst_port</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">u2</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">seq_num</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">u4</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">ack_num</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">u4</span>
</code></pre></div></div>

<p>The Guide uses a binary format which has a relatively straightforward structure. Seems like an ideal use case to experiment with Kaitai Struct!</p>

<p>Creating a Kaitai language file for The Guide was indeed not hard. After a few iterations, I was able to parse all my old Guide files without any issues in kaitai’s web IDE. After that, it was trivial to generate parsers for major programming languages via kaitai’s command line tool.</p>

<p>Besides generating parsers, Kaitai can also export parsed data as JSON or XML:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gem <span class="nb">install </span>kaitai-struct-visualizer
<span class="c"># JSON output</span>
<span class="nv">$ </span>ksdump <span class="nt">-f</span> json guide.gde gde32.kty
<span class="c"># XML output</span>
<span class="nv">$ </span>ksdump <span class="nt">-f</span> xml guide.gde gde32.kty
</code></pre></div></div>

<p>The Kaitai files I created for parsing <code class="language-plaintext highlighter-rouge">.gde</code> files are available in the <a href="https://github.com/onderweg/the-guide-non-windows">Github repro</a>.</p>

<h3 id="conclusion">Conclusion</h3>

<p>Dusting off old binary files can be fun. If you know how the format is structured, modern tools can relieve some of the effort it takes to parse them with your favourite programming language.</p>

<p>Although mainly an experiment, my first experience with kaitai was quite positive. Some nice to haves that will hopefully arrive in Kaitai in the future:</p>

<ul>
  <li>Ability to write (generate) files with</li>
  <li>The Kaitai project is still very much in development. Go support for example not finished and well documented yet (but works)</li>
</ul>

<p>You don’t have to always write your own Kaitai format descriptions by the way. For a lot of binary format there is already one <a href="https://formats.kaitai.io/">available</a>.</p>]]></content><author><name>Onderweg blog</name><email>dev@gerwert.io</email></author><category term="file-formats" /><category term="c" /><category term="markdown" /><category term="kaitai" /><summary type="html"><![CDATA[Making libguide cross platform, and experimenting with Kaitai Struct]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.onderweg.eu/reviving-the-guide/the-guide.png" /><media:content medium="image" url="https://blog.onderweg.eu/reviving-the-guide/the-guide.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Using ubisys Zigbee modules with Philips Hue</title><link href="https://blog.onderweg.eu/ubisys-switches-with-philips-hue/" rel="alternate" type="text/html" title="Using ubisys Zigbee modules with Philips Hue" /><published>2021-05-22T10:00:00+02:00</published><updated>2021-05-22T10:00:00+02:00</updated><id>https://blog.onderweg.eu/ubisys-switches-with-philips-hue</id><content type="html" xml:base="https://blog.onderweg.eu/ubisys-switches-with-philips-hue/"><![CDATA[<p>What if you want to be able to switch a light via both the existing wall switch, and the Philips Hue app and/or Homekit? You can install an in-wall home automation module behind the existing wall switch, and voila!</p>

<p>What is meant here with “in-wall switch”, is a module that can be installed behind an existing light switch, and makes “dumb” light smart. Connected lights can then be switched via an app, Philips Hue in my setup, or via the existing wall switch.</p>

<p>Zigbee is commonly used as a wireless protocol for these kind of devices. But of course such a module does not have to work with Zigbee. Besides Zigbee, wifi and z-wave are commonly used in smart lighting. And in the US, Lutron provides a very popular solution of switching dumb lights with their Caseta smart switches via their proprietary RF protocol. But these are not for the European market.</p>

<p>So there is a wide range of technologies available for smart switches.
But I’ll focus on Zigbee and Hue compatibility here.</p>

<h2 id="hue-compatible-zigbee-switches">Hue compatible Zigbee switches</h2>

<p>There are multiple Zigbee in-wall switches on the market that are compatible with Philips Hue.</p>

<p>Well known among people who are familiar with Zigbee modules, are the modules by Sunricher. Amongst others, these are sold under the <a href="https://www.icasa.io/dimmersswitches">iCasa</a> and <a href="https://www.robbshop.nl/inbouwschakelaar-zigbee-robb-smarrt">Robb</a> brands. Because both the Philips Hue bridge and these modules use the Zigbee Light Link standard, they can be added as third party devices to the Hue bridge.</p>

<p><img src="icasa.png" alt="iCasa Zigbee switch" /></p>

<p>Less known are the modules from the German manufacturer <a href="http://www.ubisys.de/">ubisys</a>. Ubisys produces really solid Zigbee modules, like dimmers and switches, that can be flush mounted.</p>

<p>The ubisys S1 is the module that has been installed at several places at my home.</p>

<p><img src="s1.png" alt="ubisys S1" /></p>

<p>The really nice thing about the S1, is the support for “normal” (rocker) light switches. While the Sunricher modules only work push buttons, the ubisys s1 supports standard European style toggle switches as well.</p>

<p><img src="schakelaar.jpg" width="300" alt="Light switch" /></p>

<p>While this is common for Wifi modules, like Shelly’s, and z-wave modules, there are still not a lot of Zigbee modules that work in a non-push button configuration.</p>

<h2 id="what-about-the-philips-hue-wall-switch-module">What about the Philips Hue wall switch module?</h2>

<p>Note that the solution with Zigbee switch modules discussed here, is different than using the <a href="https://www.philips-hue.com/en-gb/p/hue-philips-hue-wall-switch-module/8719514318045">Philips Hue wall switch module</a>.
With the Hue wall switch modules you can switch a Hue smart bulb via your existing switch. You can’t however switch dumb lights with it.</p>

<p>Also it has a battery, which you need to replace… eventually. Although it is expected to last at least 5 years, replacement is a hassle given its location behind a switch. The good thing about a battery powered device is that you don’t need a neutral wire in your wall box.</p>

<p>Because the Hue switch is battery powered, it is a <a href="https://www.zigbee2mqtt.io/information/zigbee_network.html#end-device">Zigbee end device</a>, so it does not act as a Zigbee router, and won’t help with improving your Zigbee mesh network. In contrary to hard wired modules and light bulbs.</p>

<p>So all in all, the concept of Philips’ module is quite different from other Zigbee wall switch modules.</p>

<p>Personally, it feels less solid, because there is no physical connection anymore between your light and the switch. If for whatever reason the Hue bridge is not available, there is no way to switch your lights.</p>

<p><img src="hue-wall-switch.jpeg" width="300" alt="Philips Hue wall switch module" /></p>

<p>I can imagine though, that if you already use Hue light bulbs, especially I you use color-changing lighting, the Hue wall switch is a more appropriate solution.</p>

<h2 id="about-the-ubisys-s1">About the ubisys S1</h2>

<p>A few other things that are noticeable about the ubisys S1:</p>

<ul>
  <li>The <strong>“click” sound</strong>, audible when the module switches, is relatively loud. Louder than for example in modules from Shelly and iCasa. In my experience it’s not annoying though.</li>
  <li>De S1 <strong>does not have screw terminals</strong> for the wires. Wires (neutral, live, switch) are attached permanently to the module. Connections are made via welding clamps.</li>
  <li>The module is <strong>relatively small compared</strong> to other modules like iCasa, but it still can be a real challenge to fit it in a standard wall box.</li>
  <li><strong>Ubisys also has a dimmer variant, the D1</strong>. That one does not work with a toggle switch by default.</li>
</ul>

<h2 id="ubisys-s1-in-philips-hue">Ubisys S1 in Philips Hue</h2>

<p>A really nice thing about the ubisys, is that the S1 works flawlessly with a Philips Hue bridge; and indirectly with Homekit via Homebridge.</p>

<p>Adding the S1 in the Hue app works the same as with Philips Hue accessories. After a search for new lights via the Hue app, the S1 appears as a new on/off switch.</p>

<p><img src="hue-app.png" width="300" alt="Ubisys switch the Philips Hue app" /></p>

<p>A disadvantage of using third party modules with Philips Hue, is that you can’t update the firmware of non-Philips devices. This is not a technical limitation. How over-the-air (OTA) updates work in Zigbee, is standarised. So, theoretically, Philips Hue can add functionality to support upgrading non-philips devices in the future (that’s a feature request for you, Philips!).</p>

<p>In the mean time, you can only update via the bridge of the original manufacturer. In the case of ubisys devices, that would be the Ubisys gateway, which is a pretty pricy device. As an alternative, you can also use open-source software like <a href="https://www.zigbee2mqtt.io/">zigbee2mqtt</a> in combination with a Zigbee USB stick. But that is not an easy solution for the avarage user.</p>

<p>A second disadvantage is that you won’t be able change the default configuration of ubisys modules via the Hue Bridge. Ubisys models have quite a few configuration options, but these are only accessible via the ubisys gateway (or, again, via zigbee2mqtt). It’s definitely a disadvantage, but in my experience not a problem though. For many use-case the default configuration suffices.</p>

<p>An example where it might be a problem is when you want to use a non default switch. By default, the S1 is setup for a toggle (two stable states) switch. If you want to use a push switch for example, you’re out of luck, you will need additional configuration.</p>

<p>Finally, functionality supported by the S1 but not in Hue, like measuring power consumption, is only exposed through the ubisys gateway and app. Furthermore, Philips Hue does not expose non-philips devices to Homekit. So for Homekit support you’ll need something like <a href="https://homebridge.io/">Homebridge</a> with Hue plugin.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Despite all this, if you have a fairly standard configuration, ubisys switches are an excellent choice if you want to automate your existing dumb lights, and already have a working Philips Hue setup. The modules are very solid, easy to integrate with Hue and work with existing switches (as long a there is a neutral line available).</p>]]></content><author><name>Onderweg blog</name><email>dev@gerwert.io</email></author><category term="home-automation" /><category term="hue" /><category term="ubisys" /><category term="zigbee" /><category term="home-automation" /><summary type="html"><![CDATA[My experience with using the ubisys S1 Zigbee modules with Philips Hue.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.onderweg.eu/assets/img/posts/s1.png" /><media:content medium="image" url="https://blog.onderweg.eu/assets/img/posts/s1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Developing HomeKit accessories without Homebridge</title><link href="https://blog.onderweg.eu/homekit-accessories-without-homebridge/" rel="alternate" type="text/html" title="Developing HomeKit accessories without Homebridge" /><published>2020-08-04T22:00:00+02:00</published><updated>2020-08-04T22:00:00+02:00</updated><id>https://blog.onderweg.eu/homekit-accessories-without-homebridge</id><content type="html" xml:base="https://blog.onderweg.eu/homekit-accessories-without-homebridge/"><![CDATA[<p><a href="https://homebridge.io/">Homebridge</a> is a really nice piece of software, written in Node.js, with which you can emulate HomeKit accessories.</p>

<p>In other words: with Homebridge you can add devices without native Homekit support to Homekit. All you need is a plugin for the device. From the Homebridge website:</p>

<blockquote>
  <p>Homebridge allows you to integrate with smart home devices that do not natively support HomeKit. There are over 2,000 Homebridge plugins supporting thousands of different smart accessories.</p>
</blockquote>

<p>Anyeone with Node.js knowledge, can create <a href="https://www.npmjs.com/search?q=keywords:homebridge-plugin">plugins</a> for Homebridge. Either for personal use, or to share with other Homebridge enthusiasts via NPM.</p>

<h2 id="homekit-accessory-protocol-specification">HomeKit Accessory Protocol Specification</h2>

<p>Homebridge implements the HomeKit Accessory Protocol Specification (<a href="https://developer.apple.com/support/homekit-accessory-protocol/">HAP</a>). Thanks to Apple releasing the HAP specification, developers can create their own non-commercial HomeKit accessories.</p>

<p>A difference with commercial HomeKit accessories is that the latter need certification from Apple under the MFI Program. If you add an uncertified accessory (for example via a Homebridge plugin), you’ll see a message in Homekit that te device is not certified. But beyond that, uncertified devices work perfectly fine in Homekit.</p>

<p>Working with HAP directly is not a trivial task. So the usual approach is to use a framework for all the HAP specific stuff. For Node.js for example you can use <a href="https://github.com/homebridge/HAP-NodeJS">HAP-NodeJS</a>, on which Homebridge is build.</p>

<h2 id="developing-homebridge-plugins">Developing Homebridge plugins</h2>

<p>So yes, Homebridge is a great way to run uncertified Homekit accessories “out-of-the-box”, thanks to the large number of available plugins, and a big community of Homebridge enthusiasts.</p>

<p>But there are also definitely some downsides, especially when <strong>developing</strong> you’re own plugins:</p>

<ul>
  <li>Homebridge plugins need to be installed globally, e.g. with <code class="language-plaintext highlighter-rouge">npm install -g</code>. If you want to migrate your Homebridge instance to another system, you have to manually go through your Homebridge config, and make a list of plugins you need to install on the new system.</li>
  <li>Testing plugins while developing is relatively cumbersome. You need a local Homebridge (development) instance  that runs your plugin. Every time you want to check a change, you need to restart the Homebridge instance, and check the Homebridge logs. There is no easy way of directly executing parts of a plugin for testing, without extensive mocking.</li>
</ul>

<p>Also there are some considerations when <strong>running</strong> Homebridge:</p>

<ul>
  <li>Resources: Since Homebridge runs on Node.js, naturally you need to have working Node.js environment. This is fine for, for example, a home server or a Raspberry Pi. But can be problematic for embedded systems or other devices with limited resources, such as OpenWRT routers. Also in terms of memory, Homebridge is relatively resource intensive compared to compiled binaries.</li>
  <li>Complex configuration: Homebridge act as a “bridge” in Homekit, which means it can host many child accessories/plugins. If you have many, the Homebridge config.json can get rather complex quickly.</li>
</ul>

<p>Although, it’s fair to say that some of these issues are less of a problem if you use <a href="https://github.com/oznu/homebridge-config-ui-x">Homebridge Config UI X</a></p>

<h2 id="homekit-development-in-go">Homekit development in Go</h2>

<p>Because of these downsides to Homebridge (for me at least), for my own custom developed Homekit accessories, I stepped away from using Homebridge (though I still use Homebridge with a limited number of public plugins, like the excellent <a href="https://github.com/ebaauw/homebridge-hue">Homebridge Hue</a> plugin).</p>

<p>As an alternative to Homebridge, I now mostly use the excellent Go framework <a href="https://github.com/brutella/hc">Hc</a>. HC is not a stand-alone server with plugins like Homebridge, but it’s a lightweight HAP framework, comparable to HAP-NodeJS. It abstracts the HomeKit Accessory Protocol and supports all HomeKit services and characteristics.</p>

<p>Creating a basic Homekit accessory with HC is as simple as:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
    <span class="s">"log"</span>
    <span class="s">"github.com/brutella/hc"</span>
    <span class="s">"github.com/brutella/hc/accessory"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="c">// create an accessory</span>
    <span class="n">info</span> <span class="o">:=</span> <span class="n">accessory</span><span class="o">.</span><span class="n">Info</span><span class="p">{</span><span class="n">Name</span><span class="o">:</span> <span class="s">"My Lamp"</span><span class="p">}</span>
    <span class="n">ac</span> <span class="o">:=</span> <span class="n">accessory</span><span class="o">.</span><span class="n">NewSwitch</span><span class="p">(</span><span class="n">info</span><span class="p">)</span>
    
    <span class="c">// configure the ip transport</span>
    <span class="n">config</span> <span class="o">:=</span> <span class="n">hc</span><span class="o">.</span><span class="n">Config</span><span class="p">{</span><span class="n">Pin</span><span class="o">:</span> <span class="s">"00102003"</span><span class="p">}</span>
    <span class="n">t</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">hc</span><span class="o">.</span><span class="n">NewIPTransport</span><span class="p">(</span><span class="n">config</span><span class="p">,</span> <span class="n">ac</span><span class="o">.</span><span class="n">Accessory</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
        <span class="n">log</span><span class="o">.</span><span class="n">Panic</span><span class="p">(</span><span class="n">err</span><span class="p">)</span>
    <span class="p">}</span>
    
    <span class="n">hc</span><span class="o">.</span><span class="n">OnTermination</span><span class="p">(</span><span class="k">func</span><span class="p">(){</span>
        <span class="o">&lt;-</span><span class="n">t</span><span class="o">.</span><span class="n">Stop</span><span class="p">()</span>
    <span class="p">})</span>
    
    <span class="n">t</span><span class="o">.</span><span class="n">Start</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The result of building an accessory with HC is a single binary without any dependencies. This is great for systems with limited resources (although Go binaries are relatively large, they are still a lot smaller than a complete Node.js environment).</p>

<p>Also in terms of memory, HC accessories are resource friendly. Memory foodprint is usually around 12 MB physical memory, versus 100 MB used by Homebridge in my configuration with just a few plugins.</p>

<p>But also if resources are not a problem, it’s really nice to be able to work with a single binary that’s easily portable between systems, without any additional installation steps on different machines (like setting up Homebridge).</p>]]></content><author><name>Onderweg blog</name><email>dev@gerwert.io</email></author><category term="Home automation" /><category term="tech" /><category term="homekit" /><category term="home-automation" /><category term="apple" /><category term="golang" /><summary type="html"><![CDATA[Homebridge is a really nice piece of software, written in Node.js, with which you can emulate HomeKit accessories.]]></summary></entry><entry><title type="html">Follow the sun</title><link href="https://blog.onderweg.eu/follow-the-sun/" rel="alternate" type="text/html" title="Follow the sun" /><published>2019-09-01T10:00:00+02:00</published><updated>2019-09-01T10:00:00+02:00</updated><id>https://blog.onderweg.eu/follow-the-sun</id><content type="html" xml:base="https://blog.onderweg.eu/follow-the-sun/"><![CDATA[<p>I created a small cli tool called <a href="https://github.com/onderweg/follow-the-sun"><em>Follow the sun</em></a>. What it does is switch display settings to Dark mode automatically after sunset, and back to Light mode after sunrise.
The idea is that Follow the sun runs as a daemon via <code class="language-plaintext highlighter-rouge">launchctl</code>.</p>

<p>For me this was mainly an experiement, it helped me to learn more about:</p>

<ul>
  <li>Accessing private frameworks in macOS</li>
  <li>Using CoreFoundation in C</li>
  <li>Using macOS Objective-C runtime library (<code class="language-plaintext highlighter-rouge">objc_msgSend</code>, <code class="language-plaintext highlighter-rouge">sel_registerName</code>, etc) in C.</li>
</ul>

<h2 id="how-to-retrieve-sunrisesunset-info-in-macos">How to retrieve sunrise/sunset info in macOS</h2>

<p>Sunrise/Sunset info is provided by a private macOS system framework named CoreBrightness. A bit more info on this undocumented API (Objective-C) can be found here: <a href="https://github.com/thompsonate/Shifty/issues/20">https://github.com/thompsonate/Shifty/issues/20</a>.</p>

<p>Example output from this API:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
    isDaylight = 0;
    nextSunrise = "2018-10-07 05:55:03 +0000";
    nextSunset = "2018-10-06 16:59:34 +0000";
    previousSunrise = "2018-10-05 05:51:38 +0000";
    previousSunset = "2018-10-04 17:04:09 +0000";
    sunrise = "2018-10-06 05:53:21 +0000";
    sunset = "2018-10-05 17:01:52 +0000";
}
</code></pre></div></div>

<p>You can retrieve the above schedule information from the command line in macOS Mojave:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ /usr/bin/corebrightnessdiag sunschedule 
</code></pre></div></div>

<p><em>added in 2020: macOS Big Sur:</em></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ /usr/libexec/corebrightnessdiag sunschedule
</code></pre></div></div>

<p>Caveat is that CoreBrightness needs to know your location in order to calculate the correct sunset and sunrise times. If Wifi is disabled, CoreBrightness (via <code class="language-plaintext highlighter-rouge">CLLocationManager</code>) might not be able to determine your location. In that case, the calculated sun schedule is not correct. So make sure WiFi is turned on.</p>

<p>The private API to get this info with, is provided via an objective-c class called <code class="language-plaintext highlighter-rouge">BrightnessSystemClient</code>.
To call this API with plain C, a bit of Objective-C runtime library magic is needed.</p>

<h2 id="how-to-toggle-darklight-mode">How to toggle dark/light mode</h2>

<p>For the dark/light mode toggle, there does not seem to be any objective-c or plain C API available in macOS.
Toggling is however possible programmatically, via AppleScript:</p>

<figure class="highlight"><pre><code class="language-applescript" data-lang="applescript"><span class="k">tell</span><span class="w"> </span><span class="nb">application</span><span class="w"> </span><span class="s2">"System Events"</span><span class="w">
    </span><span class="k">tell</span><span class="w"> </span><span class="nv">appearance</span><span class="w"> </span><span class="nv">preferences</span><span class="w">
      </span><span class="k">set</span><span class="w"> </span><span class="nv">dark</span><span class="w"> </span><span class="nv">mode</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="mi">1</span><span class="w">
    </span><span class="k">end</span><span class="w"> </span><span class="k">tell</span><span class="w">
</span><span class="k">end</span><span class="w"> </span><span class="k">tell</span></code></pre></figure>

<p>Since there is a way to directly execute AppleScript from objective-c (and hence from C via the Objective-C runtime), I decided to go for that route:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CFStringRef</span> <span class="n">script</span> <span class="o">=</span> <span class="n">CFSTR</span><span class="p">(</span><span class="s">"PUT APPLE SCRIPT HERE"</span><span class="p">);</span>
<span class="n">id</span> <span class="n">scriptString</span> <span class="o">=</span> <span class="n">objc_msgSend</span><span class="p">((</span><span class="n">id</span><span class="p">)</span><span class="n">objc_getClass</span><span class="p">(</span><span class="s">"NSString"</span><span class="p">),</span>
                                <span class="n">sel_registerName</span><span class="p">(</span><span class="s">"stringWithFormat:"</span><span class="p">),</span> <span class="n">scriptString</span><span class="p">,</span> <span class="n">darkMode</span><span class="p">);</span>
<span class="n">id</span> <span class="n">NSAppleScript</span> <span class="o">=</span> <span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="n">objc_getClass</span><span class="p">(</span><span class="s">"NSAppleScript"</span><span class="p">);</span>
<span class="n">SEL</span> <span class="n">alloc</span> <span class="o">=</span> <span class="n">sel_registerName</span><span class="p">(</span><span class="s">"alloc"</span><span class="p">);</span>
<span class="n">SEL</span> <span class="n">init</span> <span class="o">=</span> <span class="n">sel_registerName</span><span class="p">(</span><span class="s">"initWithSource:"</span><span class="p">);</span>
<span class="n">SEL</span> <span class="n">release</span> <span class="o">=</span> <span class="n">sel_registerName</span><span class="p">(</span><span class="s">"release"</span><span class="p">);</span>
<span class="n">id</span> <span class="n">allocScript</span> <span class="o">=</span> <span class="n">objc_msgSend</span><span class="p">(</span><span class="n">NSAppleScript</span><span class="p">,</span> <span class="n">alloc</span><span class="p">);</span>
<span class="n">id</span> <span class="n">scriptRef</span> <span class="o">=</span> <span class="n">objc_msgSend</span><span class="p">(</span><span class="n">allocScript</span><span class="p">,</span> <span class="n">init</span><span class="p">,</span> <span class="n">scriptString</span><span class="p">);</span>
<span class="c1">// Execute script    </span>
<span class="n">id</span> <span class="n">res</span> <span class="o">=</span> <span class="n">objc_msgSend</span><span class="p">(</span><span class="n">scriptRef</span><span class="p">,</span> <span class="n">sel_registerName</span><span class="p">(</span><span class="s">"executeAndReturnError:"</span><span class="p">),</span> <span class="o">&amp;</span><span class="n">err</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="running-via-launchctl">Running via launchctl</h2>

<p>Last part is to run the application via launchctl. The is reasonable straightforward. Besides the mandatory <code class="language-plaintext highlighter-rouge">.plist</code> configuration file, I created a helper script to easly start, stop and restart the service.</p>]]></content><author><name>Onderweg blog</name><email>dev@gerwert.io</email></author><category term="programming" /><category term="macos" /><category term="c" /><summary type="html"><![CDATA[How to toggle between light and dark mode programtically on macOS with C]]></summary></entry><entry><title type="html">On building a small cross-platform CLI tool in C, Go &amp;amp; Swift</title><link href="https://blog.onderweg.eu/cli-tool-c-go-swift/" rel="alternate" type="text/html" title="On building a small cross-platform CLI tool in C, Go &amp;amp; Swift" /><published>2019-02-25T20:00:00+01:00</published><updated>2019-02-25T20:00:00+01:00</updated><id>https://blog.onderweg.eu/cli-tool-c-go-swift</id><content type="html" xml:base="https://blog.onderweg.eu/cli-tool-c-go-swift/"><![CDATA[<p>A great thing about being a programmer is that if you need specific, customised, tooling, you can just write it yourself. Often there are existing options, but of course it’s a lot more fun to write your own tools, especially when you have room for some experimentation.</p>

<p>This is how I came to write a simple tool.. 3 times, in different languages.</p>

<h2 id="the-experiment">The experiment</h2>

<p>My goal was to write a very simple command line tool that can generate one-time passwords compatible with Google Authenticator. Google authenticator uses the Time-based One-Time Password algorithm (TOTP) to generate codes. Instead of writing my own implementation, I wanted to use an existing TOTP library, since there already are many good ones.</p>

<p>Essentially, all I want my tool to do, is accept a secret as single input, then call an exiting TOTP library to generate a code, and print the generated access code to the standard output.</p>

<p>The question I was asking myself was: suppose I would like to use the tool on several platforms (Mac, Windows, Ubuntu), and would like to
distribute the tool amongst a small group of - not necessarily technical - people (e.g. colleagues), what programming language would be the most pragmatic/viable/fun option?</p>

<p>Of course you can look at this question from many angles. Let’s focus on building and distributing the tool. Then, these were my “should have” requirements:</p>

<ul>
  <li>It should be possible to distribute the tool as single executable that works “out of the box”, meaning the user does not have to install dependencies like runtimes, frameworks, libraries, etc.</li>
  <li>With the same code base (but possibly different toolchains) it should be possible to produce builds for multiple platforms.</li>
</ul>

<h2 id="language-choice">Language choice</h2>

<p>I wanted to create a binary for this specific experiment, that’s why I did not consider interpreted languages like Node.js, Ruby, and Python for this specific tool. Although, of course in general these languages would all make perfectly viable options to use for writing a cross-platform command line tool.</p>

<p>There is also a disadvantage to those languages, being that the end user needs to have a runtime (e.g. Node.js) installed. Although many platforms come with common runtimes pre-installed, the user might need to install a different version. That is not always a trivial task for non-technical users.</p>

<p><em>(I’m aware that there are tools to compile interpreted languages to stand-alone executables, but that feels a bit like cheating here).</em></p>

<p>In the end, my choice was to experiment with C, Go and Swift.</p>

<p>I decided to stay in my “programming language comfort zone”, because learning a new language was not part of my experiment. Therefore I did not experiment with (in opinion) very interesting other languages, such as Rust, which I will try out in the future (feel free to leave a comment with your Rust experiences). Also good to note maybe: for this experiment I considered C++ overkill (or actually maybe, my C++ knowledge is just lacking).</p>

<h2 id="what-ilearned">What I learned</h2>

<p><strong>C</strong></p>

<ul>
  <li>Typically, executables build with C are linked dynamically. That means that end users need to install dependencies (linked libraries) in order to run the tool. That’s definitely not ideal.
There are ways around this, but these all come with some disadvantages:</li>
  <li>Static linking: create single binary that will hold all required binary code. But, that requires that all libraries that you use (for example a TOTP library) support static linking. This is definitely not always the case. Furthermore, Apple does not support statically linked binaries on Mac OS X.</li>
  <li>Distribute linked dynamic libraries with your application. This means that for every target OS you’ll have to pre-build all linked libraries, make sure these libraries can be found by the executable (e.g. changing rpath on macOS), and bundle them with the app. In other words you need compile and bundle <code class="language-plaintext highlighter-rouge">.dll</code> (Windows), <code class="language-plaintext highlighter-rouge">.dylib</code> (macOS) or <code class="language-plaintext highlighter-rouge">.so</code> (Linux) files with your app.
C does not have a runtime that needs to be bundled with the application. Therefore the resulting executable is quite small. The only dependency (dynamic library) is the C standard library libc, which is by default available on the OS’es I would like to target.</li>
  <li>Building a single C code base on different platforms can be a pain. I generally prefer to use the “default”, or most widely supported, build chain for a platform. In my opinion that is Visual Studio on Windows, Xcode on Mac (or GCC on Mac command line) and GCC on Linux. But that means that for every platform you need to install and setup a completely different build environment (project file, build scripts, etc).</li>
  <li>Compiling dependencies from source for multiple platforms is hard. Like I mentioned above, setting up build chain for your own code on different platforms can be difficult already. It is even more difficult to compile third party libraries from source for multiple platforms. Some are relatively easy to work with cross-platform, but others are a real pain because they lack support for, or documentation on, cross-platform building.</li>
</ul>

<p><strong>Go</strong></p>

<ul>
  <li>Executables build by Golang are statically linked by default. This means users don’t have to install any dependencies, and you don’t need to distribute dynamic libraries with your application. For a small command line application, the only thing you need to distribute is the executable.</li>
  <li>Unfortunately, because of the static linking, the resulting executable is relatively huge. That is because a Go binary includes the Go runtime, so that the end user does not need to have Go installed. (but, as Dotan Nahum, points out, there are ways to trim down some fat)</li>
  <li>Go is available as a binary distribution on all target platforms I was interested in. That makes setting up a build environment and building on these platforms painless.</li>
  <li>A great thing about Go, is that you can easily compile for multiple platforms on one machine. Without the hassle of setting different toolchains for differnt platform that is common when dealing with C.</li>
</ul>

<p><strong>Swift</strong></p>

<ul>
  <li>It is recommended to statically link to the Swift standard library, so that the resulting executable is not bound to the specific version of Swift that it was built with. This results in a large binary (more than 10 MB for a simple tool). The need for static linking is due to Swift lacking ABI stability. That is on the roadmap to be solved in a future Swift version though. (In comparison, Objective-C does have ABI stability by the way).</li>
  <li>Cross-platform support has not yet matured. You can compile a Swift program on both on Mac and Linux (there is no official Windows release yet), but the cross-platform build system - Swift Package Manager (SPM) - is not nearly as mature as Xcode on MacOS. Furthermore, many libraries that are available on CocoaPods or Carthage (MacOS only) don’t support SPM (cross-platform) yet.</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>When it comes to building cross-platform and distributing the tool, Go gave me the best developer experience.
Thanks to the default static linking, it is easy to create a single executable for distribution.
Building a Go program on different platforms is also really easy. There is no need for writing platform specific build scripts, or using platform dependent tool chains.
Downside is that the resulting executable is relatively large (several megabytes), but in my situation that was not a real issue.</p>

<p>Next up is C. Writing in C always gives me a pleasant sense of control and a feeling of freedom due to the lack 
of constraints from a runtime. Of course the downside to this is that you can easily shoot yourself in the foot. But the biggest problem here was that there is no single toolchain for building that works just as flawlessly cross-platform as Go.</p>

<p>And finally, Swift. While I really like Swift as a language, and I would only consider Swift as obvious choice when writing command line tools specifically for macOS. Swift is too much of a “moving target” for me. That has several implications, an important one is that it is not straightforward to use Swift on other platforms. Another issue for me is that Windows is not yet officially supported.</p>

<p>As a final note: I wanted to share my experiences, but in the end, what language suits you best comes down to personal preference and current state of languages. Next year might be different (not that it’s 2019 when I wrote this post).</p>]]></content><author><name>Onderweg blog</name><email>dev@gerwert.io</email></author><category term="programming" /><category term="cli" /><category term="c" /><category term="golang" /><category term="swift" /><category term="c" /><summary type="html"><![CDATA[A great thing about being a programmer is that if you need specific, customised, tooling, you can just write it yourself. Often there are existing options, but of course it’s a lot more fun to write your own tools, especially when you have room for some experimentation.]]></summary></entry></feed>